ロケールが使用するカレンダーや数字体系を確認する方法

JavaScriptを使用して任意のロケールのカレンダーシステムと数字形式を検出および検証する

はじめに

タイの人があなたのウェブアプリで日付を見る場合、西洋諸国で使用されるグレゴリオ暦ではなく、仏暦で日付が表示されることを期待しています。同様に、アラビア語話者は数字が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"

オプションオブジェクトはロケール文字列で指定されたカレンダーよりも優先されます。

ロケールからアクティブな数字体系を読み取る

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: "欧米式 (0-9)",
    arab: "アラビア数字 (٠-٩)",
    arabext: "東アラビア数字 (۰-۹)",
    deva: "デーヴァナーガリー数字 (०-९)",
    beng: "ベンガル数字 (০-৯)"
  };

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

const options = getNumberingSystemOptions("ar-EG");
console.log(options);
// [{ value: "arab", label: "アラビア数字 (٠-٩)" }]

これにより、各選択肢がどのように見えるかを示す明確なラベルが提供され、ユーザーに数字体系のオプションが表示されます。

カレンダー互換性の検証

ロケールにカレンダーを適用する前に、そのロケールがそのカレンダーをサポートしているか確認します:

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"

これによりカレンダー設定を保持する完全なロケール文字列が作成されます。この文字列をユーザー設定やクッキーに保存します。

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

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

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拡張子付きのロケール文字列として保存する
  • 特定のカレンダーと数字体系をロケールが対応しているか適用前に検証する

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