Fallback для отсутствующих значений

В Intl-экосистеме JavaScript отсутствующие локализационные данные не приводят к падению выполнения кода. Вместо этого применяется многоуровневая система резервирования (fallback), основанная на стандарте BCP 47, алгоритмах выбора локали и внутренних таблицах CLDR. Такой подход обеспечивает предсказуемость даже в случаях, когда запрошенная комбинация языка, региона и опций не поддерживается полностью.


Алгоритм выбора локали: базовый уровень fallback

Каждый конструктор Intl (например, Intl.NumberFormat, Intl.DateTimeFormat, Intl.Collator) принимает список предпочтительных локалей. При этом фактическая локаль выбирается по алгоритму best fit или lookup, который определяет наиболее подходящую доступную локаль.

Lookup-алгоритм

При стратегии lookup система последовательно упрощает запрос:

  • ru-KZ-u-ca-gregory
  • ru-KZ
  • ru
  • en (если русская локаль отсутствует)
  • системная локаль среды

Каждый шаг удаляет наиболее специфичный компонент до нахождения совпадения.

Best fit-алгоритм

best fit не обязан строго следовать иерархии BCP 47. Реализация может:

  • сопоставлять ru-KZ с ru-RU
  • использовать ближайший региональный аналог
  • игнорировать часть Unicode extension subtags

Это позволяет движку возвращать более «естественный» результат, даже если точное совпадение отсутствует.


Резервирование через default locale

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

  • локаль среды выполнения (например, системная)
  • либо en как универсальный fallback в некоторых реализациях

Пример:

const fmt = new Intl.DateTimeFormat(['xx-YY', 'zz-ZZ']);

Если обе локали не существуют, результат будет эквивалентен:

new Intl.DateTimeFormat(Intl.DateTimeFormat().resolvedOptions().locale);

Поведение undefined и пустых значений

В API Intl значение undefined имеет особый смысл: оно активирует стандартный fallback.

Пример

new Intl.NumberFormat(undefined);

Эквивалентно:

new Intl.NumberFormat(defaultLocale);

При этом:

  • null не используется как валидный fallback
  • пустая строка приводит к приведению к дефолтной локали

Fallback в форматировании чисел

Отсутствие настроек локали

Если локаль не содержит данных о форматировании чисел, используется базовая модель:

  • разделитель групп → , или пробел (в зависимости от дефолта)
  • десятичный разделитель → .
  • минимальный набор цифр → ASCII или locale digits fallback

Пример:

new Intl.NumberFormat('unknown-locale').format(1234567.89);

Результат не ломается, а переключается на системный формат.


Fallback систем счисления

Unicode extension nu (numbering system) может быть проигнорирован:

new Intl.NumberFormat('ar-EG-u-nu-arab').format(123);

Если система счисления arab недоступна:

  • используется latn
  • либо дефолтный numbering system локали

Fallback в форматировании дат

Intl.DateTimeFormat демонстрирует более сложную систему деградации качества.

1. Отсутствие календаря

Если календарь (ca) не поддерживается:

new Intl.DateTimeFormat('ja-JP-u-ca-japanese');

fallback:

  • переход к Gregorian calendar
  • или к дефолтному календарю локали

2. Таймзоны

Если указанная таймзона отсутствует:

new Intl.DateTimeFormat('en-US', {
  timeZone: 'Unknown/Zone'
});

поведение:

  • fallback к UTC или локальной таймзоне среды
  • либо игнорирование параметра timeZone

3. Недоступные форматы компонентов

Если конкретный dateStyle или timeStyle не поддерживается:

  • происходит замена на ближайший доступный шаблон
  • например full → long → medium → short

Fallback через formatToParts

Метод formatToParts сохраняет структуру даже при деградации локали.

Если локализация частично отсутствует:

  • возвращается минимальный набор токенов
  • неизвестные части заменяются на fallback-токены

Пример:

new Intl.NumberFormat('xx-XX').formatToParts(1000);

Результат может включать:

  • integer
  • group
  • decimal
  • literal

даже если локаль не определена.


Fallback в Intl.RelativeTimeFormat

Отсутствие поддержки единиц времени приводит к упрощению:

new Intl.RelativeTimeFormat('unknown', { numeric: 'auto' });

fallback:

  • переход к en-подобной формулировке
  • замена сложных форм на базовые строки

Например:

  • «yesterday» → «1 day ago»
  • «tomorrow» → «in 1 day»

Fallback в Intl.PluralRules

Если локаль не содержит специальных правил множественного числа:

  • используется категория other как универсальная
  • применяется английская модель pluralization
new Intl.PluralRules('xx-XX').select(1);

результат:

  • one или other по умолчанию, чаще other

Unicode extension fallback (u- параметры)

Unicode extensions позволяют задавать поведение:

  • ca — календарь
  • nu — система счисления
  • hc — часовой цикл
  • kf — порядок сортировки

Если параметр не поддерживается:

  1. игнорируется полностью
  2. локаль используется без него
  3. происходит частичный fallback без ошибок

Пример:

new Intl.DateTimeFormat('en-US-u-hc-h12');

Если hc не поддерживается → игнорируется, но форматирование продолжается.


Fallback через supportedLocalesOf

Метод позволяет заранее определить, какие локали реально поддерживаются:

Intl.NumberFormat.supportedLocalesOf(['ru-KZ', 'xx-YY']);

результат:

  • возвращается только поддерживаемая часть массива
  • неподдерживаемые локали отбрасываются полностью

Это ключевой механизм для предотвращения неожиданных fallback во время форматирования.


Поведение resolvedOptions()

Каждый Intl-объект может раскрыть фактически применённые параметры:

const fmt = new Intl.DateTimeFormat(['xx-YY', 'ru']);
fmt.resolvedOptions();

типичные поля:

  • locale — фактически выбранная локаль
  • calendar — реальный календарь
  • numberingSystem — активная система счисления
  • timeZone — итоговая таймзона

Это основной инструмент диагностики fallback-цепочек.


Fallback в строковых сравнениях (Intl.Collator)

Если локаль отсутствует:

  • используется базовая Unicode Collation Algorithm (DUCET)
  • или английская collation-логика как дефолт
new Intl.Collator('unknown').compare('a', 'b');

fallback:

  • простое сравнение по Unicode code points (в крайних случаях)
  • или системная локаль

Каскад деградации качества локализации

Система Intl работает по принципу постепенного упрощения:

Уровень 1: Полное совпадение

  • язык + регион + расширения

Уровень 2: Частичное совпадение

  • язык + регион

Уровень 3: Только язык

  • ru, en, ar

Уровень 4: Ближайший доступный язык

Уровень 5: Системная локаль

Уровень 6: Минимальная реализация (fallback ICU)


Поведение в средах с урезанным ICU

Некоторые окружения (например, минимальные сборки Node.js или старые браузеры) используют:

  • reduced ICU data
  • частичный набор локалей

В этом случае fallback усиливается:

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

Практическое значение fallback-механизма

Fallback в Intl решает три критические задачи:

  1. Стабильность API

    • отсутствие исключений при неизвестных локалях
  2. Предсказуемость

    • всегда существует валидный формат
  3. Грациозная деградация

    • качество снижается постепенно, а не резко

Скрытые особенности поведения fallback

1. Невозврат ошибок

Intl почти никогда не выбрасывает исключения из-за локали

2. Ленивое применение fallback

Решение принимается во время создания объекта, а не при каждом format

3. Кэширование выбранной локали

После резолва fallback больше не пересчитывается

4. Независимость компонентов

  • числа
  • даты
  • коллатор могут использовать разные fallback-цепочки даже для одной локали

Итоговая модель поведения

Система fallback в Intl строится вокруг идеи:

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

Такой подход позволяет API оставаться устойчивым в условиях неполных локализационных данных и различий между окружениями выполнения JavaScript.