В системах интернационализации на базе ICU MessageFormat исходные строки сообщений часто содержат сложную структуру: склонения, множественные формы, условия, вложенные выражения. Прямое выполнение таких шаблонов в рантайме приводит к значительным издержкам: парсинг строки, построение AST и вычисление результата для каждой отрисовки интерфейса.
Компиляция сообщений переводит эти операции из рантайма в этап сборки приложения. Вместо интерпретации строк в браузере формируется заранее подготовленный JavaScript-код, который выполняет только минимально необходимую логику форматирования.
Ключевой эффект такого подхода заключается в том, что:
FormatJS разделяет процесс работы с сообщениями на несколько этапов:
Основной инструмент компиляции — пакет
babel-plugin-react-intl и утилиты
@formatjs/cli.
Первый этап — статический анализ исходного кода. Babel-плагин проходит по AST и находит вызовы форматирующих функций:
defineMessagesintl.formatMessage<FormattedMessage />Пример исходного кода:
import { defineMessages } from 'react-intl';
const messages = defineMessages({
title: {
id: 'app.title',
defaultMessage: 'Привет, {name}!'
}
});
После извлечения формируется JSON-каталог:
{
"app.title": {
"defaultMessage": "Привет, {name}!"
}
}
Этот этап важен тем, что позволяет отделить код приложения от языковых ресурсов и подготовить их к дальнейшей трансформации.
FormatJS использует ICU MessageFormat как стандарт описания сообщений:
Привет, {name}!
У вас {count, plural, one {# сообщение} few {# сообщения} many {# сообщений} other {# сообщений}}
ICU-строки являются декларативными, но не исполняемыми напрямую. Их необходимо преобразовать в исполняемый код.
Компилятор @formatjs/icu-messageformat-parser разбирает
строку и строит AST, после чего генератор кода превращает его в
оптимизированную функцию.
Исходное сообщение:
У вас {count, plural, one {# письмо} other {# писем}}
После компиляции:
function (values) {
switch (getPluralRule(values.count)) {
case 'one':
return "У вас " + values.count + " письмо";
default:
return "У вас " + values.count + " писем";
}
}
Важный момент: вся структура ICU исчезает из рантайма, остаётся только логика ветвления.
CLI-инструмент @formatjs/cli используется для
автоматизации подготовки сообщений:
extract)validate)compile)Типичный production-пайплайн:
formatjs extract "src/**/*.ts" --out-file messages.json
formatjs compile messages.json --out-file compiled-messages.json
Результатом становится файл, содержащий уже готовые функции форматирования или оптимизированные структуры данных.
babel-plugin-react-intl позволяет компилировать
сообщения прямо во время сборки приложения.
Конфигурация:
{
"plugins": [
["react-intl", {
"compile": true,
"removeDefaultMessage": false
}]
]
}
После трансформации:
Исходный JSX:
<FormattedMessage id="greeting" defaultMessage="Привет, {name}" values={{ name }} />
Преобразуется в:
intl.formatMessage(
{ id: "greeting", defaultMessage: "Привет, {name}" },
{ name }
);
Если включена полная компиляция, formatMessage
заменяется на заранее сгенерированную функцию.
Одно из ключевых улучшений production-сборки — исключение парсера ICU из конечного бандла.
Без компиляции:
С компиляцией:
Это особенно важно для мобильных и low-end устройств.
Компиляция сообщений влияет на размер сборки в нескольких направлениях:
Пример сравнения:
Без компиляции:
С компиляцией:
В крупных приложениях разница может составлять десятки процентов от общего размера i18n-бандла.
Production-архитектура часто предполагает раздельную загрузку локалей:
const messages = await import(`./compiled/${locale}.json`);
Компиляция позволяет:
Компилированные сообщения часто сопровождаются хешированием:
{
"app.title": {
"hash": "a94f3b",
"fn": function(values) { ... }
}
}
Хеш используется для:
Компилятор FormatJS поддерживает все основные конструкции ICU:
Пример:
{gender, select,
male {Он добавил {count, plural, one {# файл} other {# файлов}}}
female {Она добавила {count, plural, one {# файл} other {# файлов}}}
other {Добавлено {count} файлов}
}
После компиляции структура превращается в вложенные условия без необходимости парсинга строк.
Компиляция сообщений требует контроля нескольких аспектов:
В крупных системах применяется стратегия:
FormatJS компиляция интегрируется с:
В каждом случае цель одинакова: перенести ICU-логику из runtime в build-time слой.
После production-компиляции runtime-часть FormatJS выполняет минимальную роль:
Фактически runtime становится тонким слоем оркестрации, а не интерпретатором сообщений.
Компиляция сообщений обеспечивает стабильное поведение интерфейса:
Производительность становится зависимой только от количества сообщений, но не от их внутренней структуры.