При работе с Intl API часто возникает ситуация, когда
запрошенная локаль не поддерживается текущей средой выполнения. Это
особенно заметно:
Например:
new Intl.DateTimeFormat("fr-CA")
Если окружение не поддерживает fr-CA, движок пытается
подобрать наиболее подходящий вариант автоматически.
Большинство конструкторов Intl используют механизм
автоматического отката локали (locale fallback).
Пример:
const formatter = new Intl.NumberFormat("de-AT")
console.log(formatter.resolvedOptions().locale)
Если de-AT поддерживается:
de-AT
Если нет, возможны варианты:
de
или:
en-US
В зависимости от доступных данных.
Поиск локали происходит сверху вниз.
Для:
zh-Hant-TW
движок проверяет:
zh-Hant-TWzh-HantzhАналогично:
pt-BR
может откатиться к:
pt
Метод supportedLocalesOf() позволяет определить, какие
локали действительно поддерживаются.
console.log(
Intl.DateTimeFormat.supportedLocalesOf(["fr-CA"])
)
Результат:
["fr-CA"]
или:
[]
const locales = [
"ru-RU",
"fr-CA",
"xx-YY",
"de-DE"
]
console.log(
Intl.NumberFormat.supportedLocalesOf(locales)
)
Результат:
[
"ru-RU",
"fr-CA",
"de-DE"
]
Несуществующая локаль xx-YY будет отброшена.
Все основные конструкторы Intl принимают массив локалей
в порядке приоритета.
Пример:
const formatter = new Intl.DateTimeFormat([
"fr-CA",
"fr-FR",
"en-US"
])
Движок выбирает первую доступную локаль.
Такой подход особенно полезен:
Accept-Language.const locales = [
"kk-KZ",
"ru-RU",
"en-US"
]
const formatter = new Intl.NumberFormat(locales)
console.log(
formatter.resolvedOptions().locale
)
Возможные результаты:
kk-KZ
или:
ru-RU
или:
en-US
Метод resolvedOptions() показывает фактически
используемую локаль.
const formatter = new Intl.DateTimeFormat(
["fr-CA", "fr", "en"]
)
console.log(
formatter.resolvedOptions()
)
Результат:
{
locale: "fr",
calendar: "gregory",
numberingSystem: "latn",
timeZone: "UTC"
}
Это особенно важно для:
Опция localeMatcher определяет алгоритм поиска
подходящей локали.
Поддерживаются два значения:
"lookup""best fit"Строгий пошаговый поиск.
const formatter = new Intl.NumberFormat(
["en-GB"],
{
localeMatcher: "lookup"
}
)
Используется RFC-совместимый механизм поиска.
Более гибкий алгоритм.
const formatter = new Intl.NumberFormat(
["en-GB"],
{
localeMatcher: "best fit"
}
)
Движок может использовать внутренние эвристики.
best fit используется по умолчанию.
new Intl.DateTimeFormat(
["en-XX"],
{ localeMatcher: "lookup" }
)
Может вернуть:
en
А best fit иногда подбирает локаль точнее в зависимости
от платформы.
Поведение может отличаться между:
Если ни одна локаль не найдена, используется системная локаль среды выполнения.
const formatter = new Intl.DateTimeFormat(
["xx-YY"]
)
console.log(
formatter.resolvedOptions().locale
)
Например:
en-US
console.log(
Intl.DateTimeFormat().resolvedOptions().locale
)
Пример результата:
ru-RU
Некорректная строка локали вызывает RangeError.
new Intl.NumberFormat("invalid_locale")
Ошибка:
RangeError
function isValidLocale(locale) {
try {
new Intl.NumberFormat(locale)
return true
} catch {
return false
}
}
console.log(
isValidLocale("ru-RU")
)
function isValidLocale(locale) {
try {
new Intl.Locale(locale)
return true
} catch {
return false
}
}
Класс Intl.Locale помогает анализировать и нормализовать
локали.
const locale = new Intl.Locale("fr-ca")
console.log(locale.toString())
Результат:
fr-CA
const locale = new Intl.Locale("EN_us")
console.log(locale.baseName)
Результат:
en-US
Некоторые локали имеют несколько региональных модификаций.
Примеры:
en-US
en-GB
en-AU
Форматирование может отличаться:
const us = new Intl.DateTimeFormat("en-US")
const gb = new Intl.DateTimeFormat("en-GB")
const date = new Date()
console.log(us.format(date))
console.log(gb.format(date))
Если отсутствует региональная версия:
es-MX
движок может использовать:
es
Это позволяет сохранять язык интерфейса даже без точной региональной локали.
Локаль может существовать, но отдельные расширения — нет.
Пример:
new Intl.DateTimeFormat(
"en-US-u-ca-islamic"
)
Если календарь islamic не поддерживается, движок выберет
доступный.
Проверка:
const formatter = new Intl.DateTimeFormat(
"en-US-u-ca-islamic"
)
console.log(
formatter.resolvedOptions()
)
Intl.supportedValuesOf("calendar")
Пример результата:
[
"gregory",
"buddhist",
"islamic"
]
Intl.supportedValuesOf("numberingSystem")
Например:
[
"latn",
"arab",
"thai"
]
Intl.supportedValuesOf("timeZone")
Корректная деградация — важная часть интернационализации.
Пример безопасной конфигурации:
const formatter = new Intl.DateTimeFormat(
[
"kk-KZ",
"ru-RU",
"en-US"
],
{
dateStyle: "long"
}
)
Даже если казахская локаль недоступна, приложение продолжит работать.
Иногда требуется собственная логика выбора локалей.
const preferred = [
"kk-KZ",
"ru-RU",
"en-US"
]
const supported =
Intl.DateTimeFormat.supportedLocalesOf(
preferred
)
const locale =
supported[0] || "en-US"
const formatter =
new Intl.DateTimeFormat(locale)
Частая схема:
const locale =
localStorage.getItem("locale")
Далее:
const available = [
"ru-RU",
"en-US",
"de-DE"
]
const supported =
Intl.DateTimeFormat.supportedLocalesOf([
locale
])
const finalLocale =
supported.length
? supported[0]
: "en-US"
В браузерах можно учитывать языковые предпочтения пользователя.
console.log(navigator.languages)
Пример:
[
"fr-CA",
"fr",
"en-US"
]
Использование:
const formatter =
new Intl.NumberFormat(
navigator.languages
)
В Node.js часто используется заголовок:
Accept-Language
Пример:
fr-CA,fr;q=0.9,en;q=0.8
После парсинга:
[
"fr-CA",
"fr",
"en"
]
Этот массив можно передать напрямую в Intl.
Node.js может поставляться:
Минимальная сборка поддерживает ограниченное число локалей.
Проверка:
console.log(
Intl.DateTimeFormat.supportedLocalesOf([
"ru-RU",
"zh-CN",
"ar-EG"
])
)
Для полной поддержки локалей используется:
node --icu-data-dir=...
или специальные сборки Node.js с Full ICU.
Поддержка локалей зависит от:
Например:
Это важная особенность API.
Ошибка возникает только:
Но отсутствие поддержки локали приводит к fallback, а не к исключению.
const formatter =
new Intl.NumberFormat("abc-XYZ")
Если строка синтаксически допустима, но локаль неизвестна:
console.log(
formatter.resolvedOptions().locale
)
Возможен fallback:
en-US
Intl.getCanonicalLocales() нормализует локали.
console.log(
Intl.getCanonicalLocales([
"EN-us",
"fr-ca"
])
)
Результат:
[
"en-US",
"fr-CA"
]
console.log(
Intl.getCanonicalLocales([
"en-US",
"en-us",
"EN-US"
])
)
Результат:
[
"en-US"
]
Надёжная схема работы с локалями обычно включает:
resolvedOptions().function resolveLocale(locales) {
const canonical =
Intl.getCanonicalLocales(locales)
const supported =
Intl.DateTimeFormat
.supportedLocalesOf(canonical)
return supported[0] || "en-US"
}
const locale = resolveLocale([
"kk-KZ",
"ru-RU",
"en-US"
])
const formatter =
new Intl.DateTimeFormat(locale, {
dateStyle: "full"
})
console.log(
formatter.format(new Date())
)