BCP 47 (Best Current Practice 47) — стандарт описания языковых тегов, используемый для локализации, интернационализации и настройки региональных параметров. В экосистеме JavaScript этот формат лежит в основе работы Intl API: форматирования дат, чисел, валют, единиц измерения, сортировки строк, правил множественного числа и других возможностей.
Большинство методов Intl принимают строку локали именно в формате BCP 47:
new Intl.DateTimeFormat('en-US')
new Intl.NumberFormat('fr-FR')
new Intl.Collator('de-DE')
BCP 47 стандартизирует запись языков, стран, письменностей и дополнительных параметров. Благодаря этому браузеры, операционные системы и серверные среды интерпретируют локали одинаковым образом.
Тег BCP 47 состоит из последовательности подзаголовков (subtags), разделённых дефисом:
language-script-region-variant-extension
Пример:
zh-Hant-TW
Разбор:
| Часть | Значение |
|---|---|
zh |
китайский язык |
Hant |
традиционная письменность |
TW |
Тайвань |
Язык — обязательная часть тега.
'en'
'ru'
'fr'
'ja'
'ar'
Некоторые распространённые коды:
| Код | Язык |
|---|---|
en |
английский |
ru |
русский |
de |
немецкий |
es |
испанский |
it |
итальянский |
zh |
китайский |
ja |
японский |
Intl использует стандарты ISO 639.
Регион уточняет локальные особенности языка.
'en-US'
'en-GB'
'pt-BR'
'fr-CA'
Примеры различий:
new Intl.DateTimeFormat('en-US').format(new Date())
12/31/2025
new Intl.DateTimeFormat('en-GB').format(new Date())
31/12/2025
new Intl.NumberFormat('en-US').format(1234567.89)
1,234,567.89
new Intl.NumberFormat('en-GB').format(1234567.89)
1,234,567.89
Формат чисел одинаковый, но различаются:
new Intl.NumberFormat('pt-PT').format(1234567.89)
1 234 567,89
new Intl.NumberFormat('pt-BR').format(1234567.89)
1.234.567,89
Некоторые языки используют несколько систем письма.
Пример:
sr-Cyrl
sr-Latn
| Тег | Значение |
|---|---|
sr-Cyrl |
сербский, кириллица |
sr-Latn |
сербский, латиница |
Пример:
new Intl.DisplayNames(['sr-Cyrl'], { type: 'language' })
Китайский особенно часто использует подзаголовок script.
zh-Hans
zh-Hant
| Тег | Значение |
|---|---|
zh-Hans |
упрощённый китайский |
zh-Hant |
традиционный китайский |
Регион может дополнительно уточнять локаль:
zh-Hans-CN
zh-Hant-TW
zh-Hant-HK
BCP 47 поддерживает специальные варианты.
Пример:
de-DE-1996
Обозначает немецкий язык с орфографической реформой 1996 года.
Другой пример:
sl-rozaj
Диалект резьянского языка.
В JavaScript варианты используются редко, но стандарт их поддерживает полностью.
BCP 47 позволяет добавлять расширения.
Наиболее важное для Intl API — Unicode extension
(-u-).
Пример:
en-US-u-ca-buddhist
Разбор:
| Часть | Значение |
|---|---|
en-US |
английский США |
u |
Unicode extension |
ca-buddhist |
буддийский календарь |
Unicode extensions позволяют управлять поведением Intl без передачи дополнительных опций.
new Intl.DateTimeFormat(
'en-US-u-ca-islamic'
).format(new Date())
Используется исламский календарь.
Другие варианты:
| Значение | Календарь |
|---|---|
gregory |
григорианский |
buddhist |
буддийский |
japanese |
японский |
islamic |
исламский |
new Intl.NumberFormat(
'ar-EG-u-nu-arab'
).format(123456)
Результат:
١٢٣٤٥٦
Популярные варианты:
| Значение | Система |
|---|---|
latn |
латинская |
arab |
арабская |
thai |
тайская |
hanidec |
китайская |
en-US-u-hc-h24
| Значение | Формат |
|---|---|
h12 |
12-часовой |
h23 |
23-часовой |
h24 |
24-часовой |
de-DE-u-co-phonebk
Специальная немецкая телефонная сортировка.
Intl API умеет принимать массив языков.
const locales = ['fr-CA', 'fr', 'en']
new Intl.DateTimeFormat(locales)
Механизм:
fr-CA;fr;en.Это называется locale negotiation.
Метод supportedLocalesOf позволяет узнать, какие локали
поддерживаются средой выполнения.
Intl.DateTimeFormat.supportedLocalesOf([
'ru-RU',
'fr-FR',
'xx-YY'
])
Результат:
['ru-RU', 'fr-FR']
Intl автоматически нормализует некоторые локали.
Пример:
Intl.getCanonicalLocales('EN-us')
Результат:
['en-US']
Приведение включает:
Формально регистр не важен:
en-us
EN-US
eN-uS
Все варианты валидны.
Однако существует соглашение:
| Часть | Регистр |
|---|---|
| язык | нижний |
| script | Capitalized |
| регион | верхний |
Правильная запись:
sr-Latn-RS
Некоторые строки не соответствуют BCP 47.
Пример:
new Intl.DateTimeFormat('english')
В большинстве движков:
RangeError
Правильный вариант:
new Intl.DateTimeFormat('en')
Допустимо указывать только язык:
new Intl.DateTimeFormat('ru')
Система самостоятельно подберёт регион по умолчанию.
Если локаль отсутствует:
new Intl.DateTimeFormat(undefined)
используется системная локаль среды выполнения.
В браузере — язык пользователя.
В Node.js — настройки ОС или окружения.
Intl использует два алгоритма сопоставления локалей:
| Значение | Описание |
|---|---|
lookup |
строгое сопоставление |
best fit |
интеллектуальный подбор |
Пример:
new Intl.DateTimeFormat('en-XX', {
localeMatcher: 'lookup'
})
Современный JavaScript предоставляет специальный класс для работы с BCP 47.
const locale = new Intl.Locale('ru-Cyrl-RU')
locale.language
ru
locale.script
Cyrl
locale.region
RU
Метод maximize() добавляет недостающие части.
new Intl.Locale('ru').maximize()
Результат:
ru-Cyrl-RU
new Intl.Locale('ru-Cyrl-RU').minimize()
Результат:
ru
const locale = new Intl.Locale(
'en-US-u-ca-buddhist'
)
locale.calendar
Результат:
buddhist
const locale = new Intl.Locale(
'ar-EG-u-nu-arab'
)
locale.numberingSystem
new Intl.NumberFormat(
'fr-FR'
).format(1234567.89)
Результат:
1 234 567,89
Формат определяется именно локалью BCP 47.
new Intl.DateTimeFormat(
'ja-JP-u-ca-japanese'
).format(new Date())
Дата будет отображаться с японской эрой.
new Intl.RelativeTimeFormat('ru')
Правила склонения зависят от языка локали.
const collator = new Intl.Collator('sv-SE')
Шведская сортировка отличается от английской.
Например, буква ä сортируется иначе.
API умеет отображать названия языков и регионов.
const dn = new Intl.DisplayNames(
['ru'],
{ type: 'region' }
)
dn.of('US')
Результат:
США
| Локаль | Описание |
|---|---|
en-US |
английский, США |
en-GB |
английский, Великобритания |
ru-RU |
русский, Россия |
fr-FR |
французский, Франция |
de-DE |
немецкий, Германия |
ja-JP |
японский, Япония |
zh-CN |
китайский, Китай |
zh-TW |
китайский, Тайвань |
ar-SA |
арабский, Саудовская Аравия |
Неправильно:
en_US
Правильно:
en-US
Неправильно:
russian
english
Правильно:
ru
en
Неправильно:
sr-latn-rs
Правильно:
sr-Latn-RS
Некоторые старые коды автоматически заменяются.
| Старый | Новый |
|---|---|
iw |
he |
in |
id |
ji |
yi |
Пример:
Intl.getCanonicalLocales('iw')
Результат:
['he']
BCP 47 поддерживает пользовательские расширения.
Пример:
en-US-x-company
Часть после x- интерпретируется приложением
самостоятельно.
Intl обычно игнорирует такие расширения.
Некоторые исторические теги сохранены ради совместимости.
Пример:
i-klingon
Современный аналог:
tlh
Разбор локалей через регулярные выражения крайне нежелателен.
Причины:
Предпочтительно использовать:
Intl.Locale
new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(1000)
new Intl.DateTimeFormat('ru-RU', {
dateStyle: 'full'
}).format(new Date())
navigator.languages
Возвращает массив BCP 47 локалей браузера пользователя.
Пример:
[
'ru-RU',
'en-US'
]
BCP 47 основан на нескольких RFC:
| Документ | Назначение |
|---|---|
| RFC 5646 | структура языковых тегов |
| RFC 4647 | сопоставление локалей |
Intl API опирается на:
| Технология | Назначение |
|---|---|
| ICU | библиотека интернационализации |
| CLDR | база локализационных данных Unicode |
Именно они определяют:
Intl.getCanonicalLocales([
'EN-us',
'fr-fr'
])
const formatter =
new Intl.DateTimeFormat('ru')
formatter.resolvedOptions()
Результат:
{
locale: 'ru-RU',
calendar: 'gregory',
numberingSystem: 'latn',
timeZone: 'Europe/Moscow'
}
BCP 47 поддерживается:
Большинство возможностей Intl API требуют полноценной ICU-поддержки среды выполнения.