ロケールが使用する暦法や数字体系を確認する方法

JavaScriptを使用して、任意のロケールの暦法と数字形式を検出および検証する

はじめに

タイの方があなたのWebアプリで日付を表示する際、西洋諸国で使用されているグレゴリオ暦ではなく、仏暦での表示を期待します。同様に、アラビア語話者は数字が123ではなく١٢٣として表示されることを期待します。文化によって使用する暦法や数字体系が異なり、JavaScriptは特定のロケールに適用されるものを検出するツールを提供しています。

Intl.Locale APIには、ロケールが使用する暦法と数字体系を明らかにするプロパティとメソッドが含まれています。この情報により、異なる文化がどの体系を好むかについての仮定をハードコーディングすることなく、日付と数字を正しくフォーマットできます。

このガイドでは、ロケールの暦法と数字体系を確認する方法、異なる体系が何を意味するかを理解する方法、およびこの情報を使用してコンテンツを適切にフォーマットする方法について説明します。

暦法の理解

暦法とは、時間を年、月、日に整理する方法です。グレゴリオ暦は広く普及していますが、多くの文化では宗教的、歴史的、または文化的な目的で異なる暦法を使用しています。

最も一般的な暦法には以下が含まれます:

  • 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

最初のロケールは、エジプトアラビア語のデフォルトの数値システムを使用します。2番目のロケールは、明示的にラテン数字を要求します。

カレンダー情報を使用して日付をフォーマットする

ロケールが使用するカレンダーを特定したら、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"

これにより、暦の設定を保持する完全なロケール文字列が作成されます。この文字列をユーザー設定またはCookieに保存します。

ユーザーの数字体系設定を保存する

数字体系の設定も同じ方法で保存します:

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()メソッドは、デフォルトとユーザー設定を解決した後、フォーマッターが実際に使用する暦法と数字体系を表示します。

ブラウザサポート

Intl.Locale APIは、すべてのモダンブラウザでサポートされています。getCalendars()およびgetNumberingSystems()メソッドには、最新のブラウザバージョンが必要です。Chrome 99、Firefox 99、Safari 15.4、Edge 99がこれらのメソッドをサポートしています。Node.jsでは、バージョン18以降でサポートされています。

calendarおよびnumberingSystemプロパティは、より広範なサポートがあり、Chrome 74、Firefox 75、Safari 14、Node.js 12でIntl.Localeが導入されて以来利用可能です。

使用前にメソッドのサポートを確認してください:

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

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

これにより、Intl.Localeをサポートしているが、新しいメソッドがない環境でもコードが動作することが保証されます。

まとめ

JavaScriptは、Intl.Locale APIを通じて、ロケールが使用する暦法と数字体系を検出するツールを提供します。これらのツールは、仮定をハードコーディングすることなく、異なる文化に対して日付と数値を正しくフォーマットするのに役立ちます。

主要な概念:

  • getCalendars()を使用して、ロケールで一般的に使用される暦法の配列を取得します
  • getNumberingSystems()を使用して、ロケールで一般的に使用される数字体系の配列を取得します
  • calendarプロパティは、明示的に設定された暦法またはundefinedを返します
  • numberingSystemプロパティは、明示的に設定された数字体系またはundefinedを返します
  • 異なるロケールは、異なる暦法と数字体系をデフォルトとします
  • フォーマッターを作成する際に、暦法と数字体系の情報を適用します
  • ユーザー設定をUnicode拡張を含むロケール文字列として保存します
  • 適用する前に、ロケールが特定の暦法と数字体系をサポートしていることを検証します

ロケールセレクターの構築、ユーザー設定の保存、書式設定オプションの検証、または日付や数値の文化的慣習を尊重する国際化アプリケーションの作成時に、これらのメソッドを使用してください。