Локализация в React: инженерный подход

#react#i18n#localization#frontend

TL;DR: Современные инструменты локализации для React (i18next, react-intl, Lingui) решают базовые задачи, но для enterprise-решений требуют кастомизации. Рассмотрим архитектурные паттерны и инструменты для сложных кейсов.

Введение: почему локализация — это инженерная задача

Локализация (l10n) и интернационализация (i18n) в крупных проектах — это не просто подстановка строк. Это:

  1. Динамические плейсхолдеры (например, {count} items)
  2. Плюрализация (разные формы для разных чисел)
  3. Гендерные формы
  4. Контекстные переводы (одно слово в разных значениях)
  5. Ленивая загрузка локалей

Пример боли из реального проекта:

// Проблема: разная структура предложений в языках
`Hello ${name}, you have ${count} new messages`
// В японском порядок будет обратным

Основные инструменты и их кастомизация

1. react-i18next: промышленный стандарт

Базовый пример:

import { useTranslation } from 'react-i18next';

function Component() {
  const { t } = useTranslation();
  return <h1>{t('welcome_message', { name: 'John' })}</h1>;
}

Продвинутые фичи:

2. react-intl: форматирование по стандартам

Сильные стороны:

Пример:

<FormattedMessage
  id="welcome"
  defaultMessage="Hello {name}"
  values={{ name: 'John' }}
/>

3. Lingui: компромиссный вариант

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

import { Trans } from '@lingui/macro';

<Trans>Hello {name}</Trans>

Архитектурные решения для сложных кейсов

Динамическая загрузка переводов

Оптимальная стратегия:

// webpackChunkName для анализа бандлов
import(`src/locales/${language}.json` /* webpackChunkName: "locale-[request]" */)

Интеграция с TMS (Translation Management System)

Пример конфига для Crowdin:

module.exports = {
  projectId: process.env.CROWDIN_PROJECT_ID,
  token: process.env.CROWDIN_TOKEN,
  files: [
    {
      source: 'src/locales/en.json',
      translation: 'src/locales/%locale%.json'
    }
  ]
}

Валидация переводов

Скрипт для проверки:

const validateTranslations = (baseLang, otherLangs) => {
  const baseKeys = Object.keys(baseLang);
  return otherLangs.every(lang => {
    const langKeys = Object.keys(lang);
    return baseKeys.every(key => langKeys.includes(key));
  });
};

Перфоманс-оптимизации

  1. Кеширование переводов в IndexedDB
  2. Tree-shaking локалей:
// config/i18n.js
const supportedLocales = ['en', 'es', 'fr'];
export const isLocaleSupported = locale => supportedLocales.includes(locale);
  1. SSR-поддержка:
// server.js
import { I18nextProvider } from 'react-i18next';
import { renderToString } from 'react-dom/server';

const lng = req.acceptsLanguages(supportedLocales) || 'en';
const i18n = i18next.createInstance().use(Backend).init({ lng });

const html = renderToString(
  <I18nextProvider i18n={i18n}>
    <App />
  </I18nextProvider>
);

Заключение: checklist для продакшн-решения

  1. Поддержка динамической загрузки локалей
  2. Интеграция с CI/CD для синхронизации переводов
  3. Валидация ключей на этапе сборки
  4. Оптимизированные бандлы по языкам
  5. SSR-совместимость
  6. Инструменты для переводчиков (TMS, контекст)

Для enterprise-проектов рекомендую связку: i18next + ICU + Crowdin/Phrase + кастомный babel-plugin для статического анализа.


Источник: https://www.reddit.com/r/reactjs/comments/1tc9otb/suggestions_on_localization_engineering/