Skip to main content

Руководство по react-intl: настройка интернационализации React

Настройте FormatJS react-intl в приложении React с помощью IntlProvider, FormattedMessage, useIntl, формата сообщений ICU и автоматического перевода.

Используете вместо этого react-i18next? Открыть наше руководство по react-i18next

1

Установить react-intl

react-intl входит в проект FormatJS. Он предоставляет компоненты и хуки React для форматирования строк, чисел, дат и форм множественного числа по стандарту ICU MessageFormat.

Кроме React, у react-intl нет зависимостей среды выполнения. Для форматирования чисел и дат он использует встроенный в браузер API Intl и поставляется с собственным синтаксическим анализатором ICU MessageFormat для множественного числа, select и форматированного текста.
Terminal
npm install react-intl
2

Настроить IntlProvider

Оберните корень приложения в IntlProvider. Передайте активную локаль и плоский объект messages. После этого каждый вложенный компонент сможет получать переводы через FormattedMessage или useIntl.

src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { IntlProvider } from 'react-intl';
import App from './App';
import enMessages from './messages/en.json';
import deMessages from './messages/de.json';

const messages: Record<string, Record<string, string>> = {
  en: enMessages,
  de: deMessages,
};

// Detect locale from browser or your routing layer
const locale = navigator.language.split('-')[0] || 'en';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <IntlProvider locale={locale} messages={messages[locale] || messages.en}>
      <App />
    </IntlProvider>
  </React.StrictMode>
);
IntlProvider требует плоский объект сообщений «ключ — значение», например { "app.greeting": "Hello" }. Вложенный JSON перед передачей в IntlProvider необходимо сделать плоским либо преобразовать с помощью такой утилиты, как flat.

Файлы сообщений

Создайте по одному файлу JSON для каждой локали. react-intl изначально использует синтаксис ICU MessageFormat: формы множественного числа, select и переменные записываются непосредственно в строках сообщений.

messages/en.json & messages/de.json
// messages/en.json
{
  "app.greeting": "Hello, {name}!",
  "nav.home": "Home",
  "nav.about": "About",
  "nav.settings": "Settings",
  "cart.itemCount": "{count, plural, one {# item} other {# items}} in your cart"
}

// messages/de.json
{
  "app.greeting": "Hallo, {name}!",
  "nav.home": "Startseite",
  "nav.about": "Über uns",
  "nav.settings": "Einstellungen",
  "cart.itemCount": "{count, plural, one {# Artikel} other {# Artikel}} in Ihrem Warenkorb"
}
Для организации используйте идентификаторы с точками, например "nav.home". В отличие от react-i18next, react-intl ожидает плоский объект messages: плоскими становятся ключи, а не структура.
3

Использовать переводы в компонентах

react-intl предоставляет два основных API: компонент FormattedMessage для отрисовки переведённого JSX и хук useIntl для императивного доступа — заполнителей, меток aria и программного форматирования.

Компонент FormattedMessage

Используйте FormattedMessage для декларативных переводов в JSX. Передайте идентификатор сообщения и значения интерполяции. Компонент напрямую отрисует переведённую строку.

Greeting.tsx
import { FormattedMessage } from 'react-intl';

function Greeting({ userName }: { userName: string }) {
  return (
    <div>
      <h1>
        <FormattedMessage
          id="app.greeting"
          values={{ name: userName }}
        />
      </h1>
      <nav>
        <a href="/"><FormattedMessage id="nav.home" /></a>
        <a href="/about"><FormattedMessage id="nav.about" /></a>
      </nav>
    </div>
  );
}

Хук useIntl

Используйте useIntl(), когда переведённая строка нужна как обычное значение: для заполнителей полей ввода, aria-label, document.title или передачи строк в API вне React. Он также предоставляет formatNumber, formatDate и formatRelativeTime.

SearchBar.tsx
import { useIntl } from 'react-intl';

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

  return (
    <input
      type="search"
      placeholder={intl.formatMessage({ id: 'search.placeholder' })}
      aria-label={intl.formatMessage({ id: 'search.ariaLabel' })}
    />
  );
}

// useIntl also gives you formatNumber, formatDate, formatRelativeTime:
function PriceTag({ amount, currency }: { amount: number; currency: string }) {
  const intl = useIntl();
  return (
    <span>{intl.formatNumber(amount, { style: 'currency', currency })}</span>
  );
}

Форматированный текст (HTML в переводах)

Встраивайте JSX в переводы с помощью тегов, похожих на XML, в строках сообщений. Передавайте обработчики тегов через свойство values, чтобы отрисовывать ссылки, полужирный текст или любой компонент React внутри переведённого сообщения.

SignUp.tsx
import { FormattedMessage } from 'react-intl';

// Message: "By signing up, you agree to our <link>Terms</link>."
// Key: "signup.terms"
// Value: "By signing up, you agree to our <link>Terms</link>."

function SignUp() {
  return (
    <FormattedMessage
      id="signup.terms"
      values={{
        link: (chunks) => <a href="/terms" className="underline">{chunks}</a>,
      }}
    />
  );
}
По умолчанию FormattedMessage отрисовывает React Fragment. Если нужен конкретный элемент-обёртка, передайте свойство textComponent в IntlProvider или оберните FormattedMessage собственным элементом.

Извлечение сообщений с @formatjs/cli

FormatJS предоставляет CLI для автоматического извлечения идентификаторов сообщений из исходного кода в файл JSON. Файл сообщений остаётся синхронизированным с компонентами без ручного учёта.

Terminal
# Install the CLI
npm install -g @formatjs/cli

# Extract messages from source code into a JSON file
formatjs extract 'src/**/*.tsx' --out-file messages/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]'

# Or use explicit IDs (recommended):
formatjs extract 'src/**/*.tsx' --out-file messages/en.json

# Compile messages for production (optional, improves perf)
formatjs compile messages/en.json --out-file compiled/en.json
formatjs compile messages/de.json --out-file compiled/de.json
4

Формы множественного числа и ICU select

react-intl изначально использует ICU MessageFormat. Формы множественного числа, выбор по роду и вложенное форматирование записываются непосредственно в строках сообщений: соглашения о суффиксах и отдельные ключи не нужны.

ICU plural syntax by language
// ICU MessageFormat syntax — react-intl uses this natively
// English
{
  "cart.itemCount": "{count, plural, one {# item} other {# items}} in your cart",
  "inbox.unread": "You have {count, plural, =0 {no unread messages} one {# unread message} other {# unread messages}}"
}

// Arabic — 6 plural forms
{
  "cart.itemCount": "{count, plural, zero {لا عناصر} one {عنصر واحد} two {عنصران} few {# عناصر} many {# عنصرًا} other {# عنصر}} في سلتك"
}

// Japanese — 1 form (other)
{
  "cart.itemCount": "カートに{count}個の商品があります"
}
Никогда не задавайте логику множественного числа жёстко в JavaScript. В арабском 6 форм множественного числа, во французском 0 считается единственным числом, а в японском различий множественного числа нет. Поручите обработку правил ICU MessageFormat, передав только значение count.

ICU select для рода и ролей

Используйте синтаксис ICU select для переводов, зависящих от контекста, например рода, роли пользователя или значения состояния. Выражение select выбирает правильный вариант на основе переданного значения.

ICU select syntax
// Gender-dependent messages using ICU select
{
  "user.greeting": "{gender, select, male {He} female {She} other {They}} liked your post.",
  "user.invitation": "{role, select, admin {You can manage all settings.} editor {You can edit content.} other {You can view content.}}"
}

// Usage:
<FormattedMessage
  id="user.greeting"
  values={{ gender: user.gender }}
/>

Автоматизировать контроль качества перевода

Выявляйте отсутствующие ключи и нарушенные заполнители до выпуска с помощью i18n-validate. Тестируйте интерфейс с псевдопереводами через i18n-pseudo, пока настоящие переводы ещё не готовы.

Распространённые ошибки

Чрезмерная зависимость от defaultMessage

defaultMessage — резервное значение для разработки, а не стратегия перевода. Если использовать defaultMessage для всех строк, результат извлечения сообщений будет содержать английский текст, но переводчики могут пропустить новые ключи. Всегда извлекайте и поддерживайте полный файл исходной локали.

Вложенные объекты вместо плоских ключей

IntlProvider ожидает плоский Record&lt;string, string&gt; для messages. Если передать вложенный JSON вроде { nav: { home: "Home" } }, react-intl не найдёт ключ "nav.home". Перед передачей сделайте сообщения плоскими или используйте такую библиотеку, как flat.

IntlProvider вызывает повторную отрисовку

Если создавать объект messages внутри функции render, IntlProvider получает новую ссылку на объект при каждой отрисовке, поэтому все потребители отрисовываются повторно. Мемоизируйте messages с помощью useMemo или определите их вне компонента.

В тестах отсутствует IntlProvider

Компоненты, использующие FormattedMessage или useIntl, создадут исключение при отрисовке без родительского IntlProvider. В тестах оберните компонент в IntlProvider с locale="en" и пустым либо минимальным объектом messages.

Рекомендуемая структура файлов

Project Structure
my-react-app/
├── messages/
│   ├── en.json              # Source of truth (English)
│   ├── de.json              # German
│   ├── ja.json              # Japanese
│   └── es.json              # Spanish
├── compiled/                # Optional: compiled messages for prod
│   ├── en.json
│   └── ...
├── src/
│   ├── main.tsx             # App entry with IntlProvider
│   ├── App.tsx
│   └── components/
│       ├── Greeting.tsx      # Uses FormattedMessage
│       └── SearchBar.tsx     # Uses useIntl
└── package.json

Попробовать i18n Agent

Перетащите сюда файл перевода

JSON, YAML, PO, XML, CSV, Markdown, Properties

или нажмите, чтобы выбрать

Целевые языки

Регистрация не требуетсяМгновенный расчёт

Резервные локали с react-intl-locale-chain

Когда в региональной локали, например pt-BR, отсутствует ключ перевода, react-intl сразу переходит на локаль по умолчанию, не проверяя сначала родительскую локаль pt.

Terminal
npm install react-intl-locale-chain
Configuration
<LocaleChainProvider
  fallbacks={{
    'pt-BR': ['pt', 'en'],
    'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
  }}
  defaultLocale="en"
>
  <App />
</LocaleChainProvider>

Полный список поддерживаемых фреймворков и 75 встроенных цепочек приведён в нашем руководстве по резервным локалям. Learn more →

Часто задаваемые вопросы