Rich text форматирование в экосистеме FormatJS опирается на расширение ICU MessageFormat, позволяющее встраивать структурированные элементы разметки прямо в строки локализации. В отличие от классической интерполяции значений, здесь допускается передача не только примитивов, но и компонентов или функций, которые интерпретируются как семантические элементы текста.
Ключевая идея заключается в том, что строка перевода перестаёт быть просто текстом и становится шаблоном с вложенной структурой:
Пример базовой концепции:
"You agree to our <b>Terms of Service</b> and <a>Privacy Policy</a>."
Здесь <b> и <a> не являются
HTML, а представляют абстрактные rich text элементы, которые связываются
с реальными компонентами на стороне приложения.
FormatJS реализует rich text через библиотеку
intl-messageformat, которая парсит ICU-сообщения и
формирует дерево узлов. Каждый тег в сообщении становится отдельным
узлом AST (Abstract Syntax Tree).
Процесс обработки можно представить следующим образом:
Важный момент: rich text не является HTML. Это абстрактная модель, независимая от платформы.
Rich text в FormatJS реализуется через XML-подобные теги внутри строки сообщения:
{
"terms": "Accept <b>terms</b> and <link>privacy policy</link>."
}
Теги могут быть произвольными, но должны соответствовать переданным форматерам.
Поддерживаемые элементы:
<b> — жирное выделение<i> — курсив<link> — ссылка<span> — нейтральный контейнерКаждый тег требует соответствующего обработчика при форматировании.
В react-intl rich text реализуется через передачу
React-компонентов в values объекта
FormattedMessage.
Пример:
import { FormattedMessage } from "react-intl";
<FormattedMessage
id="policy.text"
defaultMessage="Accept <b>terms</b> and <link>privacy policy</link>."
values={{
b: (chunks) => <strong>{chunks}</strong>,
link: (chunks) => <a href="/privacy">{chunks}</a>
}}
/>
chunks — это вложенное содержимое между тегамиRich text форматирование в FormatJS принципиально отделяет текст локализации от логики UI. Переводчик работает с абстрактными тегами, не зная их визуального представления.
Пример строки перевода:
"readMore": "Read <link>more details</link> about this topic."
А логика отображения задаётся в коде:
link: (chunks) => <Link to="/details">{chunks}</Link>
Такой подход позволяет:
FormatJS поддерживает вложенность тегов, что позволяет строить сложные структуры.
Пример:
"nested": "<b>Important: <i>read carefully</i></b>"
Обработка:
values={{
b: (chunks) => <strong>{chunks}</strong>,
i: (chunks) => <em>{chunks}</em>
}}
В результате получается корректная вложенная структура React-элементов.
Несмотря на гибкость, существует ряд ограничений:
Нарушение этих правил приводит к разрыву структуры AST и ошибкам форматирования.
FormatJS может возвращать не только React-элементы, но и строки или структуры данных.
Пример использования IntlMessageFormat напрямую:
import IntlMessageFormat from "intl-messageformat";
const msg = new IntlMessageFormat(
"Click <b>here</b> to continue",
"en"
);
msg.format({
b: (chunks) => `**${chunks}**`
});
В этом случае rich text превращается в текстовую разметку Markdown-подобного вида.
Каждый rich text тег соответствует функции в объекте
values. Это ключевой механизм связывания структуры
сообщения с логикой приложения.
Таблица соответствий:
| Тег в сообщении | Обработчик | Результат |
|---|---|---|
<b> |
b: fn |
<strong> |
<link> |
link: fn |
<a> или <Link> |
<i> |
i: fn |
<em> |
<user> |
user: fn |
кастомный компонент |
Rich text форматирование требует строгого контроля, так как строки переводов могут приходить из внешних источников.
Основные принципы безопасности:
FormatJS не рендерит HTML напрямую, что снижает риск XSS-уязвимостей при корректной реализации.
Rich text особенно полезен для интеграции UI-компонентов в локализацию.
Пример:
values={{
button: (chunks) => <Button>{chunks}</Button>,
highlight: (chunks) => <span className="highlight">{chunks}</span>
}}
Сообщение:
"cta": "Click <button>this button</button> to continue"
Это позволяет переводчикам управлять структурой текста без вмешательства в код.
Rich text можно комбинировать с обычной интерполяцией:
"welcome": "Hello <b>{name}</b>, welcome back!"
values={{
name: userName,
b: (chunks) => <strong>{chunks}</strong>
}}
Таким образом один шаблон может содержать одновременно:
ICU MessageFormat не требует фиксированного порядка тегов. Переводчик может изменять структуру предложения:
"msg": "<link>Click here</link> to learn more"
или
"msg": "To learn more, <link>click here</link>"
Код остаётся неизменным, поскольку логика привязана к тегу, а не позиции.
Rich text увеличивает сложность обработки сообщений, так как включает:
Оптимизации включают:
Часто встречающиеся проблемы:
Каждая из этих ошибок приводит либо к потере форматирования, либо к падению рендеринга.
Для сложных интерфейсов рекомендуется разбивать сообщения:
Пример разделения:
"title": "Welcome"
"body": "Click <link>here</link> to continue"
"footer": "Need help? <support>Contact support</support>"
Rich text является частью более широкой архитектуры i18n в FormatJS:
react-intl — UI слойintl-messageformat — парсинг и форматирование@formatjs/icu-messageformat-parser — разбор ICU@formatjs/intl — полифиллы и расширенияRich text выступает связующим звеном между переводами и компонентной моделью приложения.