Работа с локалями в JavaScript строится поверх стандарта BCP 47,
который определяет структуру языковых тегов: язык, регион, скрипт,
варианты, расширения и приватные подстановки. Валидация локалей в
контексте Intl заключается не только в проверке формальной
корректности строки, но и в приведении её к каноническому виду,
сопоставлении с поддерживаемыми локалями окружения и обработке
неоднозначных или частично корректных значений.
Локаль в BCP 47 представляется последовательностью субтегов, разделённых дефисом:
en, ru,
zh)Latn, Cyrl,
Hans)US, GB, CN)posix, fonipa)u-ca-buddhist, nu-arab)x-whatever)Примеры корректных локалей:
enen-USru-RUzh-Hans-CNsr-Cyrl-RSПримеры некорректных или проблемных значений:
en__US (некорректный формат)english-US (невалидный языковой код)ru-RU-123 (некорректный вариант)Важно различать формальную валидность и семантическую поддержку: строка может быть корректной по BCP 47, но не поддерживаться окружением выполнения.
Одной из ключевых операций является приведение локалей к каноническому виду. В JavaScript это выполняется через:
Intl.getCanonicalLocales()Этот метод:
en-us →
en-US)Пример поведения:
Intl.getCanonicalLocales("en-us")
// ["en-US"]
Intl.getCanonicalLocales(["ru-ru", "ru-RU", "en"])
// ["ru-RU", "en"]
При некорректном значении:
Intl.getCanonicalLocales("invalid-locale")
// RangeError
Каноникализация не означает проверку поддержки. Она гарантирует структурную корректность и унификацию представления.
Второй уровень валидации связан с тем, поддерживается ли локаль конкретной реализацией движка. Для этого используется:
Intl.NumberFormat.supportedLocalesOf()Intl.DateTimeFormat.supportedLocalesOf()Intl.Collator.supportedLocalesOf()Общий принцип одинаков: переданный список фильтруется, оставляя только поддерживаемые локали.
Intl.NumberFormat.supportedLocalesOf(["en-US", "xx-YY"])
// ["en-US"]
Здесь важно разделять:
Локаль может быть канонически корректной, но не поддерживаться форматтером.
При создании объектов Intl происходит сопоставление
локалей:
new Intl.DateTimeFormat(["fr-CA", "fr-FR"])
Алгоритм выбирает наиболее подходящую локаль из списка запросов и списка доступных локалей окружения.
Существуют два основных режима:
Валидация в этом контексте включает:
Современный API предоставляет объект:
Intl.LocaleОн позволяет выполнять более строгую проверку структуры локали и разбирать её на компоненты.
const loc = new Intl.Locale("en-US-u-ca-buddhist");
Если строка некорректна, выбрасывается RangeError.
Intl.Locale обеспечивает:
-u-)-x-)Пример извлечения компонентов:
const loc = new Intl.Locale("zh-Hans-CN");
loc.language; // "zh"
loc.script; // "Hans"
loc.region; // "CN"
Это превращает строковую валидацию в структурную модель данных.
BCP 47 требует определённого регистра:
en, ru)Latn, Cyrl)US, GB)Валидация через Intl автоматически приводит значения к
корректной форме:
Intl.getCanonicalLocales("eN-uS")
// ["en-US"]
Эта нормализация устраняет ошибки, связанные с пользовательским вводом или внешними источниками данных.
Некорректные локали классифицируются по типам:
Результат: RangeError при использовании
Intl.Locale или getCanonicalLocales.
Некоторые движки допускают такие значения как расширенные теги, но они не гарантируют поддержки.
В таких случаях часть тега может быть проигнорирована или нормализована.
При работе с массивами локалей важной частью валидации является дедупликация:
Intl.getCanonicalLocales(["en-US", "en-us", "en-US"])
Результат:
["en-US"]
Это критично для:
Accept-LanguageВалидация локали всегда связана с механизмом запасного выбора:
Пример логики:
Если итоговый список пуст, используется локаль окружения (например,
en-US в браузерах на английской локали).
BCP 47 допускает расширения через -u-, которые влияют на
поведение форматирования:
ca)nu)tz)Пример:
"ja-JP-u-ca-japanese"
Валидация расширений включает:
Некорректные расширения могут быть отброшены или проигнорированы без ошибки, в зависимости от реализации движка.
Данные, поступающие извне (формы, API, конфигурации), требуют многоуровневой обработки:
null и undefinedIntl.getCanonicalLocalesПример устойчивой обработки:
function normalizeLocales(input) {
return Intl.getCanonicalLocales(
(Array.isArray(input) ? input : [input])
.filter(Boolean)
);
}
Существует два подхода:
Intl.LocalegetCanonicalLocalesВыбор зависит от контекста:
Регулярные выражения не покрывают весь BCP 47 стандарт, особенно расширения и вариации, поэтому использование встроенных механизмов остаётся единственным надёжным способом.
Валидация локалей в экосистеме Intl представляет собой
многослойный процесс:
Эти механизмы образуют единый конвейер обработки локалей, в котором строковое значение превращается в строго определённый и предсказуемый идентификатор локализации.