Миграция с react-intl v2/v3

FormatJS представляет собой набор библиотек для интернационализации, построенный вокруг ICU MessageFormat и строгой типизации сообщений. В рамках React-приложений ключевым компонентом выступает react-intl, который в версиях v2/v3 реализует разные подходы к API, контексту и работе с форматированием сообщений.

Миграция между v2 и v3 связана не только с изменением интерфейсов, но и с переходом от HOC-подхода к более гибкой модели с хуками, улучшенной типизацией и изменённой стратегией экспорта API.


Изменения архитектурного уровня

От HOC к Hooks API

В v2 основная модель потребления интернационализации строилась вокруг:

  • injectIntl
  • FormattedMessage
  • FormattedNumber, FormattedDate

В v3 добавляется и активно закрепляется функциональный стиль:

  • useIntl

Ключевое отличие заключается в том, что доступ к API интернационализации перестаёт зависеть от обёрток высшего порядка и становится напрямую доступным внутри функциональных компонентов.

Существенное изменение:

  • v2: доступ через props-инъекцию
  • v3: доступ через React Hooks

IntlProvider и контекст

Стабилизация контекста

В обеих версиях используется IntlProvider, однако поведение контекста в v3 становится более предсказуемым и оптимизированным для Concurrent Rendering.

Основные изменения:

  • улучшенная мемоизация сообщений
  • более стабильное поведение при смене locale
  • снижение лишних повторных рендеров

Структура использования

В v2:

<IntlProvider locale="ru" messages={messages}>
  <App />
</IntlProvider>

В v3 структура сохраняется, но внутренний механизм обработки сообщений изменяется: больше внимания уделяется стабильности ссылок на messages и оптимизации их передачи через контекст.


formatMessage и изменения API

Сигнатура и поведение

В react-intl v2:

intl.formatMessage({ id: 'hello' })

В v3 сохраняется базовая сигнатура, но усиливается строгая работа с типами и кешированием результатов форматирования.

Изменения:

  • более агрессивное кеширование форматированных строк
  • улучшенная работа с ICU-выражениями
  • стабилизация поведения при динамических параметрах

Переход от injectIntl к useIntl

injectIntl (v2)

Компонент высшего порядка:

export default injectIntl(MyComponent);

Использование:

props.intl.formatMessage(...)

Недостатки подхода:

  • усложнение композиции компонентов
  • лишний уровень обёртки в дереве React
  • трудности при типизации

useIntl (v3)

Функциональный доступ:

const intl = useIntl();
intl.formatMessage({ id: 'key' });

Ключевые преимущества:

  • отсутствие HOC-обёртки
  • прямой доступ к API
  • улучшенная читаемость логики компонента
  • более гибкая композиция

Изменения в работе с сообщениями

ICU MessageFormat как основа

FormatJS продолжает использовать ICU-синтаксис:

  • pluralization
  • select
  • number formatting
  • date/time formatting

Пример:

{
  "items": "{count, plural, one {# элемент} few {# элемента} other {# элементов}}"
}

Различия обработки v2 и v3

  • v2: менее строгая валидация сообщений
  • v3: усиленная проверка корректности ICU-синтаксиса
  • v3: более стабильные ошибки при некорректных форматах

Форматирование чисел, дат и времени

Number formatting

intl.formatNumber(1000, { style: 'currency', currency: 'USD' })

В v3 улучшена:

  • производительность кеширования
  • согласованность локалей
  • повторное использование форматтеров

Date formatting

intl.formatDate(new Date(), { year: 'numeric', month: 'long' })

Изменения:

  • оптимизация создания Intl.DateTimeFormat
  • уменьшение повторных инстансов форматтеров
  • улучшенная работа с timezone

Migration messages: структура и типизация

defineMessages

В обеих версиях используется:

defineMessages({
  title: {
    id: 'app.title',
    defaultMessage: 'Заголовок'
  }
});

В v3 усиливается интеграция с TypeScript и строгая типизация id.


Babel и извлечение сообщений

FormatJS активно использует Babel-плагины:

  • babel-plugin-react-intl

Функциональность:

  • автоматическое извлечение сообщений
  • генерация JSON-файлов локализации
  • валидация ICU-синтаксиса на этапе сборки

Изменения в v3:

  • улучшенная совместимость с современными версиями Babel
  • более стабильное извлечение динамических сообщений
  • снижение количества false-positive ошибок

Обработка отсутствующих переводов

Поведение v2

  • fallback на defaultMessage
  • предупреждения в консоли
  • частичная игнорируемость ошибок

Поведение v3

  • более строгая диагностика
  • улучшенные механизмы fallback
  • контроль консистентности сообщений между локалями

Работа с локалями

Смена locale

В обеих версиях:

<IntlProvider locale="en" messages={messages}>

В v3:

  • уменьшено количество перерендеров при смене locale
  • оптимизирована пересборка кешей форматтеров

Производительность и оптимизация

Основные улучшения v3

  • мемоизация formatters
  • снижение затрат на создание Intl-объектов
  • оптимизация context propagation
  • уменьшение лишних вычислений при повторных рендерах

Типичные проблемы миграции

1. Зависимость от injectIntl

Код, завязанный на HOC, требует рефакторинга в сторону hooks.

2. Неявные зависимости intl

В v2 часто intl передавался через props, в v3 предпочтение отдано прямому вызову useIntl.

3. Структура сообщений

Некорректные ICU-строки, ранее допускавшиеся, в v3 могут приводить к ошибкам сборки или рантайма.

4. Кеширование formatters

Поведение кеша отличается, что может влиять на редкие кейсы динамического форматирования.


Сравнение моделей API

Область v2 v3
Доступ к intl injectIntl useIntl
Производительность базовая улучшенная
ICU валидация мягкая строгая
Типизация ограниченная расширенная
Архитектура HOC Hooks + context

Структура миграционного перехода

Основные изменения затрагивают:

  • замену HOC на hooks
  • пересмотр обработки сообщений
  • обновление Babel-конфигурации
  • корректировку ICU-синтаксиса
  • усиление типизации
  • контроль кеширования форматтеров

Совместимость и промежуточные состояния

В процессе миграции возможны гибридные сценарии:

  • одновременное использование HOC и hooks
  • частичная миграция компонентов
  • сохранение старых JSON переводов с адаптацией

react-intl v3 сохраняет обратную совместимость на уровне базовых API, однако архитектурные изменения делают постепенный переход более предсказуемым при поэтапной замене компонентов.