В экосистеме FormatJS процесс интернационализации представляет собой последовательную цепочку этапов, в которой текстовые сообщения проходят путь от исходного кода до локализованного представления в интерфейсе. Каждый этап сопровождается собственным состоянием (статусом), влияющим на поведение системы во время сборки и выполнения.
На уровне исходного кода сообщения в FormatJS представлены через
MessageDescriptor. Это абстракция, описывающая текст,
который будет локализован.
{
id: "user.greeting",
defaultMessage: "Hello, {name}",
description: "Greeting message on dashboard"
}
На этом этапе сообщения находятся в состоянии необработанного описания. Они ещё не участвуют в локализации, но уже могут быть:
1. Определено (Defined) Сообщение существует в коде
как MessageDescriptor.
2. Извлекается (Extracted) Сообщение обнаружено
Babel-плагином babel-plugin-formatjs или CLI-инструментом
@formatjs/cli.
3. Отсутствует перевод (Missing Translation) Сообщение присутствует в коде, но отсутствует в наборе локализованных сообщений.
4. Неиспользуемое (Unused) Перевод присутствует в словаре, но не встречается в исходном коде.
FormatJS использует статический анализ кода для формирования списка всех локализуемых строк.
Основной инструмент — Babel-плагин:
npm install babel-plugin-formatjs
Пример конфигурации:
{
"plugins": [
["formatjs", {
"idInterpolationPattern": "[sha512:contenthash:base64:6]"
}]
]
}
Во время анализа кода каждое сообщение проходит состояния:
Пример результата извлечения:
{
"user.greeting": {
"defaultMessage": "Hello, {name}",
"description": "Greeting message on dashboard"
}
}
После извлечения сообщения проходят этап компиляции. В FormatJS
используется intl-messageformat, который преобразует
ICU-строки в исполняемые функции форматирования.
import { createIntl, createIntlCache } from 'react-intl';
const cache = createIntlCache();
const intl = createIntl({
locale: 'en',
messages: {
"user.greeting": "Hello, {name}"
}
}, cache);
На этом этапе формируется промежуточное состояние:
1. Не скомпилировано (Raw Message) Строка хранится как текст ICU-сообщения.
2. Скомпилировано (Compiled Message) ICU-синтаксис преобразован в функцию форматирования.
3. Оптимизировано (Optimized AST) Сообщение представлено в виде AST-дерева для быстрого вычисления.
Во время работы приложения сообщения проходят финальную стадию — рендеринг.
intl.formatMessage(
{ id: "user.greeting", defaultMessage: "Hello, {name}" },
{ name: "Alex" }
);
1. Разрешено (Resolved) Сообщение найдено в текущей локали.
2. Фолбэк (Fallback) Сообщение отсутствует в текущей
локали, используется defaultMessage или fallback
locale.
3. Не найдено (Missing Message) Нет ни перевода, ни
defaultMessage. Может быть выброшено предупреждение.
4. Отформатировано (Formatted) ICU-строка успешно преобразована в итоговый текст.
FormatJS поддерживает динамическую смену локали. Это влияет на весь процесс форматирования.
1. Инициализирована (Initialized Locale) Установлена начальная локаль приложения.
2. Загружается (Loading Locale Data) Асинхронная загрузка переводов.
3. Активна (Active Locale) Локаль используется для форматирования сообщений.
4. Частично загружена (Partially Loaded) Доступны не все сообщения или не все сегменты ICU данных (числа, даты, плурализация).
Пример загрузки:
async function loadLocale(locale) {
const messages = await fetch(`/i18n/${locale}.json`).then(r => r.json());
return {
locale,
messages
};
}
FormatJS использует ICU Message Syntax, которая поддерживает сложные конструкции: множественное число, выбор, вложенные параметры.
1. Парсинг (Parsing) Строка преобразуется в AST.
2. Валидация (Validation) Проверка корректности ICU-синтаксиса.
3. Интерполяция (Interpolation) Подстановка переменных.
4. Выбор ветки (Selection) Определение plural/sel ect формы.
Пример:
intl.formatMessage(
{
id: "cart.items",
defaultMessage: "You have {count, plural, one {# item} other {# items}}"
},
{ count: 3 }
);
Plural и select выражения формируют отдельный слой состояний внутри ICU.
Система выбирает активную ветку на основе правил CLDR.
{ gender, select,
male {He}
female {She}
other {They}
}
Состояния:
otherВ react-intl сообщения проходят через React
lifecycle.
1. Provider не инициализирован
IntlProvider отсутствует, форматирование невозможно.
2. Provider активен Контекст доступен через
useIntl().
3. Перерендер при смене локали Все компоненты, использующие форматирование, обновляются.
import { IntlProvider, useIntl } fr om 'react-intl';
function App() {
return (
<IntlProvider locale="en" messages={{}}>
<Page />
</IntlProvider>
);
}
FormatJS использует кеширование для ускорения форматирования.
1. Пустой кеш Первый запуск форматирования.
2. Частично заполненный кеш Часть сообщений уже скомпилирована.
3. Полностью заполненный кеш Повторное использование функций форматирования без пересборки.
Система обработки ошибок в FormatJS определяет несколько уровней деградации.
1. Invalid ICU Syntax Ошибка парсинга сообщения.
2. Missing Variable Не передан параметр форматирования.
3. Unknown Locale Data Отсутствуют данные CLDR.
4. Fallback Activation Система переключается на резервную локаль.
При использовании CI/CD и @formatjs/cli сообщения
проходят этап сборки.
npx formatjs compile
Пример трансформации:
"user.greeting" -> "a1b2c3"
FormatJS зависит от данных CLDR, и локали могут находиться в разных состояниях готовности.
В рамках FormatJS можно выделить многослойную модель статусов:
Эта система образует непрерывный поток трансформации данных, где каждое сообщение последовательно проходит стадии анализа, нормализации, компиляции и выполнения.