Как узнать, какой календарь или систему чисел использует локаль

Определяйте и проверяйте системы календарей и форматы чисел для любой локали с помощью JavaScript

Введение

Когда кто-то из Таиланда смотрит на дату в вашем веб-приложении, они ожидают увидеть даты по буддийскому календарю, а не по григорианскому, принятому в западных странах. Точно так же носители арабского языка ожидают увидеть цифры, отображённые как ١٢٣, а не 123. Разные культуры используют разные системы календарей и чисел, и в JavaScript есть инструменты, которые позволяют определить, какие из них применяются для конкретной локали.

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

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

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

Система календаря — это способ организации времени по годам, месяцам и дням. Хотя григорианский календарь широко распространён, многие культуры используют другие системы календарей по религиозным, историческим или культурным причинам.

Самые распространённые системы календарей включают:

  • gregory — григорианский календарь, используется в большинстве западных стран
  • buddhist — буддийский календарь, используется в Таиланде, Камбодже и Мьянме
  • islamic — исламский лунный календарь, используется для религиозных целей в мусульманских странах
  • hebrew — еврейский календарь, используется в Израиле и для иудейских религиозных обрядов
  • japanese — японский календарь, использует императорские эры
  • chinese — китайский лунно-солнечный календарь, используется для традиционных праздников
  • persian — персидский солнечный календарь, используется в Иране и Афганистане
  • indian — индийский национальный календарь
  • coptic — коптский календарь, используется в Египте коптскими христианами

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

Понимание систем счисления

Система счисления — это набор символов для обозначения цифр. В западных странах используются латинские цифры (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), а в других культурах для тех же числовых значений применяются другие символы.

К распространённым системам счисления относятся:

  • latn для латинских цифр: 0 1 2 3 4 5 6 7 8 9
  • arab для арабских цифр: ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩
  • arabext для восточно-арабских цифр: ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹
  • deva для деванагари: ० १ २ ३ ४ ५ ६ ७ ८ ९
  • beng для бенгальских цифр: ০ ১ ২ ৩ ৪ ৫ ৬ ৭ ৮ ৯
  • thai для тайских цифр: ๐ ๑ ๒ ๓ ๔ ๕ ๖ ๗ ๘ ๙
  • hanidec для китайских десятичных чисел
  • fullwide для полноширинных цифр, используемых в восточноазиатской типографике

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

Как узнать календарь по умолчанию для локали

Метод getCalendars() возвращает массив календарных систем, которые обычно используются для данной локали, в порядке предпочтения. Первый элемент — это календарь по умолчанию.

const locale = new Intl.Locale("th-TH");
const calendars = locale.getCalendars();
console.log(calendars);
// ["buddhist", "gregory"]

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

В разных локалях используются разные календари по умолчанию:

const usLocale = new Intl.Locale("en-US");
console.log(usLocale.getCalendars());
// ["gregory"]

const saLocale = new Intl.Locale("ar-SA");
console.log(saLocale.getCalendars());
// ["gregory", "islamic", "islamic-civil"]

const jpLocale = new Intl.Locale("ja-JP");
console.log(jpLocale.getCalendars());
// ["gregory", "japanese"]

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

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

Как узнать систему счисления по умолчанию для локали

Метод getNumberingSystems() возвращает массив систем счисления, которые обычно используются в локали. Первый элемент — это система счисления по умолчанию.

const locale = new Intl.Locale("ar-EG");
const numberingSystems = locale.getNumberingSystems();
console.log(numberingSystems);
// ["arab"]

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

В разных локалях используются разные системы счисления по умолчанию:

const usLocale = new Intl.Locale("en-US");
console.log(usLocale.getNumberingSystems());
// ["latn"]

const inLocale = new Intl.Locale("hi-IN");
console.log(inLocale.getNumberingSystems());
// ["latn"]

const thLocale = new Intl.Locale("th-TH");
console.log(thLocale.getNumberingSystems());
// ["latn"]

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

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

Как узнать активный календарь локали

Свойство calendar возвращает календарную систему, явно установленную для локали. Если календарь не указан, возвращается undefined.

const locale = new Intl.Locale("en-US");
console.log(locale.calendar);
// undefined

Базовая локаль без расширения календаря возвращает undefined. В этом случае локаль будет использовать календарь по умолчанию при форматировании дат.

Вы можете создавать локали с явными предпочтениями календаря с помощью расширений Unicode:

const locale = new Intl.Locale("en-US-u-ca-buddhist");
console.log(locale.calendar);
// "buddhist"

Расширение -u-ca-buddhist указывает на буддийский календарь. Свойство calendar возвращает "buddhist".

Вы также можете задать календарь при создании локали:

const locale = new Intl.Locale("en-US", { calendar: "islamic" });
console.log(locale.calendar);
// "islamic"

Объект options имеет приоритет над любым календарём, указанным в строке локали.

Получение активной системы счисления из локали

Свойство numberingSystem возвращает явно установленную для локали систему счисления. Если система счисления не указана, возвращается undefined.

const locale = new Intl.Locale("en-US");
console.log(locale.numberingSystem);
// undefined

Базовая локаль без расширения системы счисления возвращает undefined. Такая локаль будет использовать свою систему счисления по умолчанию при форматировании чисел.

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

const locale = new Intl.Locale("en-US-u-nu-arab");
console.log(locale.numberingSystem);
// "arab"

Расширение -u-nu-arab указывает на арабско-индийские цифры. Свойство numberingSystem возвращает "arab".

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

const locale = new Intl.Locale("ar-SA", { numberingSystem: "latn" });
console.log(locale.numberingSystem);
// "latn"

Это создаёт арабскую локаль, использующую латинские цифры вместо стандартных арабско-индийских.

Определение, задан ли в локали явный календарь

Чтобы проверить, установлен ли в локали явный календарь или используется календарь по умолчанию, проверьте, определено ли свойство calendar:

function hasExplicitCalendar(localeString) {
  const locale = new Intl.Locale(localeString);
  return locale.calendar !== undefined;
}

console.log(hasExplicitCalendar("en-US"));
// false

console.log(hasExplicitCalendar("en-US-u-ca-buddhist"));
// true

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

Определение, задана ли явная система счисления в локали

Аналогично, проверьте, определено ли свойство numberingSystem, чтобы узнать, задана ли явная система счисления:

function hasExplicitNumberingSystem(localeString) {
  const locale = new Intl.Locale(localeString);
  return locale.numberingSystem !== undefined;
}

console.log(hasExplicitNumberingSystem("ar-EG"));
// false

console.log(hasExplicitNumberingSystem("ar-EG-u-nu-latn"));
// true

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

Используйте информацию о календаре для форматирования дат

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

const date = new Date("2025-03-15");

const gregorianLocale = new Intl.Locale("en-US");
const gregorianFormatter = new Intl.DateTimeFormat(gregorianLocale, {
  year: "numeric",
  month: "long",
  day: "numeric"
});
console.log(gregorianFormatter.format(date));
// "March 15, 2025"

const buddhistLocale = new Intl.Locale("th-TH");
const buddhistFormatter = new Intl.DateTimeFormat(buddhistLocale, {
  year: "numeric",
  month: "long",
  day: "numeric"
});
console.log(buddhistFormatter.format(date));
// "15 มีนาคม 2568"

В тайском буддийском календаре та же дата отображается как 2568 год, что на 543 года больше григорианского календаря.

Вы также можете явно переопределить календарь:

const date = new Date("2025-03-15");

const locale = new Intl.Locale("en-US", { calendar: "hebrew" });
const formatter = new Intl.DateTimeFormat(locale, {
  year: "numeric",
  month: "long",
  day: "numeric"
});
console.log(formatter.format(date));
// "15 Adar II 5785"

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

Используйте информацию о системе счисления для форматирования чисел

Применяйте информацию о системе счисления при форматировании чисел с помощью Intl.NumberFormat:

const number = 123456;

const latinLocale = new Intl.Locale("ar-EG", { numberingSystem: "latn" });
const latinFormatter = new Intl.NumberFormat(latinLocale);
console.log(latinFormatter.format(number));
// "123,456"

const arabicLocale = new Intl.Locale("ar-EG", { numberingSystem: "arab" });
const arabicFormatter = new Intl.NumberFormat(arabicLocale);
console.log(arabicFormatter.format(number));
// "١٢٣٬٤٥٦"

Одно и то же число отображается разными символами цифр в зависимости от системы счисления.

Создайте селектор календаря

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

function getCalendarOptions(localeString) {
  const locale = new Intl.Locale(localeString);
  const calendars = locale.getCalendars();

  return calendars.map(calendar => ({
    value: calendar,
    label: calendar.charAt(0).toUpperCase() + calendar.slice(1)
  }));
}

const options = getCalendarOptions("ar-SA");
console.log(options);
// [
//   { value: "gregory", label: "Gregory" },
//   { value: "islamic", label: "Islamic" },
//   { value: "islamic-civil", label: "Islamic-civil" }
// ]

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

Создайте селектор системы счисления

Создайте интерфейс для выбора предпочтительной системы счисления:

function getNumberingSystemOptions(localeString) {
  const locale = new Intl.Locale(localeString);
  const systems = locale.getNumberingSystems();

  const labels = {
    latn: "Western (0-9)",
    arab: "Arabic-Indic (٠-٩)",
    arabext: "Eastern Arabic (۰-۹)",
    deva: "Devanagari (०-९)",
    beng: "Bengali (০-৯)"
  };

  return systems.map(system => ({
    value: system,
    label: labels[system] || system
  }));
}

const options = getNumberingSystemOptions("ar-EG");
console.log(options);
// [{ value: "arab", label: "Arabic-Indic (٠-٩)" }]

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

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

Перед применением календаря к локали убедитесь, что локаль поддерживает этот календарь:

function supportsCalendar(localeString, calendar) {
  const locale = new Intl.Locale(localeString);
  const supportedCalendars = locale.getCalendars();
  return supportedCalendars.includes(calendar);
}

console.log(supportsCalendar("th-TH", "buddhist"));
// true

console.log(supportsCalendar("en-US", "buddhist"));
// false

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

Проверьте совместимость системы счисления

Проверьте, поддерживает ли локаль определённую систему счисления:

function supportsNumberingSystem(localeString, numberingSystem) {
  const locale = new Intl.Locale(localeString);
  const supportedSystems = locale.getNumberingSystems();
  return supportedSystems.includes(numberingSystem);
}

console.log(supportsNumberingSystem("ar-EG", "arab"));
// true

console.log(supportsNumberingSystem("en-US", "arab"));
// false

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

Определение используемого календаря для форматирования

При форматировании дат определите, какой календарь будет фактически использоваться:

function getEffectiveCalendar(localeString) {
  const locale = new Intl.Locale(localeString);

  if (locale.calendar) {
    return locale.calendar;
  }

  const defaultCalendars = locale.getCalendars();
  return defaultCalendars[0];
}

console.log(getEffectiveCalendar("th-TH"));
// "buddhist"

console.log(getEffectiveCalendar("en-US-u-ca-islamic"));
// "islamic"

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

Определение используемой системы счисления для форматирования

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

function getEffectiveNumberingSystem(localeString) {
  const locale = new Intl.Locale(localeString);

  if (locale.numberingSystem) {
    return locale.numberingSystem;
  }

  const defaultSystems = locale.getNumberingSystems();
  return defaultSystems[0];
}

console.log(getEffectiveNumberingSystem("ar-EG"));
// "arab"

console.log(getEffectiveNumberingSystem("ar-EG-u-nu-latn"));
// "latn"

Возвращается явно установленная система счисления, если она есть, иначе — система по умолчанию для локали.

Сохранение пользовательских настроек календаря

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

function setUserCalendarPreference(baseLocale, calendar) {
  const locale = new Intl.Locale(baseLocale, { calendar });
  return locale.toString();
}

const preference = setUserCalendarPreference("en-US", "buddhist");
console.log(preference);
// "en-US-u-ca-buddhist"

Это создаёт полную строку локали, в которой сохраняется выбранный календарь. Сохраняйте эту строку в настройках пользователя или в cookies.

Сохранение пользовательских настроек системы счисления

Сохраняйте предпочтения системы счисления аналогичным образом:

function setUserNumberingPreference(baseLocale, numberingSystem) {
  const locale = new Intl.Locale(baseLocale, { numberingSystem });
  return locale.toString();
}

const preference = setUserNumberingPreference("ar-SA", "latn");
console.log(preference);
// "ar-SA-u-nu-latn"

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

Одновременная работа с несколькими предпочтениями

Пользователь может выбрать и календарь, и систему счисления одновременно:

function createLocaleWithPreferences(baseLocale, options) {
  const locale = new Intl.Locale(baseLocale, {
    calendar: options.calendar,
    numberingSystem: options.numberingSystem
  });
  return locale.toString();
}

const preference = createLocaleWithPreferences("ar-SA", {
  calendar: "islamic",
  numberingSystem: "latn"
});
console.log(preference);
// "ar-SA-u-ca-islamic-nu-latn"

Это объединяет несколько предпочтений форматирования в одну строку локали.

Проверка итоговых параметров форматирования

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

const locale = new Intl.Locale("th-TH");
const formatter = new Intl.DateTimeFormat(locale, {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const options = formatter.resolvedOptions();
console.log(options.calendar);
// "buddhist"
console.log(options.numberingSystem);
// "latn"

Метод resolvedOptions() показывает, какой календарь и систему счисления на самом деле использует форматтер после применения значений по умолчанию и пользовательских настроек.

Поддержка браузеров

API Intl.Locale поддерживается во всех современных браузерах. Методы getCalendars() и getNumberingSystems() требуют более новых версий браузеров. Chrome 99, Firefox 99, Safari 15.4 и Edge 99 поддерживают эти методы. Node.js поддерживает их начиная с версии 18.

Свойства calendar и numberingSystem имеют более широкую поддержку и доступны с момента появления Intl.Locale в Chrome 74, Firefox 75, Safari 14 и Node.js 12.

Проверьте поддержку метода перед использованием:

const locale = new Intl.Locale("th-TH");

if (typeof locale.getCalendars === "function") {
  const calendars = locale.getCalendars();
  console.log(calendars);
}

Это гарантирует, что ваш код будет работать в средах, где поддерживается Intl.Locale, но отсутствуют новые методы.

Итоги

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

Ключевые моменты:

  • Используйте getCalendars(), чтобы получить массив часто используемых календарей для локали
  • Используйте getNumberingSystems(), чтобы получить массив часто используемых систем счисления для локали
  • Свойство calendar возвращает явно установленный календарь или undefined
  • Свойство numberingSystem возвращает явно установленную систему счисления или undefined
  • В разных локалях по умолчанию используются разные календари и системы счисления
  • Учитывайте информацию о календаре и системе счисления при создании форматтеров
  • Сохраняйте пользовательские настройки в виде строк локали с Unicode-расширениями
  • Проверяйте, поддерживает ли локаль определённые календари и системы счисления перед их применением

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