Unicode расширения локалей

Локаль в JavaScript представляется строкой, соответствующей стандарту BCP 47. Помимо базовой структуры вида language-region-script, стандарт поддерживает механизм расширений Unicode, позволяющий задавать дополнительные параметры форматирования без изменения самой локали. Эти расширения начинаются с ключа -u- и образуют набор ключ-значение, который интерпретируется через Intl API.

Структура Unicode extension sequence

Общий вид:

language[-script][-region][-variants]-u-key1-value1-key2-value2

Пример:

en-US-u-ca-gregory-nu-latn

Здесь:

  • ca-gregory — календарь Gregorian
  • nu-latn — латинская система чисел

Каждый ключ имеет фиксированное значение из реестра Unicode CLDR, что обеспечивает переносимость между реализациями.


Intl.Locale и извлечение расширений

Объект Intl.Locale позволяет декодировать Unicode extensions и работать с ними как со структурированными свойствами.

const locale = new Intl.Locale("en-US-u-ca-islamic-nu-arab");

console.log(locale.calendar);  // "islamic"
console.log(locale.numberingSystem); // "arab"

Основные свойства Intl.Locale

  • calendar — система календаря
  • numberingSystem — система числовой записи
  • collation — правила сортировки
  • caseFirst — приоритет регистра при сортировке
  • hourCycle — формат 12/24 часа
  • numeric — числовая сортировка строк

Календарные системы (ca)

Ключ ca определяет календарную систему, используемую при форматировании дат.

Примеры значений

  • gregory — григорианский календарь
  • buddhist — буддийский календарь
  • islamic — исламский календарь
  • hebrew — еврейский календарь
  • chinese — китайский календарь

Использование в Intl.DateTimeFormat

const fmt = new Intl.DateTimeFormat("en-US-u-ca-islamic");

console.log(fmt.format(new Date()));

Особенности обработки

Календарь влияет не только на отображение года, но и на структуру всего вычисления даты, включая эпоху и длину месяцев.


Системы нумерации (nu)

Ключ nu задаёт систему записи чисел.

Распространённые системы

  • latn — латинские цифры (0–9)
  • arab — арабско-индийские цифры
  • thai — тайская система
  • deva — деванагари
  • fullwide — полноширинные символы

Пример

const fmt = new Intl.NumberFormat("ar-EG-u-nu-arab");

console.log(fmt.format(123456)); // цифры в арабской системе

Влияние на Intl.NumberFormat

Система нумерации влияет только на визуальное представление числа, не затрагивая его внутреннее значение.


Колляция и сортировка (co)

Ключ co определяет алгоритм сортировки строк.

Основные значения

  • standard — стандартная сортировка
  • search — оптимизация под поиск
  • phonebk — телефонная книга (немецкий стиль)
  • emoji — сортировка эмодзи

Пример

const collator = new Intl.Collator("de-DE-u-co-phonebk");

console.log(collator.compare("ä", "a"));

Особенности

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


Режим учета регистра (kf)

Ключ kf управляет приоритетом регистра при сортировке.

Значения

  • upper
  • lower
  • false

Пример

const collator = new Intl.Collator("en-u-kf-upper");

console.log(collator.compare("a", "A"));

При upper заглавные символы имеют приоритет.


Часовой цикл (hc)

Ключ hc определяет формат времени.

Значения

  • h12 — 12-часовой формат
  • h23 — 24-часовой формат
  • h11, h24 — региональные варианты

Пример

const fmt = new Intl.DateTimeFormat("en-US-u-hc-h24", {
  hour: "numeric",
  minute: "numeric"
});

console.log(fmt.format(new Date()));

Числовая сортировка (kn)

Ключ kn включает числовую интерпретацию строк.

Поведение

Без kn строки сортируются лексикографически:

["file1", "file10", "file2"]

С kn включенным:

["file1", "file2", "file10"]

Пример

const collator = new Intl.Collator("en-u-kn-true");

console.log(
  ["file1", "file10", "file2"].sort(collator.compare)
);

Локальные предпочтения и расширения в Intl API

Unicode extensions могут задаваться как напрямую в строке локали, так и через API опции. Однако строковое расширение имеет приоритет, если оно конфликтует с параметрами конструктора.

Пример конфликта

new Intl.NumberFormat("en-US-u-nu-arab", {
  numberingSystem: "latn"
});

В этом случае результат зависит от реализации, но обычно строковое расширение nu-arab имеет приоритет.


Парсинг и нормализация Intl.Locale

Intl.Locale нормализует расширения, приводя их к каноническому виду.

const loc = new Intl.Locale("EN-us-u-Nu-LaTn");

console.log(loc.toString()); 
// "en-US-u-nu-latn"

Каскад расширений

Один идентификатор может содержать несколько ключей:

en-US-u-ca-gregory-nu-latn-co-phonebk-kf-upper-hc-h24

Разбор:

  • календарь: gregory
  • числа: latn
  • сортировка: phonebk
  • регистр: upper
  • часы: 24-часовой формат

Ограничения и поддержка

Поддержка ключей зависит от движка JavaScript и ICU-библиотеки. Основные ограничения:

  • не все календари доступны во всех локалях
  • некоторые collation-режимы частично реализованы
  • Intl.Locale поддерживается не во всех устаревших окружениях

Взаимодействие с CLDR

Unicode extensions напрямую связаны с базой данных CLDR (Common Locale Data Repository). Именно она определяет:

  • допустимые ключи
  • допустимые значения
  • поведение сортировки и форматирования

Intl API выступает как слой интерпретации над CLDR-данными, обеспечивая единый интерфейс в JavaScript.


Приоритет обработки расширений

При разборе локали происходит следующий порядок:

  1. Парсинг BCP 47 строки
  2. Выделение Unicode extension sequence
  3. Применение значений к Intl объектам
  4. Перекрытие опциями конструктора (частично)
  5. Финальная нормализация через ICU

Использование расширений в разных Intl конструкторах

NumberFormat

Поддерживает:

  • nu
  • co (частично через сравнение строк)
  • kn (через Collator)

DateTimeFormat

Поддерживает:

  • ca
  • hc
  • nu

Collator

Поддерживает:

  • co
  • kf
  • kn

Комбинирование локалей с расширениями

Одна и та же локаль может давать разные результаты:

const a = new Intl.NumberFormat("en-US");
const b = new Intl.NumberFormat("en-US-u-nu-arab");

console.log(a.format(12345));
console.log(b.format(12345));

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


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

Разбор строки:

fr-FR-u-ca-buddhist-nu-thai-co-emoji
  • язык: французский
  • регион: Франция
  • календарь: буддийский
  • числа: тайские
  • сортировка: эмодзи

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


Поведение при неизвестных ключах

Если движок не поддерживает конкретное расширение:

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

Пример:

en-US-u-ca-unknown

В этом случае ca будет проигнорирован, и используется gregory.


Влияние на интернационализацию интерфейсов

Unicode extensions позволяют задавать пользовательские предпочтения на уровне локали, что особенно важно для:

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

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