Локали и языковые теги

Локаль определяет язык, региональные настройки и правила форматирования данных. В экосистеме FormatJS локаль влияет на:

  • язык интерфейса;
  • формат даты и времени;
  • представление чисел;
  • отображение валют;
  • правила множественного числа;
  • сортировку строк;
  • направление текста.

FormatJS опирается на международные стандарты ECMAScript Internationalization API (Intl) и использует языковые теги BCP 47.

Пример локали:

'en'
'ru'
'ru-RU'
'en-US'
'kk-KZ'
'zh-Hans-CN'

Структура языкового тега BCP 47

Языковой тег состоит из нескольких частей.

Общая структура:

language-script-region-variant

Пример:

zh-Hans-CN

Разбор:

Часть Значение
zh китайский язык
Hans упрощённая письменность
CN Китай

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

Код языка

Язык — обязательная часть локали.

Примеры:

Код Язык
en английский
ru русский
de немецкий
fr французский
kk казахский

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

<IntlProvider locale="ru">

Код региона

Регион уточняет особенности языка.

Например:

en-US
en-GB
pt-BR
pt-PT

Хотя язык одинаковый, различаются:

  • формат дат;
  • валюта;
  • орфография;
  • единицы измерения.

Пример:

new Intl.NumberFormat('en-US').format(1000000)
// 1,000,000

new Intl.NumberFormat('de-DE').format(1000000)
// 1.000.000

Скрипт (письменность)

Некоторые языки имеют несколько систем письма.

Примеры:

zh-Hans
zh-Hant
sr-Cyrl
sr-Latn
Тег Значение
zh-Hans китайский, упрощённый
zh-Hant китайский, традиционный
sr-Cyrl сербский, кириллица
sr-Latn сербский, латиница

Варианты

Дополнительные модификаторы локали.

Пример:

de-DE-1996

Обозначает немецкий язык с орфографической реформой 1996 года.

В веб-разработке используется редко.


Использование локалей в IntlProvider

Главный компонент FormatJS — IntlProvider.

Пример:

import { IntlProvider } from 'react-intl'

<IntlProvider locale="ru">
  <App />
</IntlProvider>

Локаль автоматически используется всеми компонентами:

  • FormattedMessage
  • FormattedDate
  • FormattedNumber
  • FormattedRelativeTime
  • FormattedPlural

Выбор локали приложения

Чаще всего локаль определяется:

  • из настроек браузера;
  • из URL;
  • из профиля пользователя;
  • из cookies;
  • из HTTP-заголовка Accept-Language.

Получение локали браузера

Пример:

const locale = navigator.language

console.log(locale)
// ru-RU

Несколько предпочтительных языков:

console.log(navigator.languages)

Результат:

['ru-RU', 'en-US', 'en']

Автоматический fallback локали

Если перевод отсутствует, FormatJS может использовать запасной язык.

Пример:

<IntlProvider
  locale="kk"
  defaultLocale="en"
  messages={messages}
>
  <App />
</IntlProvider>

Здесь:

  • locale="kk" — активная локаль;
  • defaultLocale="en" — резервная локаль.

Иерархия fallback

Многие системы локализации используют постепенное упрощение локали.

Пример:

ru-KZ
↓
ru
↓
en

Алгоритм:

  1. поиск ru-KZ;
  2. если отсутствует — поиск ru;
  3. затем глобальный fallback.

Локали и сообщения

Сообщения обычно организуются по языковым тегам.

Структура проекта:

locales/
  en.json
  ru.json
  kk.json

Пример:

{
  "app.title": "Главная"
}

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

import ru from './locales/ru.json'

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

Региональные различия переводов

Даже внутри одного языка возможны отличия.

Пример английского:

Локаль Слово
en-US color
en-GB colour

Пример:

en-US.json
en-GB.json

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

FormatJS использует Intl.NumberFormat.

Пример:

<FormattedNumber value={1234567.89} />

Результат зависит от локали.

en-US

1,234,567.89

ru-RU

1 234 567,89

de-DE

1.234.567,89

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

Валюта зависит не только от языка, но и от региона.

Пример:

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

en-US

$1,000.00

fr-FR

1 000,00 $US

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

Разные страны используют разные правила записи дат.

Пример:

<FormattedDate
  value={new Date()}
  year="numeric"
  month="long"
  day="numeric"
/>

Различия форматов дат

en-US

May 28, 2026

ru-RU

28 мая 2026 г.

de-DE

28. Mai 2026

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

Формат времени также зависит от локали.

en-US

12-часовой формат:

3:45 PM

ru-RU

24-часовой формат:

15:45

Правила множественного числа

Разные языки имеют разные формы множественного числа.

Русский язык:

Число Форма
1 товар
2 товара
5 товаров

Английский:

Число Форма
1 item
остальные items

FormatJS использует правила Intl.PluralRules.


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

Пример:

<FormattedPlural
  value={count}
  one="товар"
  few="товара"
  many="товаров"
  other="товара"
/>

RTL и LTR локали

Некоторые языки пишутся справа налево.

RTL-языки:

  • арабский;
  • иврит;
  • персидский.

Примеры локалей:

ar
he
fa

Для RTL необходимо:

  • менять направление интерфейса;
  • учитывать зеркальное расположение элементов;
  • корректировать иконки и отступы.

Определение направления текста

Пример:

const isRTL = ['ar', 'he', 'fa'].includes(locale)

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

<html dir={isRTL ? 'rtl' : 'ltr'}>

Нормализация локалей

Пользовательские локали могут быть записаны по-разному.

Примеры:

ru-ru
RU_ru
en_us

Корректный формат:

ru-RU
en-US

Intl.Locale

Современный API ECMAScript предоставляет объект Intl.Locale.

Пример:

const locale = new Intl.Locale('ru-RU')

console.log(locale.language)
console.log(locale.region)

Результат:

ru
RU

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

Некоторые окружения не поддерживают все локали.

Проверка:

Intl.NumberFormat.supportedLocalesOf([
  'ru',
  'kk',
  'fr'
])

Canonicalization локалей

Приведение локали к стандартному виду.

Пример:

Intl.getCanonicalLocales('EN_us')

Результат:

['en-US']

Unicode extension tags

BCP 47 поддерживает расширения.

Пример:

en-US-u-ca-buddhist

Разбор:

Часть Значение
u Unicode extension
ca calendar
buddhist буддийский календарь

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

Пример:

new Intl.DateTimeFormat(
  'th-TH-u-ca-buddhist'
).format(new Date())

Нумерационные системы

Пример:

ar-EG-u-nu-arab

Использование арабских цифр.


Locale matching

При выборе локали браузер ищет наиболее подходящий вариант.

Например:

Запрошено: en-AU
Доступно:
- en-US
- en-GB

Будет выбран ближайший вариант.


Lookup matcher и Best fit matcher

Intl использует два режима сопоставления локалей.

lookup

Строгое сопоставление.

best fit

Более гибкий поиск.

Пример:

new Intl.DateTimeFormat(
  ['en-AU'],
  { localeMatcher: 'lookup' }
)

Локали в Node.js

Поддержка локалей зависит от сборки Node.js.

Проверка:

process.versions.icu

Full ICU

Для полноценной интернационализации требуется Full ICU.

Проверка:

Intl.DateTimeFormat.supportedLocalesOf([
  'ru',
  'zh',
  'ar'
])

Локали и polyfill

Старые браузеры могут не поддерживать:

  • Intl.RelativeTimeFormat;
  • Intl.DisplayNames;
  • Intl.ListFormat;
  • Intl.PluralRules.

FormatJS предоставляет polyfill-пакеты.

Пример:

npm install @formatjs/intl-pluralrules

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

Пример:

import '@formatjs/intl-pluralrules/polyfill'
import '@formatjs/intl-pluralrules/locale-data/ru'

Dynamic locale loading

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

Пример:

async function loadLocale(locale) {
  const messages = await import(
    `./locales/${locale}.json`
  )

  return messages.default
}

Переключение языка

Пример:

const [locale, setLocale] = useState('ru')

Переключение:

setLocale('en')

Хранение локали

Популярные способы хранения:

  • localStorage;
  • cookies;
  • серверный профиль;
  • URL-параметры.

Локаль в URL

Пример:

/ru/products
/en/products
/kk/products

Преимущества:

  • SEO;
  • возможность поделиться ссылкой;
  • предсказуемая маршрутизация.

Accept-Language

Браузер отправляет список предпочитаемых языков.

Пример заголовка:

Accept-Language: ru-RU,ru;q=0.9,en;q=0.8

Парсинг Accept-Language

Упрощённый пример:

const header =
  'ru-RU,ru;q=0.9,en;q=0.8'

const locales = header
  .split(',')
  .map(item => item.split(';')[0])

Ошибки при работе с локалями

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

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

english
russian
ru_RU

Правильно:

en
ru
ru-RU

Смешивание языка и страны

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

RU
US
KZ

Это коды стран, а не языков.


Жёстко заданные строки

Плохо:

const date = '05/12/2026'

Правильно:

new Intl.DateTimeFormat(locale)

Рекомендации по организации локалей

Использование ISO-стандартов

  • язык — ISO 639;
  • страна — ISO 3166.

Единообразие именования

Лучше:

en-US
ru-RU
kk-KZ

Централизованное хранение

Пример:

export const SUPPORTED_LOCALES = [
  'en',
  'ru',
  'kk'
]

Локаль по умолчанию

Всегда должна существовать базовая локаль.

Пример:

const DEFAULT_LOCALE = 'en'

Тестирование локалей

Необходимо проверять:

  • даты;
  • валюты;
  • множественное число;
  • RTL;
  • переполнение текста;
  • переносы строк.

Особенности казахской локали

Локаль:

kk-KZ

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

  • кириллица;
  • формат дат Казахстана;
  • тенге (KZT);
  • локальные правила форматирования.

Пример валюты:

new Intl.NumberFormat('kk-KZ', {
  style: 'currency',
  currency: 'KZT'
})

Intl.DisplayNames

API для локализованных названий языков и стран.

Пример:

const languageNames =
  new Intl.DisplayNames(['ru'], {
    type: 'language'
  })

console.log(languageNames.of('en'))

Результат:

английский

Intl.ListFormat

Локализованное объединение списков.

Пример:

new Intl.ListFormat('ru').format([
  'JavaScript',
  'TypeScript',
  'Rust'
])

Результат:

JavaScript, TypeScript и Rust

Intl.RelativeTimeFormat

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

Пример:

new Intl.RelativeTimeFormat('ru').format(
  -1,
  'day'
)

Результат:

1 день назад

Влияние локали на UX

Корректная локализация:

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

Неправильная локализация приводит к:

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