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 потребує плоского об’єкта messages із парами ключів і значень (наприклад, { "app.greeting": "Hello" }). Перед передаванням до IntlProvider вкладений JSON потрібно перетворити на плоску структуру або скористатися для цього утилітою на кшталт 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-labels, 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. Форми множини, select за родом і вкладене форматування задаються безпосередньо в рядках повідомлень: окремі ключі або правила суфіксів не потрібні.

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 — просто передайте значення кількості.

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". Перетворіть messages на плоску структуру перед передаванням або скористайтеся бібліотекою на кшталт 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 →

Поширені запитання