Как узнать, какой язык и параметры реально использует форматтер?

Используйте метод resolvedOptions(), чтобы посмотреть фактическую конфигурацию любого Intl форматтера

Введение

Когда вы создаёте форматтер Intl в JavaScript, запрошенные вами параметры не всегда совпадают с теми, что реально используются. Браузер проводит согласование локали, подставляет значения по умолчанию и нормализует ваши настройки, чтобы получить итоговую конфигурацию. Метод resolvedOptions() позволяет узнать, какие именно параметры применяются в итоге.

У каждого форматтера в API Intl есть метод resolvedOptions(). Это касается Intl.DateTimeFormat, Intl.NumberFormat, Intl.ListFormat, Intl.PluralRules, Intl.Collator и других. Этот метод возвращает объект со всеми деталями конфигурации, которые форматтер использует после обработки ваших входных данных.

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

Базовый синтаксис

Метод resolvedOptions() не принимает параметры. Его вызывают на любом экземпляре форматтера, и он возвращает объект.

const formatter = new Intl.DateTimeFormat('en-US');
const options = formatter.resolvedOptions();

console.log(options);
// {
//   locale: 'en-US',
//   calendar: 'gregory',
//   numberingSystem: 'latn',
//   timeZone: 'America/Los_Angeles',
//   ...
// }

Возвращаемый объект всегда содержит как минимум свойства locale, calendar и numberingSystem для форматтеров дат, или locale и numberingSystem для форматтеров чисел. Дополнительные свойства зависят от указанных вами опций и типа форматтера.

Почему запрошенные параметры отличаются от итоговых

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

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

const formatter = new Intl.DateTimeFormat('de-AT-u-ca-buddhist');
const options = formatter.resolvedOptions();

console.log(options.locale);
// Likely outputs: 'de-AT' or 'de'

console.log(options.calendar);
// Likely outputs: 'gregory' (if Buddhist calendar is not supported)

Во-вторых, браузер подставляет значения по умолчанию для любых опций, которые вы не указали. Эти значения зависят от локали.

const formatter = new Intl.NumberFormat('en-US');
const options = formatter.resolvedOptions();

console.log(options.style);
// Outputs: 'decimal'

console.log(options.minimumFractionDigits);
// Outputs: 0

console.log(options.maximumFractionDigits);
// Outputs: 3

В-третьих, браузер нормализует некоторые опции до их канонической формы. Если вы используете dateStyle или timeStyle, браузер не включает их в итоговые опции. Вместо этого он добавляет отдельные компонентные опции, на которые они разворачиваются.

Проверка фактической используемой локали

Свойство locale в итоговых опциях показывает, какую именно локаль использует форматтер. Это результат согласования локалей.

const formatter = new Intl.DateTimeFormat(['zh-TW', 'zh-CN', 'en-US']);
const options = formatter.resolvedOptions();

console.log(options.locale);
// Outputs the first supported locale from the list

Если вы запрашиваете локаль с ключевыми словами расширения Unicode, итоговая локаль может не содержать эти ключевые слова, если они не поддерживаются или были применены как отдельные опции.

const formatter = new Intl.NumberFormat('en-US-u-nu-arab');
const options = formatter.resolvedOptions();

console.log(options.locale);
// Might output: 'en-US'

console.log(options.numberingSystem);
// Might output: 'arab' or 'latn' depending on support

Просмотр опций форматирования даты и времени

Для Intl.DateTimeFormat итоговые опции содержат детали о том, какие компоненты даты и времени будут отображаться и как именно.

const formatter = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  timeZone: 'America/New_York'
});

const options = formatter.resolvedOptions();

console.log(options.year);
// Outputs: 'numeric'

console.log(options.month);
// Outputs: 'long'

console.log(options.day);
// Outputs: 'numeric'

console.log(options.timeZone);
// Outputs: 'America/New_York'

console.log(options.calendar);
// Outputs: 'gregory'

Когда вы используете dateStyle или timeStyle, эти сокращения не включаются в итоговые опции. Браузер разворачивает их в отдельные компонентные опции в зависимости от локали.

const formatter = new Intl.DateTimeFormat('en-US', {
  dateStyle: 'full'
});

const options = formatter.resolvedOptions();

console.log(options.dateStyle);
// Outputs: undefined

console.log(options.weekday);
// Outputs: 'long'

console.log(options.year);
// Outputs: 'numeric'

console.log(options.month);
// Outputs: 'long'

console.log(options.day);
// Outputs: 'numeric'

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

const formatter = new Intl.DateTimeFormat('ja-JP');
const options = formatter.resolvedOptions();

console.log(options);
// Shows default date component options for Japanese locale

Просмотр опций форматирования чисел

Для Intl.NumberFormat итоговые опции показывают стиль, точность, группировку и другие детали форматирования.

const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

const options = formatter.resolvedOptions();

console.log(options.style);
// Outputs: 'currency'

console.log(options.currency);
// Outputs: 'USD'

console.log(options.currencyDisplay);
// Outputs: 'symbol'

console.log(options.minimumFractionDigits);
// Outputs: 2

console.log(options.maximumFractionDigits);
// Outputs: 2

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

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

const formatter = new Intl.NumberFormat('en-US', {
  minimumSignificantDigits: 3,
  maximumSignificantDigits: 5
});

const options = formatter.resolvedOptions();

console.log(options.minimumSignificantDigits);
// Outputs: 3

console.log(options.maximumSignificantDigits);
// Outputs: 5

Проверка других типов форматтеров

Метод resolvedOptions() работает одинаково для всех Intl-форматтеров.

Для Intl.ListFormat он показывает тип и стиль.

const formatter = new Intl.ListFormat('en-US', {
  style: 'long',
  type: 'conjunction'
});

const options = formatter.resolvedOptions();

console.log(options.locale);
// Outputs: 'en-US'

console.log(options.style);
// Outputs: 'long'

console.log(options.type);
// Outputs: 'conjunction'

Для Intl.PluralRules он показывает тип и минимальное/максимальное количество цифр.

const formatter = new Intl.PluralRules('en-US', {
  type: 'ordinal'
});

const options = formatter.resolvedOptions();

console.log(options.locale);
// Outputs: 'en-US'

console.log(options.type);
// Outputs: 'ordinal'

console.log(options.minimumIntegerDigits);
// Outputs: 1

Типовые сценарии использования resolvedOptions()

Самый частый сценарий — отладка. Если отформатированный вывод выглядит не так, как вы ожидали, resolvedOptions() помогает понять, что именно делает форматтер.

const formatter = new Intl.DateTimeFormat('en-US', {
  dateStyle: 'medium'
});

console.log(formatter.format(new Date()));
// Check the output

console.log(formatter.resolvedOptions());
// See exactly which options produced that output

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

const formatter = new Intl.DateTimeFormat('en-US', {
  calendar: 'islamic'
});

const options = formatter.resolvedOptions();

if (options.calendar === 'islamic') {
  console.log('Islamic calendar is supported');
} else {
  console.log('Islamic calendar is not supported, using ' + options.calendar);
}

Также можно использовать resolvedOptions(), чтобы получить предпочтения пользователя по локали. Если вы создаёте форматтер без указания локали, браузер использует настройки пользователя.

const formatter = new Intl.DateTimeFormat();
const options = formatter.resolvedOptions();

console.log(options.locale);
// Outputs: the user's preferred locale

console.log(options.timeZone);
// Outputs: the user's time zone

console.log(options.hourCycle);
// Outputs: the user's hour cycle preference (12 or 24 hour)

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

const formatter1 = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

const options = formatter1.resolvedOptions();

const formatter2 = new Intl.NumberFormat(options.locale, options);

// formatter2 will format numbers identically to formatter1

Основные выводы

Метод resolvedOptions() возвращает объект с фактической конфигурацией, которую использует форматтер. Эта конфигурация может отличаться от запрошенной из-за согласования локали, значений по умолчанию и нормализации.

У каждого Intl-форматтера есть этот метод. Вы вызываете его без параметров и получаете объект со свойствами вроде locale, calendar, numberingSystem и специфическими для форматтера опциями.

Используйте resolvedOptions() в основном для отладки поведения форматтера, определения доступных функций и понимания того, как браузер интерпретировал ваши параметры. Возвращаемый объект также можно использовать для воссоздания идентичного форматтера или для просмотра пользовательских настроек.