Выбор стратегии локализации в приложениях на базе FormatJS определяется балансом между производительностью, гибкостью, сложностью сборки и требованиями к обновлению переводов без релиза. Библиотека предоставляет набор инструментов, позволяющих реализовать как полностью динамическую модель интернационализации, так и подход с максимальной компиляцией сообщений на этапе сборки.
Динамический подход предполагает, что все сообщения остаются
доступными в рантайме, а форматирование выполняется непосредственно в
браузере или на сервере через Intl и обёртки FormatJS.
Основной элемент этой стратегии — использование
react-intl (или @formatjs/intl) без
агрессивной предварительной компиляции сообщений.
Пример базовой структуры:
import { IntlProvider, FormattedMessage } from 'react-intl';
const messages = {
ru: {
greeting: 'Привет, {name}!',
},
en: {
greeting: 'Hello, {name}!',
},
};
export function App() {
return (
<IntlProvider locale="ru" messages={messages['ru']}>
<FormattedMessage id="greeting" values={{ name: 'Alex' }} />
</IntlProvider>
);
}
Ключевая характеристика этого подхода — простота доставки переводов, так как они могут поставляться как JSON с сервера.
Преимущества:
Ограничения:
FormatJS предоставляет CLI-инструменты для статического анализа исходного кода и извлечения сообщений из компонентов. Это фундамент для более строгой и масштабируемой локализации.
Обычно используется пакет @formatjs/cli.
Пример команды:
formatjs extract "src/**/*.tsx" --out-file messages.json
Исходный код:
import { defineMessages } from 'react-intl';
const messages = defineMessages({
title: {
id: 'page.title',
defaultMessage: 'Главная страница',
},
});
Результат extraction:
{
"page.title": {
"defaultMessage": "Главная страница"
}
}
Данный подход позволяет:
FormatJS поддерживает компиляцию ICU MessageFormat строк в оптимизированные структуры. Это снижает стоимость интерпретации сообщений в рантайме.
Используется пакет @formatjs/ts-transformer или
Babel-плагин.
Пример исходного сообщения:
const message = {
id: 'cart.items',
defaultMessage: 'В корзине {count, plural, one {# товар} few {# товара} many {# товаров} other {# товаров}}',
};
После компиляции сообщение превращается в структуру, готовую к быстрому выполнению без полного парсинга ICU-строки в рантайме.
Преимущества:
Недостатки:
На практике наиболее распространённым является гибридный подход, при котором:
Архитектура выглядит следующим образом:
id + defaultMessage.При росте приложения критически важным становится разбиение переводов по модулям.
Пример структуры:
locales/
ru/
common.json
auth.json
dashboard.json
en/
common.json
auth.json
dashboard.json
Загрузка может быть ленивой:
async function loadMessages(locale) {
const common = await import(`./locales/${locale}/common.json`);
const auth = await import(`./locales/${locale}/auth.json`);
return {
...common.default,
...auth.default,
};
}
Такой подход снижает начальный вес приложения и позволяет масштабировать количество локалей без деградации загрузки.
Runtime-first модель предполагает минимальную предварительную
обработку. Все ICU-строки остаются в исходном виде и интерпретируются в
браузере через IntlMessageFormat.
Пример:
import { IntlProvider } from 'react-intl';
<IntlProvider locale="ru" messages={messages}>
...
</IntlProvider>
Сообщения:
{
"price": "Цена: {value, number, currency}"
}
Особенности:
Эта стратегия подходит для небольших приложений или прототипов.
TypeScript-интеграция FormatJS позволяет вводить строгие контракты для сообщений.
Пример типизации:
type Messages = {
'page.title': string;
'cart.items': string;
};
Или более продвинутый вариант через генерацию типов из extracted JSON:
.d.ts файл;Преимущество подхода — снижение риска обращения к несуществующим ключам и улучшение качества поддержки локализации в крупных кодовых базах.
FormatJS основан на ICU MessageFormat, что позволяет выражать сложную логику прямо в строках перевода.
Пример ICU:
{
"notifications": "{count, plural, one {# уведомление} few {# уведомления} many {# уведомлений} other {# уведомлений}}"
}
Альтернативный подход — разбиение логики на код:
const label = plural(count, {
one: 'уведомление',
few: 'уведомления',
many: 'уведомлений',
other: 'уведомлений',
});
ICU-стратегия предпочтительнее при:
Кодовая стратегия — при:
Существует несколько моделей доставки переводов:
1. Полная загрузка
Все локали загружаются сразу:
import messages from './locales/all.json';
Простая, но не масштабируемая модель.
2. Ленивая загрузка по локали
Загружается только нужный набор:
const messages = await import(`./locales/${locale}.json`);
3. Гибрид с fallback
Если локаль неполная, используется базовая:
const merged = {
...messages['en'],
...messages[locale],
};
FormatJS позволяет задавать fallback-локали и поведение при отсутствии ключей.
Типичный сценарий:
ruenЛогика:
ru, используется
en;en, используется
defaultMessage.Такой подход критически важен для стабильности интерфейса в продакшене.
В больших системах стратегия локализации перестаёт быть техническим решением и становится архитектурным слоем.
Типовая модель включает:
Особое значение приобретает контроль:
Выбор подхода определяется не синтаксисом FormatJS, а требованиями системы:
Каждая стратегия формирует собственный профиль производительности, сложности и гибкости, и именно их комбинация определяет итоговую архитектуру интернационализации приложения.