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

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

В библиотеке Globalize локаль устройства используется как первичный ориентир при выборе набора правил форматирования и локализованных ресурсов, загруженных из CLDR (Common Locale Data Repository). Однако сама библиотека не выполняет автоматического «угадывания» локали — она опирается на данные среды выполнения.

Источники локали в JavaScript-среде

Определение локали происходит через несколько стандартных каналов:

1. Браузерное API

Основной источник в веб-среде:

  • navigator.language
  • navigator.languages
navigator.language;     // "ru-RU"
navigator.languages;    // ["ru-RU", "en-US", "en"]

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

2. Node.js окружение

В серверной среде локаль часто определяется через:

  • Intl.DateTimeFormat().resolvedOptions().locale
  • переменные окружения (LANG, LC_ALL)
const locale = Intl.DateTimeFormat().resolvedOptions().locale;

3. API Intl как источник фактической локали

const locale = new Intl.NumberFormat().resolvedOptions().locale;

Этот механизм отражает реальную локаль, которую использует движок JavaScript.


Роль локали в Globalize

Библиотека Globalize не использует системную локаль автоматически в момент вызова форматирующих функций. Вместо этого она требует явного задания локали:

Globalize.locale("ru");

или

Globalize.locale("ru-RU");

Таким образом разделяется два уровня:

  • Системная локаль — определяется средой выполнения
  • Локаль Globalize — задаётся программно и управляет форматированием

Преобразование системной локали в локаль Globalize

Системная локаль часто содержит региональную часть (ru-RU, en-US, fr-FR). Globalize использует CLDR-идентификаторы, которые совместимы с таким форматом, но могут требовать нормализации.

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

Типичные преобразования:

  • ru-RUru
  • en-USen
  • pt-BRpt-BR (сохраняется регион)
function normalizeLocale(locale) {
  if (!locale) return "en";
  const parts = locale.split("-");
  return parts.length > 1 ? `${parts[0]}-${parts[1]}` : parts[0];
}

Автоматическое определение локали

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

  1. navigator.languages[0]
  2. navigator.language
  3. локаль по умолчанию
const locale =
  (navigator.languages && navigator.languages[0]) ||
  navigator.language ||
  "en";

Globalize.locale(locale);

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

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

  • переход к базовой языковой части (fr-CAfr)
  • использование явно заданной локали по умолчанию
  • невозможность форматирования при отсутствии данных CLDR

Связь локали и CLDR данных

Для корректной работы Globalize требует загрузки структурированных данных:

  • числа
  • валюты
  • даты и время
  • правила плюрализации
Globalize.load(
  require("cldr-data").entireSupplemental(),
  require("cldr-data").entireMainFor("ru")
);

Без этих данных локаль, определённая устройством, не приводит к корректному форматированию.


Приоритеты выбора локали в приложении

В реальных системах интернационализации используется многоуровневая модель:

  1. Явно заданная локаль пользователя (настройки профиля)
  2. Локаль устройства
  3. Локаль браузера
  4. Локаль сервера по умолчанию
const userLocale = getUserSettingsLocale();
const deviceLocale =
  navigator.languages?.[0] ||
  navigator.language;

Globalize.locale(userLocale || deviceLocale || "en");

Особенности работы с языковыми вариантами

Различия между локалями могут влиять на форматирование:

  • en-US — месяц/день/год, доллар
  • en-GB — день/месяц/год, фунт
  • ru-RU — день.месяц.год, рубль

Globalize интерпретирует эти различия через CLDR, поэтому корректность локали критична.


Использование Intl как резервного механизма

При отсутствии явной локализации в Globalize можно использовать стандартный API:

const formatter = new Intl.DateTimeFormat(undefined, {
  dateStyle: "long"
});

formatter.format(new Date());

undefined заставляет движок использовать локаль устройства, что часто применяется как fallback до инициализации Globalize.


Ошибки определения локали и их источники

Типовые проблемы:

  • отсутствие navigator.languages в устаревших окружениях
  • некорректные значения переменных окружения в Node.js
  • несовпадение формата CLDR и системной локали
  • отсутствие загруженных данных для региона

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