Совместимость с фреймворками и окружениями

Библиотека FormatJS строится вокруг стандарта ECMAScript Internationalization API (Intl) и предоставляет набор инструментов для интернационализации JavaScript-приложений независимо от используемого окружения. Благодаря модульной архитектуре FormatJS совместим с браузерами, Node.js, React, React Native, серверным рендерингом, статической генерацией и современными сборщиками.

Основные пакеты экосистемы:

Пакет Назначение
react-intl Интеграция с React
intl-messageformat Форматирование ICU-сообщений
@formatjs/intl Polyfill-реализации Intl API
@formatjs/cli Извлечение и компиляция переводов
babel-plugin-formatjs Оптимизация и extraction сообщений
@formatjs/ts-transformer Поддержка TypeScript

FormatJS не привязан к конкретному UI-фреймворку. Большая часть функциональности работает на чистом JavaScript, а React-слой является отдельной надстройкой.


Совместимость с браузерами

Поддержка Intl API

FormatJS активно использует встроенный API Intl. Современные браузеры поддерживают большую часть возможностей:

  • Intl.DateTimeFormat
  • Intl.NumberFormat
  • Intl.RelativeTimeFormat
  • Intl.PluralRules
  • Intl.DisplayNames
  • Intl.ListFormat

Однако степень поддержки зависит от браузера и его версии.

Поддерживаемые браузеры

Браузер Поддержка
Chrome Полная
Firefox Полная
Safari Частичная в старых версиях
Edge Полная
Internet Explorer 11 Требуются polyfill

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

Для старых браузеров необходимо подключать polyfill-пакеты.

Установка

npm install @formatjs/intl-pluralrules
npm install @formatjs/intl-relativetimeformat
npm install @formatjs/intl-numberformat

Подключение

import '@formatjs/intl-pluralrules/polyfill';
import '@formatjs/intl-relativetimeformat/polyfill';
import '@formatjs/intl-numberformat/polyfill';

Для конкретных локалей:

import '@formatjs/intl-pluralrules/locale-data/ru';
import '@formatjs/intl-relativetimeformat/locale-data/ru';

Условная загрузка polyfill

Оптимальным подходом является динамическая загрузка только при отсутствии поддержки.

async function loadPolyfills() {
  if (!Intl.PluralRules) {
    await import('@formatjs/intl-pluralrules/polyfill');
    await import('@formatjs/intl-pluralrules/locale-data/ru');
  }
}

Такой подход уменьшает размер основного bundle.


Совместимость с Node.js

Использование в серверных приложениях

FormatJS полностью поддерживается в Node.js.

Пример:

import {IntlMessageFormat} from 'intl-messageformat';

const message = new IntlMessageFormat(
  'Привет, {name}',
  'ru'
);

console.log(
  message.format({name: 'Алексей'})
);

ICU в Node.js

Node.js использует ICU (International Components for Unicode) для работы Intl.

Существует два режима:

Режим Описание
small-icu Только английская локаль
full-icu Полная поддержка локалей

Проверка поддержки локалей

console.log(
  Intl.DateTimeFormat.supportedLocalesOf(['ru'])
);

Если результат пустой — ICU отсутствует.


Подключение full-icu

Установка

npm install full-icu

Запуск

NODE_ICU_DATA=node_modules/full-icu node app.js

Совместимость с React

React Intl

Основной пакет для React — react-intl.

Установка

npm install react-intl

IntlProvider

Базовый провайдер локализации:

import {IntlProvider} from 'react-intl';

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

Использование хуков

useIntl

import {useIntl} from 'react-intl';

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

  return (
    <span>
      {intl.formatNumber(1500, {
        style: 'currency',
        currency: 'RUB'
      })}
    </span>
  );
}

Поддержка React 18

FormatJS совместим с React 18:

  • Concurrent Rendering
  • Suspense
  • Streaming SSR
  • Server Components

StrictMode

react-intl корректно работает в StrictMode.

<React.StrictMode>
  <App />
</React.StrictMode>

Совместимость с Next.js

Интеграция с SSR

FormatJS хорошо подходит для серверного рендеринга.

Пример _app.js

import {IntlProvider} from 'react-intl';

export default function App({Component, pageProps}) {
  return (
    <IntlProvider
      locale={pageProps.locale}
      messages={pageProps.messages}
    >
      <Component {...pageProps} />
    </IntlProvider>
  );
}

Загрузка переводов

export async function getStaticProps({locale}) {
  const messages = (
    await import(`../lang/${locale}.json`)
  ).default;

  return {
    props: {
      locale,
      messages
    }
  };
}

App Router

В Next.js App Router FormatJS может использоваться внутри client-компонентов.

'use client';

import {IntlProvider} from 'react-intl';

Проблемы hydration

Hydration mismatch возникает при различии локалей сервера и клиента.

Неправильно:

locale={navigator.language}

Правильно:

locale={serverLocale}

Совместимость с Remix

Remix поддерживает серверный рендеринг и потоковую передачу данных, поэтому FormatJS интегрируется без дополнительных адаптеров.

Пример loader

export async function loader() {
  return json({
    locale: 'ru',
    messages
  });
}

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

<IntlProvider
  locale={data.locale}
  messages={data.messages}
>
  <Outlet />
</IntlProvider>

Совместимость с Gatsby

Статическая генерация

FormatJS подходит для SSG.

Инициализация

export const wrapRootElement = ({element}) => (
  <IntlProvider
    locale="ru"
    messages={messages}
  >
    {element}
  </IntlProvider>
);

Многоязычные маршруты

Обычно используются:

  • /ru/
  • /en/
  • /de/

Генерация страниц выполняется через createPages.


Совместимость с Vue

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

FormatJS можно использовать независимо от React.

Пример

import {IntlMessageFormat} from 'intl-messageformat';

const msg = new IntlMessageFormat(
  'Товаров: {count}',
  'ru'
);

msg.format({count: 5});

Интеграция с Vue

Обычно FormatJS применяется как слой ICU-форматирования внутри vue-i18n.

import {IntlMessageFormat} from 'intl-messageformat';

Совместимость с Angular

Angular имеет собственную i18n-систему, однако FormatJS используется:

  • для ICU-форматирования;
  • динамических переводов;
  • runtime-интернационализации.

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

import {IntlMessageFormat} from 'intl-messageformat';

@Injectable()
export class I18nService {
  format(message, values) {
    return new IntlMessageFormat(
      message,
      'ru'
    ).format(values);
  }
}

Совместимость с Svelte

FormatJS интегрируется через обычные JavaScript-модули.

Пример store

import {writable} from 'svelte/store';

export const locale = writable('ru');

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

import {IntlMessageFormat} from 'intl-messageformat';

Совместимость с React Native

Особенности среды

React Native не всегда содержит полноценный Intl.

Особенно это касается:

  • Android Hermes
  • старых Android WebView
  • некоторых iOS runtime

Подключение polyfill

npm install @formatjs/intl-getcanonicallocales
npm install @formatjs/intl-locale
npm install @formatjs/intl-pluralrules

Инициализация

import '@formatjs/intl-getcanonicallocales/polyfill';
import '@formatjs/intl-locale/polyfill';
import '@formatjs/intl-pluralrules/polyfill';

Hermes

Hermes частично поддерживает Intl, но возможности зависят от версии React Native.

Для старых версий часто требуется полный набор polyfill.


Совместимость с Electron

Electron использует Chromium и Node.js одновременно, поэтому FormatJS работает практически без ограничений.


Renderer process

import {IntlProvider} from 'react-intl';

Main process

import {IntlMessageFormat} from 'intl-messageformat';

Совместимость с TypeScript

Типизация сообщений

FormatJS поддерживает TypeScript на уровне API.

Пример

import {IntlShape} from 'react-intl';

function formatPrice(intl: IntlShape) {
  return intl.formatNumber(1000);
}

Type-safe messages

type MessageIds =
  | 'app.title'
  | 'menu.home';

ts-transformer

FormatJS предоставляет TypeScript transformer.

Установка

npm install @formatjs/ts-transformer

Совместимость со сборщиками

Webpack

FormatJS полностью совместим с Webpack.


Babel Plugin

npm install babel-plugin-formatjs

Конфигурация

{
  plugins: [
    ['formatjs', {
      idInterpolationPattern:
        '[sha512:contenthash:base64:6]'
    }]
  ]
}

Vite

Vite поддерживает FormatJS без специальных адаптеров.

Пример

import {defineConfig} from 'vite';

export default defineConfig({
  plugins: []
});

ESBuild

ESBuild поддерживает FormatJS через Babel-плагины или precompile.


Rollup

Rollup работает через стандартную Babel-интеграцию.


Совместимость с SSR

Серверное форматирование

FormatJS безопасен для SSR.

intl.formatDate(new Date());

Изоляция запросов

Нельзя использовать глобальный singleton.

Неправильно:

const intl = createIntl(...);

Правильно:

function handleRequest(req) {
  const intl = createIntl(...);
}

Кэширование

const cache = createIntlCache();

Совместимость с Edge Runtime

Современные edge-среды:

  • Vercel Edge Functions
  • Cloudflare Workers
  • Deno Deploy

поддерживают большую часть Intl.


Ограничения

Некоторые edge-runtime не содержат:

  • полный ICU;
  • редкие локали;
  • старые Intl API.

Проверка возможностей

if (Intl.RelativeTimeFormat) {
  // supported
}

Совместимость с Deno

FormatJS может работать в Deno благодаря поддержке ES Modules.

Пример

import {IntlMessageFormat}
  from 'npm:intl-messageformat';

Совместимость с Bun

Bun поддерживает большую часть Node.js API и Intl.

FormatJS работает без изменений в коде.


Совместимость с ESM и CommonJS

ES Modules

import {IntlMessageFormat}
  from 'intl-messageformat';

CommonJS

const {
  IntlMessageFormat
} = require('intl-messageformat');

Совместимость с CDN

Использование через UNPKG

<script src="
https://unpkg.com/react-intl
"></script>

Использование через jsDelivr

<script src="
https://cdn.jsdelivr.net/npm/react-intl
"></script>

Совместимость с ICU MessageFormat

FormatJS реализует ICU Message syntax.

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

  • plural;
  • select;
  • selectordinal;
  • nested formatting.

Пример plural

'{count, plural,
  one {# товар}
  few {# товара}
  many {# товаров}
}'

Пример select

'{gender, select,
  male {Он}
  female {Она}
  other {Они}
}'

Совместимость локалей

CLDR

FormatJS использует данные Unicode CLDR.


Поддержка языков

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

  • русский;
  • английский;
  • китайский;
  • арабский;
  • японский;
  • немецкий;
  • французский;
  • десятки других локалей.

Совместимость с динамическими импортами

Lazy loading переводов

const messages = await import(
  `./lang/${locale}.json`
);

Code splitting

Каждая локаль может находиться в отдельном чанке.


Совместимость с monorepo

FormatJS хорошо работает в:

  • Nx;
  • Turborepo;
  • Lerna;
  • Yarn Workspaces.

Централизация переводов

Обычно создаётся отдельный пакет:

packages/i18n

Общие сообщения

export const messages = {
  'app.title': 'Приложение'
};

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

Jest

FormatJS поддерживается в Jest.

Mock Intl

global.Intl = Intl;

Testing Library

render(
  <IntlProvider locale="ru">
    <Component />
  </IntlProvider>
);

Snapshot testing

Локаль должна быть фиксированной:

locale="en"

Иначе snapshots будут различаться.


Совместимость с Cypress

E2E-тестирование

cy.visit('/ru');

Проверка локализации

cy.contains('Главная');

Совместимость с Storybook

Декоратор

export const decorators = [
  (Story) => (
    <IntlProvider locale="ru">
      <Story />
    </IntlProvider>
  )
];

Совместимость с Microfrontend

Изоляция локалей

Каждый microfrontend может иметь собственный IntlProvider.


Общий контекст

Альтернативный вариант — единый провайдер в shell-приложении.


Совместимость с PWA

FormatJS полностью совместим с:

  • Service Worker;
  • offline cache;
  • lazy translations;
  • background sync.

Совместимость с Web Components

FormatJS может использоваться внутри custom elements.

class MyElement extends HTMLElement {
  connectedCallback() {
    const formatter =
      new Intl.NumberFormat('ru');
  }
}

Ограничения совместимости

Основные проблемы

Наиболее частые источники ошибок:

Проблема Причина
Missing locale data Не подключён locale-data
Hydration mismatch Разные локали
Unsupported Intl API Старый браузер
ICU parsing error Некорректный ICU-синтаксис
Different timezone Сервер и клиент используют разные TZ

Проверка окружения

console.log(Intl);
console.log(Intl.PluralRules);
console.log(Intl.RelativeTimeFormat);

Практические рекомендации

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

if (!Intl.ListFormat) {
  await import(
    '@formatjs/intl-listformat/polyfill'
  );
}

Разделение locale-data

import(
  `@formatjs/intl-pluralrules/locale-data/${locale}`
);

Единая локаль SSR/CSR

Локаль должна определяться на сервере и передаваться клиенту.


Предкомпиляция сообщений

Компиляция ICU-сообщений во время build уменьшает runtime-издержки.

formatjs compile

Минимизация bundle size

Основные способы:

  • динамический импорт переводов;
  • selective polyfills;
  • tree shaking;
  • precompile ICU messages;
  • code splitting по локалям.