Intl.Locale API

ロケール識別子を構造化されたJavaScriptオブジェクトで解析・操作・検索する

はじめに

複数の言語や地域に対応したアプリケーションを開発すると、en-USfr-FRzh-Hans-CN のようなロケール識別子によく遭遇します。これらの文字列はブラウザAPIやHTTPヘッダー、ユーザー設定などに現れ、言語・地域・文字体系・書式設定の優先情報を含んでいます。

Intl.Locale API を使えば、こうした分かりにくい文字列を簡単に解析して、扱いやすい構造化オブジェクトとして扱えます。手動で文字列を解析したり、zh-Hans-CN が何を意味するか推測する代わりに、ロケールオブジェクトを生成してそのプロパティを直接確認できるようになります。

このガイドでは、ロケール識別子の仕組みや、Intl.Locale API を用いた実践方法、構造化ロケールオブジェクトが役立つ場面について解説します。

ロケール識別子の理解

ロケール識別子とは、日付・数値・通貨・テキストなどの書式設定に関する文化的な優先情報を指定する文字列です。この識別子はハイフンで区切られた複数の要素から構成されます。

もっとも一般的な形式は BCP 47 標準に従っています。

language-script-region-variant-extensions

言語コード以外の要素はすべて省略可能です。

言語コード

言語コードは ISO 639 で定められた2文字または3文字の英字を使います。例としては次の通りです:

  • en:英語
  • es:スペイン語
  • fr:フランス語
  • de:ドイツ語
  • ja:日本語
  • zh:中国語
  • ar:アラビア語

言語コードは必ず小文字で、識別子の最初に記載されます。

地域コード

地域コードは、ISO 3166-1の2文字大文字で地理的なエリアを示し、どのバリアントの言語を使用するかを表します:

  • en-US:アメリカ英語
  • en-GB:イギリス英語
  • es-ES:ヨーロッパスペイン語
  • es-MX:メキシコスペイン語
  • fr-FR:フランス(フランス語)
  • fr-CA:カナダ(フランス語)

リージョンコードによって日付などのフォーマット規則が変わります。アメリカ英語ではMM/DD/YYYYの日付表記、イギリス英語ではDD/MM/YYYYが使われます。

スクリプトコード

スクリプトコードは、4文字で構成され、最初の文字を大文字にすることで文字体系を指定します。1つの言語が複数の文字体系で書かれる場合に重要です:

  • 簡体字中国語にはzh-Hans
  • 繁体字中国語にはzh-Hant
  • セルビア語のキリル文字にはsr-Cyrl
  • セルビア語のラテン文字にはsr-Latn

ほとんどのロケールでは、言語によって標準のスクリプトが決まっているため、スクリプトコードは省略されます。たとえば英語は標準でラテン文字を使用するため、en と書くだけでよく、en-Latn と表記する必要はありません。

拡張タグ

拡張タグはロケール識別子にフォーマット設定を追加できます。-u-で始まり、その後にキーと値のペアが続きます:

en-US-u-ca-gregory-nu-latn-hc-h12

主な拡張キー:

  • カレンダー方式にはca(例:gregorybuddhistislamic
  • 数字方式にはnu(例:latnarabhanidec
  • 時刻表記にはhc(例:h12h23h11h24

拡張タグを使うと、言語や地域はそのままに、書式の表示方法だけをカスタマイズできます。

ロケールオブジェクトの作成

Intl.Localeコンストラクターはロケール識別子の文字列を受け取り、構造化されたオブジェクトを返します:

const locale = new Intl.Locale("en-US");

console.log(locale.language); // "en"
console.log(locale.region); // "US"

オプションオブジェクトを渡して、プロパティを上書きしたり追加したりすることもできます:

const locale = new Intl.Locale("en", {
  region: "GB",
  hourCycle: "h23"
});

console.log(locale.baseName); // "en-GB"
console.log(locale.hourCycle); // "h23"

識別子が無効な場合、このコンストラクターはRangeErrorをスローします:

try {
  const invalid = new Intl.Locale("invalid-locale-code");
} catch (error) {
  console.error(error.message); // "invalid language subtag: invalid"
}

このバリデーションによって、不正なロケール識別子をフォーマッターに渡す前に検出できます。

ロケールプロパティの取得

ロケールオブジェクトは、識別子文字列の要素に対応するプロパティを持っています。すべて読み取り専用です。

コアプロパティ

languageプロパティは言語コードを返します:

const locale = new Intl.Locale("fr-CA");
console.log(locale.language); // "fr"

regionプロパティはリージョンコードを返します:

const locale = new Intl.Locale("fr-CA");
console.log(locale.region); // "CA"

script プロパティは、スクリプトコードが存在する場合にその値を返します:

const locale = new Intl.Locale("zh-Hans-CN");
console.log(locale.script); // "Hans"

baseName プロパティは、拡張子を除いた完全なコア識別子を返します:

const locale = new Intl.Locale("en-US-u-ca-gregory-nu-latn");
console.log(locale.baseName); // "en-US"

言語と地域が必要で、書式設定の好みを無視したい場合は、baseName を使ってください。

拡張プロパティ

拡張プロパティは、-u- 拡張タグ、もしくは指定がない場合は undefined の値を返します。

calendar プロパティは、カレンダーシステムを返します:

const locale = new Intl.Locale("ar-SA-u-ca-islamic");
console.log(locale.calendar); // "islamic"

numberingSystem プロパティは、数字表記体系を返します:

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

hourCycle プロパティは、時間表記方式(時間サイクル)の好みを返します:

const locale = new Intl.Locale("en-US-u-hc-h23");
console.log(locale.hourCycle); // "h23"

caseFirst プロパティは、大文字小文字の照合(コレーション)の好みを返します:

const locale = new Intl.Locale("en-US-u-kf-upper");
console.log(locale.caseFirst); // "upper"

numeric プロパティは、数値コレーションが有効かどうかを判定します:

const locale = new Intl.Locale("en-US-u-kn-true");
console.log(locale.numeric); // true

これらのプロパティを使うと、拡張文字列を手動で解析することなく、書式設定の好みを確認できます。

ロケール情報のクエリ

Intl.Locale API には、ロケールで利用可能な選択肢の配列を返すメソッドがあります。これらのメソッドは、ユーザーインターフェース作成や書式選択の検証に役立ちます。

利用可能なカレンダー

getCalendars() メソッドは、指定したロケールで一般的に使われるカレンダーシステムを返します:

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

最初の要素がデフォルトのカレンダーです。タイのロケールでは仏暦がデフォルトですが、グレゴリオ暦も使用されます。

利用可能な照合順序

getCollations() メソッドは、文字列のソートに使われる照合順序タイプを返します。

const locale = new Intl.Locale("de-DE");
const collations = locale.getCollations();
console.log(collations); // ["phonebk", "emoji", "eor"]

ドイツ語には、標準的なUnicode照合順序とは異なる、電話帳用の照合順序があります。

利用可能な時刻表記

getHourCycles() メソッドは、時刻表記形式を返します。

const locale = new Intl.Locale("en-US");
const hourCycles = locale.getHourCycles();
console.log(hourCycles); // ["h12"]

アメリカ英語ではデフォルトで12時間表記を使用します。その他多くのロケールでは24時間表記として["h23"]が返されます。

利用可能な数字体系

getNumberingSystems() メソッドは、ロケールで一般的に使われる数字体系を返します。

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

アラビア語のロケールでは、アラビア数字(インド数字)とラテン数字の両方がよくサポートされています。

文字方向

getTextInfo() メソッドは、テキストの並び順情報を返します。

const locale = new Intl.Locale("ar-SA");
const textInfo = locale.getTextInfo();
console.log(textInfo.direction); // "rtl"

アラビア語やヘブライ語のような右から左への言語は"rtl"を返します。左から右への言語は"ltr"を返します。

週の慣習

getWeekInfo() メソッドは、ロケールの週の構成情報を返します。

const locale = new Intl.Locale("en-US");
const weekInfo = locale.getWeekInfo();
console.log(weekInfo.firstDay); // 7 (Sunday)
console.log(weekInfo.weekend); // [6, 7] (Saturday, Sunday)
console.log(weekInfo.minimalDays); // 1

週の慣習は地域によって異なります。アメリカでは日曜日から始まり、ヨーロッパの多くでは月曜日から始まります。

タイムゾーン

getTimeZones() メソッドは、そのロケール地域に該当するタイムゾーンを返します。

const locale = new Intl.Locale("en-US");
const timeZones = locale.getTimeZones();
console.log(timeZones); // ["America/New_York", "America/Chicago", ...]

このメソッドは、地域コードに対応するIANAタイムゾーン識別子を返します。

ロケール識別子の最大化

maximize() メソッドは、完全な識別子を作成するために推定サブタグを追加します。言語データに基づいて、スクリプトや地域コードの不足分を補います。

言語コードのみを指定した場合、maximize() は最も一般的なスクリプトと地域を追加します:

const locale = new Intl.Locale("en");
const maximized = locale.maximize();
console.log(maximized.baseName); // "en-Latn-US"

英語の場合、最もよく使われる組み合わせであるため、標準でラテン文字スクリプトとアメリカ合衆国の地域が選ばれます。

中国語の場合は、使われるスクリプトによって最大化される内容が異なります:

const simplified = new Intl.Locale("zh-Hans");
const maximized = simplified.maximize();
console.log(maximized.baseName); // "zh-Hans-CN"

const traditional = new Intl.Locale("zh-Hant");
const maximizedTrad = traditional.maximize();
console.log(maximizedTrad.baseName); // "zh-Hant-TW"

簡体字中国語は中国と結びつき、繁体字中国語は台湾と結びつきます。

ユーザー入力の正規化やロケール識別子の比較を行う場合は、maximize() を使用してください。暗黙的な情報を明示化できます。

ロケール識別子の最小化

minimize() メソッドは、冗長なサブタグを削除し、最短の同等識別子を作成します。スクリプトや地域コードがデフォルトの可能性がある場合、それらを取り除きます。

ロケールがデフォルトのスクリプトと地域を使っている場合、minimize() はそれらを削除します:

const locale = new Intl.Locale("en-Latn-US");
const minimized = locale.minimize();
console.log(minimized.baseName); // "en"

英語の場合、ラテン文字スクリプトとアメリカ合衆国の地域がデフォルトなので、情報を失わずに省略できます。

デフォルトでない地域が使われているロケールでは、minimize() は地域コードを保持します:

const locale = new Intl.Locale("en-Latn-GB");
const minimized = locale.minimize();
console.log(minimized.baseName); // "en-GB"

イギリス英語の場合、デフォルトとは異なるため、地域コードが必要です。

ストレージやURL用に意味を損なわずコンパクトな識別子を作成したい場合は、minimize() を利用してください。

文字列への変換

toString() メソッドは、拡張情報も含めた完全なロケール識別子の文字列を返します:

const locale = new Intl.Locale("en-US", {
  calendar: "gregory",
  numberingSystem: "latn",
  hourCycle: "h12"
});

console.log(locale.toString()); // "en-US-u-ca-gregory-hc-h12-nu-latn"

この文字列表現は、他の Intl API への受け渡しや、設定データとして保存するときに有効です。

ロケールオブジェクトを暗黙的に文字列へ変換することもできます:

const locale = new Intl.Locale("fr-FR");
const formatter = new Intl.DateTimeFormat(locale);

DateTimeFormat コンストラクタはロケールオブジェクトを直接受け取り、内部的に toString() を呼び出します。

実際の使用例

Intl.Locale API は、国際化対応アプリケーションの開発でよくある問題をいくつか解決します。

ロケールセレクターの構築

ロケールセレクターを使うと、ユーザーは言語と地域を選択できます。Intl.Locale API はユーザーが選択した内容を解析・検証するのに役立ちます:

function createLocaleSelector(locales) {
  const options = locales.map(identifier => {
    const locale = new Intl.Locale(identifier);
    const displayName = new Intl.DisplayNames([identifier], { type: "language" });

    return {
      value: locale.toString(),
      label: displayName.of(locale.language),
      region: locale.region
    };
  });

  return options;
}

const options = createLocaleSelector(["en-US", "en-GB", "fr-FR", "es-MX"]);
console.log(options);
// [
//   { value: "en-US", label: "English", region: "US" },
//   { value: "en-GB", label: "English", region: "GB" },
//   { value: "fr-FR", label: "French", region: "FR" },
//   { value: "es-MX", label: "Spanish", region: "MX" }
// ]

ロケール入力の検証

ユーザー入力や設定ファイルからロケール識別子を受け取る際は、使用前に検証してください:

function validateLocale(identifier) {
  try {
    const locale = new Intl.Locale(identifier);
    return {
      valid: true,
      locale: locale.toString(),
      language: locale.language,
      region: locale.region
    };
  } catch (error) {
    return {
      valid: false,
      error: error.message
    };
  }
}

console.log(validateLocale("en-US")); // { valid: true, locale: "en-US", ... }
console.log(validateLocale("invalid")); // { valid: false, error: "..." }

フォールバック用の言語抽出

言語フォールバックチェーンを実装する際は、地域を除いた言語コードを取り出します:

function getLanguageFallback(identifier) {
  const locale = new Intl.Locale(identifier);
  const fallbacks = [locale.toString()];

  if (locale.region) {
    const languageOnly = new Intl.Locale(locale.language);
    fallbacks.push(languageOnly.toString());
  }

  fallbacks.push("en");

  return fallbacks;
}

console.log(getLanguageFallback("fr-CA"));
// ["fr-CA", "fr", "en"]

これにより、まず特定のロケールを試し、それがなければ地域なしの言語、さらにデフォルト言語の順にフォールバックするチェーンを作成できます。

フォーマッターの設定

ロケールオブジェクトを使って、特定の設定で Intl フォーマッターを構成しましょう:

function createFormatter(baseLocale, options = {}) {
  const locale = new Intl.Locale(baseLocale, {
    calendar: options.calendar || "gregory",
    numberingSystem: options.numberingSystem || "latn",
    hourCycle: options.hourCycle || "h23"
  });

  return {
    date: new Intl.DateTimeFormat(locale),
    number: new Intl.NumberFormat(locale),
    locale: locale.toString()
  };
}

const formatter = createFormatter("ar-SA", {
  calendar: "islamic",
  numberingSystem: "arab"
});

console.log(formatter.locale); // "ar-SA-u-ca-islamic-nu-arab"

ユーザーの好みを検出する

ブラウザーの API からロケール文字列を取得し、ユーザーの好みを把握できます:

function getUserPreferences() {
  const userLocale = new Intl.Locale(navigator.language);
  const hourCycles = userLocale.getHourCycles();
  const calendars = userLocale.getCalendars();
  const textInfo = userLocale.getTextInfo();

  return {
    language: userLocale.language,
    region: userLocale.region,
    preferredHourCycle: hourCycles[0],
    preferredCalendar: calendars[0],
    textDirection: textInfo.direction
  };
}

const preferences = getUserPreferences();
console.log(preferences);
// { language: "en", region: "US", preferredHourCycle: "h12", ... }

これにより、ブラウザー設定にもとづいたユーザーの好みのプロファイルを作ることができます。

ブラウザー対応

Intl.Locale API は、すべての最新ブラウザーで利用できます。Chrome、Firefox、Safari、Edge では、本ガイドで紹介したコンストラクターやプロパティ、メソッドがすべてサポートされています。

新しいメソッド(getCalendars()getCollations()getHourCycles()getNumberingSystems()getTextInfo()getTimeZones()getWeekInfo() など)は、最近のブラウザーバージョンが必要です。古いブラウザーをサポートする場合は、これらのメソッドを使う前にブラウザー対応表を確認してください。

Node.js では、バージョン 12 から Intl.Locale API が使えます。メソッド全体のサポートはバージョン 18 以降です。

まとめ

Intl.Locale API は、ロケール識別子の文字列を、調査や操作が可能な構造化オブジェクトに変換します。文字列を手作業で解析するのではなく、ロケールオブジェクトを作成し、そのプロパティを読み取ります。

主なポイント:

  • ロケール識別子は、言語、スクリプト、地域、拡張要素で構成されています
  • Intl.Locale コンストラクタは識別子の検証とオブジェクトの生成を行います
  • languageregioncalendar などのプロパティで構造化されたアクセスが可能です
  • getCalendars()getHourCycles() などのメソッドで利用可能なオプションを取得できます
  • maximize()minimize() の各メソッドは識別子を正規化します
  • ロケールオブジェクトは他の Intl APIとも連携できます

ロケールセレクターの作成、ユーザー入力の検証、フォールバックチェーンの実装、フォーマッターの設定時などに Intl.Locale API をご活用ください。JavaScript アプリケーションでロケール識別子を標準的に扱う方法を提供します。