Интеграция React Intl

Интеграция начинается с установки ядра экосистемы FormatJS для React — пакета интернационализации, обеспечивающего форматирование строк, чисел, дат и управление словарями сообщений через ICU-синтаксис.

Основные зависимости:

npm install react-intl

В современных проектах часто требуется дополнительная настройка Babel или TypeScript для извлечения сообщений и строгой типизации, но базовый слой строится вокруг провайдера контекста и компонентов форматирования.

Ключевой элемент архитектуры — провайдер локали, который передаёт текущий язык и набор сообщений всем вложенным компонентам.


IntlProvider как центр управления локализацией

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

import { IntlProvider } from "react-intl";

const messages = {
  ru: {
    welcome: "Добро пожаловать",
    items: "У вас {count} сообщений"
  }
};

function App() {
  return (
    <IntlProvider locale="ru" messages={messages.ru}>
      <Root />
    </IntlProvider>
  );
}

IntlProvider решает три задачи:

  • передаёт текущую локаль (locale)
  • поставляет словарь сообщений (messages)
  • управляет фоллбэками при отсутствии ключей

При изменении языка компонент вызывает повторный рендер всего дерева, что делает локализацию реактивной частью состояния приложения.


Формат сообщений ICU и структура переводов

FormatJS использует ICU MessageFormat — стандарт описания интернационализированных строк.

Пример базового сообщения:

{
  "greeting": "Привет, {name}"
}

Сообщения могут содержать:

  • переменные
  • выбор форм
  • множественные числа
  • вложенные конструкции

Пример с условиями:

{
  "notifications": "У вас {count, plural, one {# уведомление} few {# уведомления} many {# уведомлений} other {# уведомления}}"
}

ICU-синтаксис позволяет избежать ручного управления грамматическими правилами языка и переносит логику в слой форматирования.


Использование сообщений через useIntl

Современный способ работы с переводами основан на хуке:

import { useIntl } from "react-intl";

function Header() {
  const intl = useIntl();

  return (
    <h1>
      {intl.formatMessage({ id: "welcome" })}
    </h1>
  );
}

useIntl предоставляет объект с методами:

  • formatMessage
  • formatNumber
  • formatDate
  • formatTime
  • formatRelativeTime

Такой подход исключает необходимость прокидывания props через дерево компонентов.


Компонент FormatMessage

Для декларативного использования сообщений применяется компонент:

import { FormattedMessage } from "react-intl";

function Profile({ name }) {
  return (
    <FormattedMessage
      id="greeting"
      values={{ name }}
    />
  );
}

Особенности:

  • автоматически выбирает перевод по id
  • поддерживает интерполяцию значений
  • упрощает JSX-структуру

При необходимости можно использовать render-prop стиль:

<FormattedMessage id="greeting">
  {(text) => <span>{text}</span>}
</FormattedMessage>

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

FormatJS оборачивает нативный Intl.NumberFormat, обеспечивая единообразное API:

const intl = useIntl();

intl.formatNumber(123456.78, {
  style: "currency",
  currency: "RUB"
});

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

import { FormattedNumber } from "react-intl";

<FormattedNumber
  value={1000}
  style="currency"
  currency="USD"
/>

Поддерживаются режимы:

  • валюты
  • проценты
  • единицы измерения
  • компактные форматы (1K, 1M)

Форматирование дат и времени

import { FormattedDate } from "react-intl";

<FormattedDate
  value={Date.now()}
  year="numeric"
  month="long"
  day="2-digit"
/>

Через useIntl:

intl.formatDate(new Date(), {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
});

Аналогично доступно форматирование времени:

intl.formatTime(new Date(), {
  hour: "2-digit",
  minute: "2-digit"
});

Работа с множественными числами и условиями

ICU plural rules зависят от локали и автоматически применяются:

intl.formatMessage(
  { id: "cart.items" },
  { count: 5 }
);

Пример сообщения:

{
  "cart.items": "В корзине {count, plural, one {# товар} few {# товара} many {# товаров} other {# товара}}"
}

Система учитывает правила языка без дополнительной логики в коде.


Вложенные компоненты и rich text форматирование

FormatJS поддерживает HTML-подобные конструкции внутри сообщений:

{
  "terms": "Я принимаю <link>условия использования</link>"
}

Рендер:

<FormattedMessage
  id="terms"
  values={{
    link: (chunks) => <a href="/terms">{chunks}</a>
  }}
/>

Это позволяет безопасно внедрять интерактивные элементы в переводимые строки без ручного HTML.


Подключение нескольких локалей и динамическое переключение

Архитектура предполагает хранение словарей по языкам:

const messages = {
  en: { welcome: "Welcome" },
  ru: { welcome: "Добро пожаловать" }
};

Переключение реализуется через состояние:

const [locale, setLocale] = useState("ru");

<IntlProvider locale={locale} messages={messages[locale]}>
  <App />
</IntlProvider>

При смене locale React пересобирает дерево с новыми переводами.


Форматирование относительного времени

intl.formatRelativeTime(-1, "day");

Результаты зависят от локали:

  • «вчера»
  • «2 дня назад»
  • «in 2 days»

Компонент:

import { FormattedRelativeTime } from "react-intl";

<FormattedRelativeTime value={-3} unit="day" />

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

При работе с большими приложениями важны следующие аспекты:

  • мемоизация сообщений
  • разделение локалей по чанкам
  • ленивый импорт переводов
  • избегание пересоздания messages на каждый рендер

Пример стабилизации объектов:

const messages = useMemo(() => loadMessages(locale), [locale]);

SSR и гидратация

Server-Side Rendering требует синхронизации локали между сервером и клиентом.

На сервере:

import { renderToString } from "react-dom/server";

const html = renderToString(
  <IntlProvider locale="ru" messages={messages.ru}>
    <App />
  </IntlProvider>
);

На клиенте важно использовать те же данные, чтобы избежать расхождения гидратации:

  • одинаковая локаль
  • идентичные сообщения
  • синхронная инициализация IntlProvider

TypeScript-интеграция и типизация сообщений

В крупных проектах применяется строгая типизация ключей сообщений:

type MessageIds = "welcome" | "cart.items";

const messages: Record<MessageIds, string> = {
  welcome: "Добро пожаловать",
  cart.items: "Товары в корзине"
};

Дополнительно возможно генерировать типы через Babel-плагины FormatJS для автоматической синхронизации ключей.


Архитектурные особенности и роль в экосистеме React

React выступает базовой платформой для построения интерфейсов, а система интернационализации FormatJS расширяет её слой представления, отделяя текстовую логику от компонентов.

React Intl интегрируется через контекст React и формирует единый слой управления локализацией без необходимости внешних сервисов или ручной обработки языковых правил.

Такое разделение обеспечивает:

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