State management локали

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

Ключевым элементом выступает строка локали в формате BCP 47 (например, en-US, ru-RU, zh-Hans-CN), которая используется как входная точка для построения внутренних объектов форматирования. Однако реальное состояние локали в приложении выходит за пределы строки и включает результаты нормализации, выбранные фолбэки и параметры, вычисленные движком ICU.

Структура и семантика Intl.Locale

Объект Intl.Locale представляет формализованное описание локали, включая язык, письменность, регион, календарь и другие расширения.

const locale = new Intl.Locale('ru-RU');

Структура локали включает несколько уровней:

  • language: базовый язык (ru)
  • script: письменность (Cyrl)
  • region: регион (RU)
  • extensions: дополнительные параметры (nu, ca, hc)

Расширенные параметры позволяют описывать, например, тип календаря или систему нумерации:

const locale = new Intl.Locale('ar-EG-u-nu-arab-ca-islamic');

С точки зрения состояния приложения Intl.Locale является неизменяемым объектом. Любая модификация приводит к созданию нового экземпляра:

const base = new Intl.Locale('en-US');
const withRegion = base.setRegion('GB');

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

Разрешение локали и механизм fallback

При создании форматтеров Intl выполняется процесс разрешения локали. Он включает:

  • выбор ближайшей поддерживаемой локали движком
  • применение пользовательских настроек окружения
  • учет fallback-цепочек
const formatter = new Intl.DateTimeFormat('xx-XX');
console.log(formatter.resolvedOptions().locale);

Результирующая локаль часто отличается от запрошенной. Это значение становится фактическим состоянием, влияющим на поведение форматирования.

Механизм fallback особенно важен при работе с частично поддерживаемыми идентификаторами локалей, где движок ICU выбирает наиболее близкий доступный вариант.

Хранение локали как части состояния приложения

В архитектуре приложений локаль рассматривается как глобальное или контекстное состояние. Она влияет на:

  • форматирование дат (Intl.DateTimeFormat)
  • форматирование чисел (Intl.NumberFormat)
  • сортировку строк (Intl.Collator)
  • отображение сообщений

Базовый подход предполагает хранение текущей локали в одном источнике истины:

let currentLocale = 'en-US';

function setLocale(nextLocale) {
  currentLocale = nextLocale;
}

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

Кэширование Intl-объектов и зависимость от локали

Создание объектов Intl является относительно дорогой операцией, поскольку включает обращение к ICU-данным. Поэтому состояние локали тесно связано с стратегией кэширования.

Типичная ошибка — повторное создание форматтера без учета локали:

function formatDate(date) {
  const formatter = new Intl.DateTimeFormat(currentLocale);
  return formatter.format(date);
}

Более устойчивый подход использует кэш по локали:

const dateFormatCache = new Map();

function getDateFormatter(locale) {
  if (!dateFormatCache.has(locale)) {
    dateFormatCache.set(locale, new Intl.DateTimeFormat(locale));
  }
  return dateFormatCache.get(locale);
}

Такой подход связывает состояние локали с состоянием кэша, минимизируя пересоздание объектов при повторном использовании.

Инвалидация кэша при смене локали

Изменение локали требует пересмотра всех зависимых Intl-объектов. При наличии кэша возникает проблема синхронизации состояния.

Стратегии управления включают:

  • полную очистку кэша
  • версионирование локали
  • ленивое обновление

Пример очистки:

function setLocale(nextLocale) {
  currentLocale = nextLocale;
  dateFormatCache.clear();
}

Более масштабируемая модель использует ключ вида ${locale}-${optionsHash}, позволяя учитывать не только локаль, но и параметры форматирования.

Intl.NumberFormat как зависимость состояния локали

Форматирование чисел напрямую зависит от локали, включая:

  • разделитель тысяч
  • десятичный разделитель
  • правила группировки
const nf = new Intl.NumberFormat('de-DE');
nf.format(1234567.89);

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

Intl.DateTimeFormat и контекст времени

Форматирование дат включает не только локаль, но и часовой пояс, что расширяет понятие состояния:

const dtf = new Intl.DateTimeFormat('ru-RU', {
  timeZone: 'Europe/Moscow'
});

Комбинация локали и временной зоны формирует составное состояние, влияющее на итоговое представление времени.

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

Intl.Collator и состояние сортировки

Сравнение строк зависит от локали и правил сортировки:

const collator = new Intl.Collator('tr-TR', { sensitivity: 'base' });

Сортировка в разных локалях приводит к различным порядкам символов, что делает локаль частью логики бизнес-данных, а не только UI.

Состояние коллатора часто кэшируется отдельно от форматтеров чисел и дат из-за различных опций конфигурации.

Наследование локали и окружение выполнения

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

  • операционной системы
  • браузера
  • настроек пользователя
const formatter = new Intl.NumberFormat();

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

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

Иммутабельность и стабильность локализационного состояния

Все основные объекты Intl являются неизменяемыми. После создания их параметры не могут быть изменены. Это свойство делает их пригодными для безопасного хранения в состоянии приложения и повторного использования.

const nf = new Intl.NumberFormat('en-US');
// nf.format = изменяемый метод, но конфигурация неизменяема

Иммутабельность упрощает:

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

Стратегии управления локалью в больших приложениях

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

Централизованное состояние

Локаль хранится в одном модуле и используется через доступ к функции получения текущего значения.

Контекстная локаль

Локаль передается через контекст выполнения, что позволяет различным частям приложения использовать разные настройки одновременно.

Мемоизированные фабрики

Фабрики Intl-объектов зависят от локали и параметров:

function createFormatters(locale) {
  return {
    date: new Intl.DateTimeFormat(locale),
    number: new Intl.NumberFormat(locale),
    collator: new Intl.Collator(locale)
  };
}

Такая группировка снижает избыточные пересоздания и синхронизирует состояние внутри одного набора форматтеров.

Версионирование локали как часть состояния

При динамической смене локали часто вводится версия состояния:

let localeState = {
  locale: 'en-US',
  version: 0
};

function updateLocale(nextLocale) {
  localeState = {
    locale: nextLocale,
    version: localeState.version + 1
  };
}

Версия позволяет автоматически инвалидировать кэши и пересчитывать зависимости без ручного управления списками объектов.

Производственные ограничения и стоимость пересоздания

Intl-объекты зависят от ICU-данных, что делает их создание относительно дорогим по сравнению с обычными функциями. Поэтому управление состоянием локали тесно связано с оптимизацией производительности:

  • минимизация пересоздания форматтеров
  • использование долгоживущих экземпляров
  • группировка по локали и опциям
  • ленивое создание при первом использовании

Баланс между актуальностью локали и затратами на пересоздание определяет архитектуру слоя локализации.

Стабильность состояния и предсказуемость вывода

Корректное управление локалью обеспечивает стабильность отображения данных во всей системе. При одинаковом состоянии локали результат работы Intl-объектов становится полностью детерминированным, включая формат дат, чисел и порядок сортировки строк.