Переход на экосистему FormatJS требует переосмысления базовых понятий интернационализации. Многие библиотеки, такие как i18next, vue-i18n или самописные решения, опираются на простую модель «ключ → строка», тогда как FormatJS строится вокруг стандарта ICU MessageFormat и более строгого разделения форматирования и данных.
Ключевые различия:
В классических системах интернационализации часто встречается логика вида:
t('cart.items', { count: 5 })
В FormatJS аналогичная концепция переносится в само сообщение:
You have {count, plural, one {# item} other {# items}}
Это фундаментальный сдвиг: логика множественных форм перестаёт жить в коде и перемещается в слой сообщений.
FormatJS использует ICU MessageFormat как единый стандарт описания сообщений. Это означает, что любые переводы должны быть преобразованы в декларативные выражения.
Типовые конструкции:
Hello, {name}
Balance: {amount, number, currency}
Today is {date, date, long}
{count, plural,
one {One message}
other {# messages}
}
В отличие от библиотек, где plural rules реализуются в коде или через отдельные функции, здесь вся логика описывается в строке сообщения.
i18next широко использует ключевую модель и JSON-структуры ресурсов. Основная задача миграции — перенос логики из runtime в ICU-шаблоны.
i18next:
{
"cart": {
"items": "You have {{count}} items"
}
}
FormatJS:
You have {count, plural, one {# item} other {# items}}
Изменяется сама философия хранения:
i18next:
t('welcome', { name: 'Alex' })
Welcome {{name}}
FormatJS:
Welcome {name}
Разница заключается в отсутствии специального синтаксиса вроде
{{ }} — используется единый формат ICU.
i18next:
t('items', { count: 3 })
{
"items_one": "1 item",
"items_other": "{{count}} items"
}
FormatJS:
{count, plural, one {# item} other {# items}}
Ключевая оптимизация миграции — устранение множества ключей под разные формы и переход к одному сообщению.
В i18next распространена система namespaces:
t('common:button.save')
В FormatJS чаще используется плоский набор сообщений, разделённых по загрузке:
const messages = defineMessages({
save: {
id: 'button.save',
defaultMessage: 'Save'
}
})
Миграция заключается в переносе namespace-логики в систему загрузки файлов или сборки сообщений.
FormatJS включает react-intl, который со временем
эволюционировал. При переходе со старых версий ключевые изменения
связаны с API и типизацией сообщений.
Современная структура:
import { IntlProvider } from 'react-intl'
<IntlProvider locale="en" messages={messages}>
<App />
</IntlProvider>
В старых проектах часто использовались:
Миграция требует централизации сообщений и их нормализации в единый формат.
import { defineMessages } from 'react-intl'
const messages = defineMessages({
title: {
id: 'page.title',
defaultMessage: 'Dashboard'
}
})
Это позволяет:
В Vue-экосистеме часто используется ключевая модель с JSON:
{
"errors": {
"required": "This field is required"
}
}
И вызов:
t('errors.required')
В FormatJS переход означает:
Пример преобразования:
This field is required
остаётся без изменений, но сложные конструкции мигрируют:
{field, select,
email {Email is required}
password {Password is required}
other {This field is required}
}
Одним из ключевых аспектов миграции является изменение модели хранения.
Пример трансформации структуры:
{
"profile": {
"title": "User profile",
"greeting": "Hello {{name}}"
}
}
превращается в набор сообщений:
User profile
Hello {name}
В старых библиотеках часто используется ручное форматирование:
new Date(date).toLocaleDateString()
или через вспомогательные функции i18next.
FormatJS использует Intl API напрямую через
декларативные сообщения.
{date, date, medium}
{date, time, short}
{price, number, currency}
Переход требует:
Системы вроде i18next позволяют строить сообщения динамически в коде:
t(`status.${type}`)
В FormatJS такой подход считается нежелательным. Вместо этого:
select в ICUПример:
{status, select,
success {Operation completed}
error {Operation failed}
pending {Operation pending}
other {Unknown status}
}
Это снижает гибкость, но повышает предсказуемость и локализационную корректность.
Одно из существенных изменений при переходе — появление строгого контроля сообщений.
В экосистеме FormatJS активно используется:
Пример описания:
type Messages = {
'button.save': string
'button.cancel': string
}
Миграция с менее строгих систем требует:
В крупных приложениях переход редко происходит одномоментно. Обычно используется гибридный подход:
Типичный сценарий:
IntlProviderМногие старые системы используют глубокую структуру ключей, которая плохо переносится в плоскую модель сообщений.
{{variable}} → {variable}При миграции часто возникают ситуации, когда один текст представлен несколькими ключами. FormatJS требует нормализации до единого сообщения.
ICU строго определяет plural rules, и ошибки проявляются быстрее, чем в runtime-строках:
otherFormatJS активно использует build-time анализ.
Типичный процесс:
defineMessagesЭто заменяет runtime-решения вроде динамической подгрузки ключей.
Миграция требует внедрения build-step:
После миграции структура приложения обычно изменяется:
Пример архитектурного слоя:
В старых системах fallback часто реализуется неявно (например, через ключ или английский текст). FormatJS требует явного определения:
defaultMessage как fallbackЭто изменяет поведение системы при неполных переводах: вместо скрытого fallback появляется управляемая деградация интерфейса.
FormatJS переносит значительную часть логики в compile-time, что влияет на runtime:
IntlПри этом увеличивается роль сборки и предварительной обработки сообщений.