Валидация опций

Intl API в JavaScript опирается на строгую систему обработки параметров конфигурации, которая определяет поведение форматирования локализованных данных. Практически каждый конструктор (Intl.DateTimeFormat, Intl.NumberFormat, Intl.Collator, Intl.PluralRules, Intl.RelativeTimeFormat, Intl.ListFormat) принимает объект опций, который проходит многоуровневую валидацию до того, как используется в реальной локализации.

Приведение параметров к объекту

Первый этап обработки опций связан с приведением входного значения к объекту.

Если передано undefined, используется пустой объект:

new Intl.NumberFormat("ru")
new Intl.NumberFormat("ru", undefined)

Оба варианта эквивалентны.

Если передан примитив, он преобразуется через Object(...):

new Intl.NumberFormat("ru", 42)
// эквивалентно new Number(42) как объекту

На этом этапе возможна ошибка:

  • TypeError, если значение не может быть приведено к объекту (например, null)
new Intl.NumberFormat("ru", null) // TypeError

Нормализация и извлечение свойств

После приведения к объекту выполняется извлечение опций через внутренний механизм, аналогичный Get в спецификации ECMAScript.

Ключевой момент: доступ к свойствам осуществляется динамически, поэтому геттеры могут быть выполнены:

const options = {
  minimumFractionDigits: {
    valueOf() {
      return 2;
    }
  }
};

Валидация не требует «плоского» объекта — допускаются вычисляемые значения.


Каноникализация значений

Intl API приводит многие параметры к строгим строковым наборам значений. Этот процесс называется каноникализацией.

Пример:

new Intl.DateTimeFormat("ru", {
  hourCycle: "H24"
});

Значение "H24" будет преобразовано в каноническое "h24" или заменено на дефолтное в зависимости от реализации.

Примеры канонических параметров

  • localeMatcher: "best fit" | "lookup"
  • formatMatcher: "basic" | "best fit"
  • hourCycle: "h11" | "h12" | "h23" | "h24"
  • numberingSystem: "latn", "arab", "deva" и др.
  • calendar: "gregory", "islamic", "buddhist" и др.

Некорректные значения приводят к откату к значениям по умолчанию.


Строгая проверка допустимых диапазонов

Часть опций проходит числовую проверку диапазона. Это особенно характерно для Intl.NumberFormat.

new Intl.NumberFormat("ru", {
  minimumFractionDigits: -1
});

Такое значение приводит к исключению RangeError, поскольку допустимый диапазон — от 0 до 20.

Типичные диапазонные проверки:

  • minimumFractionDigits ∈ [0, 20]
  • maximumFractionDigits ∈ [0, 20]
  • minimumSignificantDigits ∈ [1, 21]
  • maximumSignificantDigits ∈ [1, 21]

Также проверяется логическая согласованность:

{
  minimumFractionDigits: 5,
  maximumFractionDigits: 2
}

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


Взаимозависимость опций

Валидация Intl API учитывает взаимные ограничения параметров. Некоторые опции не могут существовать независимо.

NumberFormat

Если заданы значащие цифры, дробные игнорируются:

{
  minimumFractionDigits: 2,
  maximumFractionDigits: 4,
  minimumSignificantDigits: 3
}

В этом случае fractionDigits может быть проигнорирован.

DateTimeFormat

Опции dateStyle и timeStyle конфликтуют с детализированными полями:

{
  dateStyle: "full",
  year: "numeric"
}

year будет игнорироваться, поскольку стиль форматирования имеет приоритет.


Обработка неизвестных опций

Intl API игнорирует неизвестные ключи, не выбрасывая ошибок.

new Intl.NumberFormat("ru", {
  unknownOption: true
});

Такой код валиден, но unknownOption не влияет на результат.

Это поведение обеспечивает обратную совместимость и устойчивость API.


Проверка локалей и fallback-механизм

Хотя локали формально не являются частью опций, они тесно связаны с их валидацией.

new Intl.DateTimeFormat("xx-XX", {
  timeZone: "Europe/Mars"
});

Если локаль или таймзона не поддерживаются:

  • применяется fallback-локаль (например "en-US")
  • либо ближайшая доступная конфигурация

При этом ошибки не обязательны — Intl стремится к деградации, а не к падению.


Валидация timeZone

Intl.DateTimeFormat выполняет строгую проверку таймзоны:

new Intl.DateTimeFormat("ru", {
  timeZone: "Invalid/Zone"
});

Результат:

  • RangeError, если таймзона не существует в IANA базе

Допустимые значения соответствуют IANA Time Zone Database:

  • Europe/Moscow
  • Asia/Almaty
  • UTC

Логика обработки boolean-подобных значений

Некоторые опции приводятся к логическому типу через абстрактное преобразование:

new Intl.NumberFormat("ru", {
  useGrouping: "0"
});

Строка "0" интерпретируется как truthy, поэтому группировка включится.

Корректный способ:

{
  useGrouping: false
}

Обработка числовых опций через ToNumber

Числовые параметры проходят через ToNumber, что создаёт неожиданные эффекты:

new Intl.NumberFormat("ru", {
  minimumFractionDigits: "3"
});

Строка "3" будет преобразована в число 3.

Но:

{
  minimumFractionDigits: "3abc"
}

даст NaN и приведёт к использованию значений по умолчанию или ошибке в зависимости от поля.


Особенности работы с undefined и null значениями

Различие между undefined и null критично:

  • undefined → значение игнорируется, используется дефолт
  • null → чаще приводит к TypeError или конвертации в объект
new Intl.NumberFormat("ru", {
  style: undefined
});

эквивалентно отсутствию style.


Дедупликация и приоритет опций

Некоторые конструкторы выполняют нормализацию с приоритетами:

  1. Явные стилевые параметры (style)
  2. Специфические опции форматирования
  3. Локальные настройки окружения
  4. Значения по умолчанию

Пример приоритета:

new Intl.NumberFormat("ru", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 10
});

Здесь style: "currency" активирует валютный режим, который может ограничить влияние других числовых параметров.


Нормализация внутри спецификации

Внутренне Intl использует алгоритмы:

  • ToObject(options)
  • Get(options, property)
  • ToString
  • CanonicalizeLocaleList

Каждый этап строго определён спецификацией ECMA-402.

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


Поведение при конфликтующих комбинациях

Конфликтующие опции не всегда приводят к ошибке. Чаще происходит переопределение:

new Intl.Collator("ru", {
  usage: "sort",
  sensitivity: "base",
  ignorePunctuation: true
});

Если комбинация невалидна, Intl выбирает ближайшую допустимую конфигурацию.


Ленивое применение валидации

Важно, что часть проверок выполняется не в момент создания объекта, а при первом использовании:

const fmt = new Intl.DateTimeFormat("ru", {
  timeZone: "Europe/Moscow"
});

// здесь ещё нет ошибки

fmt.format(new Date());

Это означает:

  • конструктор не всегда выполняет полную проверку
  • ошибки могут проявляться позже, при вызове методов

Итоговые свойства системы валидации

Система обработки опций Intl API характеризуется следующими принципами:

  • мягкая деградация при неизвестных значениях
  • строгая проверка критических параметров (например, timeZone)
  • каноникализация строковых опций
  • числовая нормализация через ToNumber
  • игнорирование лишних ключей
  • приоритетность конфликтующих параметров
  • ленивое выполнение части проверок