コンポーネントからロケール識別子を構築する方法

JavaScriptで言語、文字体系、地域コードを組み合わせてロケール識別子を構築する

はじめに

ロケール識別子(en-USzh-Hans-CNなど)は、言語、文字体系、地域に関する情報をエンコードします。固定文字列を使用するのではなく、これらの識別子をプログラムで構築する必要がある場合があります。たとえば、ユーザーに言語と地域を個別に選択させ、それらを有効なロケール識別子に結合することができます。

JavaScriptのIntl.Localeコンストラクタを使用すると、個別のコンポーネントからロケール識別子を構築できます。言語、文字体系、地域を個別のパラメータとして指定でき、コンストラクタはそれらを適切にフォーマットされた識別子に組み立てます。

このガイドでは、コンポーネントからロケール識別子を構築する方法、このアプローチを使用するタイミング、エッジケースの処理方法について説明します。

ロケール識別子のコンポーネントを理解する

ロケール識別子は、ハイフンで区切られたコンポーネントで構成されます。各コンポーネントは、文化的な設定の異なる側面を表します。

言語コードは、使用する言語を指定します。ISO 639の2文字または3文字の小文字を使用します。

  • enは英語
  • esはスペイン語
  • frはフランス語
  • zhは中国語
  • arはアラビア語

文字体系コードは、書記体系を指定します。ISO 15924の最初の文字が大文字の4文字を使用します。

  • Hansは簡体字中国語
  • Hantは繁体字中国語
  • Cyrlはキリル文字
  • Latnはラテン文字

地域コードは、地理的な地域を指定します。ISO 3166-1の2文字の大文字、またはUN M.49の3桁の数字を使用します。

  • USはアメリカ合衆国
  • GBはイギリス
  • CNは中国
  • MXはメキシコ

これらのコンポーネントは特定の順序で組み合わされます。言語、次に文字体系、そして地域の順です。例えば、zh-Hans-CNは中国語、簡体字、中国地域を意味します。

言語と地域のみでロケールを構築する

最も一般的なシナリオは、言語コードと地域コードを組み合わせることです。ほとんどのアプリケーションでは、各言語にデフォルトの文字体系があるため、文字体系を指定する必要はありません。

最初の引数として言語コードを渡し、地域を含むオプションオブジェクトを渡すことで、ロケールを構築できます。

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

console.log(locale.toString());
// Output: "en-US"

これにより、アメリカ英語のロケール識別子が作成されます。

同じ言語の異なる地域バリアントを構築できます。

const usEnglish = new Intl.Locale("en", { region: "US" });
const britishEnglish = new Intl.Locale("en", { region: "GB" });
const canadianEnglish = new Intl.Locale("en", { region: "CA" });

console.log(usEnglish.toString()); // "en-US"
console.log(britishEnglish.toString()); // "en-GB"
console.log(canadianEnglish.toString()); // "en-CA"

各バリアントは同じ言語を使用しますが、異なる地域のフォーマット規則を使用します。

言語、文字体系、地域でロケールを構築する

一部の言語では、明示的な文字体系コードが必要です。中国語、セルビア語、その他いくつかの言語は複数の書記体系を使用します。曖昧さを避けるために文字体系を指定する必要があります。

オプションオブジェクトに文字体系コンポーネントを追加できます。

const simplifiedChinese = new Intl.Locale("zh", {
  script: "Hans",
  region: "CN"
});

console.log(simplifiedChinese.toString());
// Output: "zh-Hans-CN"

これにより、中国で使用される簡体字中国語のロケールが作成されます。

繁体字中国語は異なる文字体系と地域を使用します。

const traditionalChinese = new Intl.Locale("zh", {
  script: "Hant",
  region: "TW"
});

console.log(traditionalChinese.toString());
// Output: "zh-Hant-TW"

文字体系コードは2つの書記体系を区別します。

セルビア語はキリル文字とラテン文字の両方を使用します。どちらの文字体系を使用するかを指定する必要があります。

const serbianCyrillic = new Intl.Locale("sr", {
  script: "Cyrl",
  region: "RS"
});

const serbianLatin = new Intl.Locale("sr", {
  script: "Latn",
  region: "RS"
});

console.log(serbianCyrillic.toString()); // "sr-Cyrl-RS"
console.log(serbianLatin.toString()); // "sr-Latn-RS"

両方のロケールはセルビアでセルビア語を使用しますが、異なる書記体系を使用します。

ユーザー選択からロケールを構築する

ユーザーインターフェースでは、言語と地域を個別に選択できることがよくあります。これらの選択を組み合わせてロケール識別子を作成できます。

2つのドロップダウンメニューを持つ設定フォームを考えてみましょう。

function buildLocaleFromSelections(languageCode, regionCode) {
  const locale = new Intl.Locale(languageCode, {
    region: regionCode
  });

  return locale.toString();
}

const userLocale = buildLocaleFromSelections("es", "MX");
console.log(userLocale);
// Output: "es-MX"

これにより、独立した選択からロケール識別子が作成されます。

コンストラクタからのエラーをキャッチすることで、選択を検証できます。

function buildLocaleFromSelections(languageCode, regionCode) {
  try {
    const locale = new Intl.Locale(languageCode, {
      region: regionCode
    });
    return {
      success: true,
      locale: locale.toString()
    };
  } catch (error) {
    return {
      success: false,
      error: error.message
    };
  }
}

const valid = buildLocaleFromSelections("fr", "CA");
console.log(valid);
// Output: { success: true, locale: "fr-CA" }

const invalid = buildLocaleFromSelections("invalid", "XX");
console.log(invalid);
// Output: { success: false, error: "..." }

コンストラクタは、いずれかのコンポーネントが無効な場合にRangeErrorをスローします。

オプションのコンポーネントを使用したロケールの構築

すべてのロケールにすべてのコンポーネントが必要なわけではありません。必要のないコンポーネントは省略できます。

言語のみのロケールでは、地域とスクリプトを省略します。

const locale = new Intl.Locale("fr");
console.log(locale.toString());
// Output: "fr"

これは、特定の地域やスクリプトを指定せずにフランス語を表します。

ユーザー入力に基づいて、条件付きでコンポーネントを含めることができます。

function buildLocale(language, options = {}) {
  const localeOptions = {};

  if (options.region) {
    localeOptions.region = options.region;
  }

  if (options.script) {
    localeOptions.script = options.script;
  }

  const locale = new Intl.Locale(language, localeOptions);
  return locale.toString();
}

console.log(buildLocale("en"));
// Output: "en"

console.log(buildLocale("en", { region: "US" }));
// Output: "en-US"

console.log(buildLocale("zh", { script: "Hans", region: "CN" }));
// Output: "zh-Hans-CN"

この関数は、利用可能な情報に基づいて最もシンプルな有効なロケール識別子を構築します。

既存のロケールのコンポーネントのオーバーライド

既存のロケール識別子を取得し、特定のコンポーネントをオーバーライドできます。これは、他の部分をそのままにして1つの部分を変更する必要がある場合に便利です。

コンストラクタの2番目の引数は、最初の引数のコンポーネントをオーバーライドします。

const baseLocale = new Intl.Locale("en-US");
const withDifferentRegion = new Intl.Locale(baseLocale, {
  region: "GB"
});

console.log(withDifferentRegion.toString());
// Output: "en-GB"

新しいロケールは言語を保持しますが、地域を変更します。

複数のコンポーネントをオーバーライドできます。

const original = new Intl.Locale("zh-Hans-CN");
const modified = new Intl.Locale(original, {
  script: "Hant",
  region: "TW"
});

console.log(modified.toString());
// Output: "zh-Hant-TW"

これにより、言語を保持しながら、スクリプトと地域の両方が変更されます。

構築されたロケールへのフォーマット設定の追加

言語、スクリプト、地域に加えて、ロケールにはフォーマット設定を含めることができます。これらの設定は、日付、数値、その他の値の表示方法を制御します。

ロケールを構築する際にカレンダーの設定を追加できます。

const locale = new Intl.Locale("ar", {
  region: "SA",
  calendar: "islamic"
});

console.log(locale.toString());
// Output: "ar-SA-u-ca-islamic"

console.log(locale.calendar);
// Output: "islamic"

カレンダーの設定は、識別子文字列内にUnicode拡張として表示されます。

複数のフォーマット設定を指定できます。

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

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

コンストラクタは拡張キーをアルファベット順に並べます。

これらの設定は、フォーマッタがデータを表示する方法に影響します。

const locale = new Intl.Locale("ar", {
  region: "EG",
  numberingSystem: "arab"
});

const formatter = new Intl.NumberFormat(locale);
console.log(formatter.format(12345));
// Output: "١٢٬٣٤٥" (Arabic-Indic numerals)

数字体系の設定は、表示される数字を制御します。

コンポーネントの組み合わせの検証

言語、文字体系、地域のすべての組み合わせが意味を持つわけではありません。コンストラクタは構文的に有効なコンポーネントを受け入れますが、一部の組み合わせは実際のロケールを表さない場合があります。

コンストラクタは構文を検証しますが、意味的な正確性は検証しません。

// Syntactically valid but semantically questionable
const locale = new Intl.Locale("en", {
  script: "Arab",
  region: "JP"
});

console.log(locale.toString());
// Output: "en-Arab-JP"

これは、日本におけるアラビア文字の英語のロケールを構築します。識別子はBCP 47に従って有効ですが、実世界のロケールを表していません。

maximize()メソッドを使用して、ロケールが一般的なパターンと一致するかどうかを確認できます。

const locale = new Intl.Locale("en", { region: "JP" });
const maximized = locale.maximize();

console.log(maximized.toString());
// Output: "en-Latn-JP"

このメソッドは、言語に最も可能性の高い文字体系を追加します。結果が期待されるパターンと一致する場合、その組み合わせは妥当です。

構築されたロケールからコンポーネントを読み取る

ロケールを構築した後、そのコンポーネントをプロパティとして読み取ることができます。

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

const locale = new Intl.Locale("fr", { region: "CA" });
console.log(locale.language);
// Output: "fr"

regionプロパティは地域コードを返します。

const locale = new Intl.Locale("fr", { region: "CA" });
console.log(locale.region);
// Output: "CA"

scriptプロパティは、指定されている場合は文字体系コードを返します。

const locale = new Intl.Locale("zh", {
  script: "Hans",
  region: "CN"
});

console.log(locale.script);
// Output: "Hans"

スクリプトが指定されていない場合、プロパティはundefinedを返します:

const locale = new Intl.Locale("en", { region: "US" });
console.log(locale.script);
// Output: undefined

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

const locale = new Intl.Locale("ar", {
  region: "SA",
  calendar: "islamic",
  numberingSystem: "arab"
});

console.log(locale.baseName);
// Output: "ar-SA"

これにより、書式設定の設定を除いた言語-スクリプト-地域の部分が取得されます。

ロケール識別子を文字列に変換する

toString()メソッドは、完全なロケール識別子を文字列として返します:

const locale = new Intl.Locale("es", { region: "MX" });
const identifier = locale.toString();

console.log(identifier);
// Output: "es-MX"

この文字列を他のIntl APIで使用できます:

const locale = new Intl.Locale("de", { region: "DE" });
const formatter = new Intl.NumberFormat(locale.toString());

const price = 1234.56;
console.log(formatter.format(price));
// Output: "1.234,56"

フォーマッターは文字列表現を受け入れます。

ほとんどのIntl APIは、ロケールオブジェクトを直接受け入れることもできます:

const locale = new Intl.Locale("de", { region: "DE" });
const formatter = new Intl.NumberFormat(locale);

APIは必要に応じて内部的にtoString()を呼び出します。

実用的なユースケース

コンポーネントからロケール識別子を構築することで、国際化されたアプリケーションにおけるいくつかの一般的な問題が解決されます。

ロケールピッカーの作成

ユーザーインターフェースでは、ユーザーが言語と地域を個別に選択できることがよくあります。選択内容を組み合わせます:

function createLocaleFromPicker(languageSelect, regionSelect) {
  const language = languageSelect.value;
  const region = regionSelect.value;

  const locale = new Intl.Locale(language, { region });
  return locale.toString();
}

// User selects "Spanish" and "Mexico"
const selectedLocale = createLocaleFromPicker(
  { value: "es" },
  { value: "MX" }
);

console.log(selectedLocale);
// Output: "es-MX"

ロケールバリアントの生成

単一の言語コードから複数の地域バリアントを生成できます:

function generateRegionalVariants(languageCode, regionCodes) {
  return regionCodes.map(regionCode => {
    const locale = new Intl.Locale(languageCode, {
      region: regionCode
    });
    return locale.toString();
  });
}

const englishVariants = generateRegionalVariants("en", [
  "US",
  "GB",
  "CA",
  "AU",
  "NZ"
]);

console.log(englishVariants);
// Output: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ"]

これにより、異なる英語圏地域のロケール識別子のリストが作成されます。

URLパラメータからロケールを構築する

URLでは、ロケール設定を個別のパラメータとしてエンコードすることがよくあります。これらのパラメータからロケールを構築できます:

function getLocaleFromURL(url) {
  const params = new URL(url).searchParams;
  const language = params.get("lang");
  const region = params.get("region");

  if (!language) {
    return null;
  }

  const options = {};
  if (region) {
    options.region = region;
  }

  try {
    const locale = new Intl.Locale(language, options);
    return locale.toString();
  } catch (error) {
    return null;
  }
}

const locale1 = getLocaleFromURL("https://example.com?lang=fr&region=CA");
console.log(locale1);
// Output: "fr-CA"

const locale2 = getLocaleFromURL("https://example.com?lang=ja");
console.log(locale2);
// Output: "ja"

ロケール識別子の正規化

ロケール識別子を解析して再構築することで正規化できます。

function normalizeLocale(identifier) {
  try {
    const locale = new Intl.Locale(identifier);
    return locale.toString();
  } catch (error) {
    return null;
  }
}

console.log(normalizeLocale("EN-us"));
// Output: "en-US"

console.log(normalizeLocale("zh_Hans_CN"));
// Output: null (invalid separator)

コンストラクタは大文字小文字を正規化し、構造を検証します。

ユーザー設定によるフォーマッタの構成

ユーザー設定に基づいて、フォーマット設定を含むロケール識別子を構築できます。

function buildFormatterLocale(language, region, preferences) {
  const locale = new Intl.Locale(language, {
    region,
    hourCycle: preferences.use24Hour ? "h23" : "h12",
    numberingSystem: preferences.numberingSystem
  });

  return locale;
}

const userPreferences = {
  use24Hour: true,
  numberingSystem: "latn"
};

const locale = buildFormatterLocale("fr", "FR", userPreferences);

const timeFormatter = new Intl.DateTimeFormat(locale, {
  hour: "numeric",
  minute: "numeric"
});

const now = new Date("2025-10-15T14:30:00");
console.log(timeFormatter.format(now));
// Output: "14:30" (24-hour format)

ロケールにはユーザー設定からのフォーマット設定が含まれます。

コンポーネントからロケールを構築する場合

コンポーネントからロケールを構築することは、特定のシナリオで有用です。言語と地域のデータが別々にある場合、ユーザー入力を処理する場合、またはプログラムでロケールのバリエーションを生成する場合に、このアプローチを使用します。

固定ロケールにはリテラル文字列を使用します。

// Good for fixed locales
const locale = new Intl.Locale("en-US");

値が変数から来る場合は、コンポーネントから構築します。

// Good for dynamic locales
const locale = new Intl.Locale(userLanguage, {
  region: userRegion
});

コンストラクタはコンポーネントを検証し、適切にフォーマットされた識別子を作成します。

ブラウザサポート

Intl.Localeコンストラクタは、すべてのモダンブラウザで動作します。Chrome、Firefox、Safari、Edgeは、コンポーネントからロケールを構築するためのコンストラクタとオプションオブジェクトをサポートしています。

Node.jsは、バージョン12からIntl.Localeをサポートしており、バージョン14以降ではすべてのコンストラクタオプションを完全にサポートしています。

まとめ

Intl.Localeコンストラクタは、個々のコンポーネントからロケール識別子を構築します。最初の引数として言語コードを渡し、オプションオブジェクトでスクリプト、地域、フォーマット設定を提供します。

主要な概念:

  • ロケール識別子は、言語、スクリプト、地域のコンポーネントで構成されます
  • コンストラクタは、languagescriptregionプロパティを持つオプションオブジェクトを受け取ります
  • 既存のロケールを最初の引数として渡すことで、コンポーネントを上書きできます
  • calendarhourCycleなどのフォーマット設定は、Unicode拡張として表示されます
  • toString()メソッドは、完全な識別子文字列を返します
  • languageregionscriptなどのプロパティを使用して、コンポーネントを読み取ることができます
  • コンストラクタは構文を検証しますが、意味的な正確性は検証しません

ユーザー入力からロケールを構築する場合、地域バリアントを生成する場合、または言語と地域の選択を組み合わせる場合は、このアプローチを使用してください。固定ロケールの場合は、代わりに文字列リテラルを使用してください。