Обратная совместимость

Международное API в JavaScript построено вокруг строгого принципа: поведение должно оставаться предсказуемым при изменении версий движков и окружений. Это достигается сочетанием спецификации ECMAScript Internationalization API и зависимостью от ICU (International Components for Unicode), которая поставляет локализационные данные.

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


Зависимость от ICU и влияние на совместимость

Intl API не является полностью самодостаточным слоем JavaScript-движка. Основная логика форматирования делегируется ICU.

ICU как источник поведения

ICU определяет:

  • правила локализации дат и времени
  • алгоритмы сортировки строк (collation)
  • правила плюрализации
  • формат чисел и валют
  • поддержку языковых пакетов (locale data)

Из этого следует ключевой аспект обратной совместимости: различие версий ICU может менять результат форматирования без изменения JavaScript-кода.

Пример:

  • Node.js, собранный с ICU minimal → ограниченные локали
  • Node.js с full ICU → полный набор локалей и более точные правила

Это приводит к ситуации, когда один и тот же код:

new Intl.DateTimeFormat("ru-RU", { dateStyle: "full" }).format(new Date())

может возвращать слегка отличающиеся строки в разных окружениях.


Базовый слой стабильности спецификации

ECMAScript Internationalization API фиксирует:

  • интерфейсы конструкторов
  • поведение методов форматирования
  • алгоритмы fallback для локалей
  • структуру опций

Стабильность обеспечивается тем, что изменения добавляются только через расширение возможностей, но не через изменение существующего поведения.

Например:

  • Intl.DateTimeFormat не меняет порядок компонентов даты задним числом
  • Intl.NumberFormat сохраняет форматирование группировок цифр для заданной локали

Алгоритм fallback локалей

Одним из ключевых механизмов совместимости является алгоритм выбора локали.

Lookup fallback

При запросе:

new Intl.NumberFormat("ru-KZ")

движок выполняет цепочку:

  1. Проверка полной локали ru-KZ
  2. Проверка базовой ru
  3. Fallback на системную локаль
  4. Fallback на en или дефолт ICU

Этот механизм гарантирует, что код не падает при отсутствии локали, а деградирует предсказуемо.


Feature detection как основа совместимости

Поскольку Intl расширяется поэтапно, каждая новая сущность требует проверки доступности.

Проверка конструктора

if (typeof Intl.RelativeTimeFormat !== "undefined") {
  // безопасное использование
}

Проверка методов

Некоторые методы появляются позже:

  • formatToParts
  • formatRange
  • resolvedOptions

Пример проверки:

if (typeof Intl.DateTimeFormat.prototype.formatToParts === "function") {
  // структурированное форматирование доступно
}

Расширение API и проблема «частичной поддержки»

Развитие Intl происходит неравномерно между средами:

  • браузеры обновляются быстрее
  • Node.js зависит от версии V8 и ICU
  • старые устройства могут не иметь новых классов Intl

Типичный разрыв совместимости

Возможность Старые среды Современные среды
Intl.PluralRules отсутствует доступен
Intl.RelativeTimeFormat отсутствует доступен
Intl.Segmenter отсутствует частичная поддержка

Polyfill-стратегии

Для обеспечения совместимости используются полифиллы, которые имитируют недостающие части API.

Подход 1: полифилл через JS-реализацию

Например, Intl.PluralRules может быть эмулирован:

  • через таблицы правил CLDR
  • через упрощённые эвристики
  • через fallback на английскую модель плюрализации

Подход 2: библиотечные решения

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

  • FormatJS (intl polyfills)
  • core-js (частичная поддержка Intl)
  • full-icu data bundles для Node.js

Поведение при частичной реализации ICU

В некоторых сборках среды ICU урезан.

Minimal ICU

  • ограниченный набор языков
  • базовая локализация чисел и дат
  • отсутствие расширенных правил

Full ICU

  • полный набор локалей
  • корректные региональные правила
  • поддержка сложных языков (арабский, индийские локали)

Small ICU

  • компромиссный вариант
  • часто используется в production-сборках Node.js

Стабильность формата и риск регресса

Даже при стабильности API возможны изменения в результате форматирования.

Причины:

  • обновление базы CLDR
  • изменение правил грамматики в ICU
  • исправления ошибок локализации

Пример изменения:

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

Это считается допустимым видом эволюции API.


Intl.Locale и обратная совместимость

Intl.Locale добавляет структурированное представление локали.

const loc = new Intl.Locale("ru-KZ-u-nu-latn");

Проблема совместимости:

  • старые среды не поддерживают Intl.Locale
  • расширенные subtags могут игнорироваться

Fallback поведение:

  • строка локали сохраняется
  • неизвестные extension keys игнорируются

Поведение при неизвестных опциях

Intl API спроектирован так, чтобы игнорировать неизвестные параметры вместо выброса ошибок.

Пример:

new Intl.DateTimeFormat("ru", {
  dateStyle: "full",
  unknownOption: true
});

Поведение:

  • unknownOption игнорируется
  • остальные параметры применяются

Это критический элемент обратной совместимости: новые версии API не ломают старый код с дополнительными опциями.


Версионирование через capability queries

Вместо явного versioning используется проверка возможностей.

supportedLocalesOf

Intl.NumberFormat.supportedLocalesOf(["ru", "en"]);

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

resolvedOptions

new Intl.NumberFormat("ru").resolvedOptions();

Позволяет понять, какие настройки фактически применились после fallback.


Совместимость форматирования диапазонов

formatRange появился позже классических методов.

new Intl.DateTimeFormat("ru").formatRange(date1, date2);

Проблема совместимости:

  • старые среды не имеют метода
  • альтернативой является ручная конкатенация format()

Fallback стратегия:

  • проверка наличия метода
  • деградация к двум форматированиям

Стабильность числового форматирования

Intl.NumberFormat считается наиболее стабильной частью API.

Гарантируется:

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

Однако:

  • локальные стандарты могут изменяться
  • обновления ICU могут менять формат отображения

Сравнение строк и обратная совместимость collator

Intl.Collator зависит от сложных правил сортировки.

new Intl.Collator("ru").compare("ё", "е");

Проблема:

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

Обратная совместимость и эволюция API

Расширение Intl происходит по принципу:

  • добавление новых классов без изменения старых
  • расширение опций без удаления старых
  • сохранение поведения дефолтов

Добавление новых API:

  • Intl.RelativeTimeFormat
  • Intl.PluralRules
  • Intl.Segmenter
  • Intl.DisplayNames

Каждый новый модуль полностью изолирован от старых.


Деградация функциональности как механизм совместимости

Ключевая модель поведения Intl:

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

Примеры деградации:

  • отсутствующая локаль → fallback на базовую
  • отсутствующий класс → ручная реализация
  • отсутствующая опция → игнорирование

Итоговая модель устойчивости Intl API

Система обратной совместимости Intl строится на трёх слоях:

  • стабильный контракт ECMAScript
  • изменяемая, но совместимая ICU-реализация
  • fallback-алгоритмы локалей и опций

Эта модель позволяет API оставаться расширяемым без разрушения существующих приложений, при этом допуская эволюцию языковых и региональных стандартов без изменения интерфейсов JavaScript-кода.