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

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());
// 出力: "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());
// 出力: "zh-Hans-CN"

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

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

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

console.log(traditionalChinese.toString());
// 出力: "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);
// 出力: "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);
// 出力: { success: true, locale: "fr-CA" }

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

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

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

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

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

const locale = new Intl.Locale("fr");
console.log(locale.toString());
// 出力: "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"));
// 出力: "en"

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

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

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

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

既存のロケール識別子を使用して、特定のコンポーネントをオーバーライドすることができます。これは、他の部分を維持しながら一部を変更する必要がある場合に便利です。

コンストラクタの第二引数は、第一引数からのコンポーネントをオーバーライドします:

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

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

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

複数のコンポーネントをオーバーライドすることもできます:

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

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

これにより、言語を保持しながら文字体系と地域の両方が変更されます。

構築されたロケールにフォーマット設定の環境設定を追加する

言語、文字体系、地域を超えて、ロケールにはフォーマット設定の環境設定を含めることができます。これらの環境設定は、日付、数値、およびその他の値がどのように表示されるかを制御します。

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

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

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

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

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

複数のフォーマット設定の環境設定を指定することができます:

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

console.log(locale.toString());
// 出力: "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));
// 出力: "١٢٬٣٤٥" (アラビア数字)

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

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

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

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

// 構文的には有効だが意味的には疑問がある
const locale = new Intl.Locale("en", {
  script: "Arab",
  region: "JP"
});

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

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

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

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

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

このメソッドは、その言語に最も可能性の高いスクリプトを追加します。結果が予想されるパターンと一致する場合、その組み合わせは合理的です。

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

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

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

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

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

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

scriptプロパティは指定された場合、スクリプトコードを返します:

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

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

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

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

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

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

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

これにより、フォーマット設定の環境設定なしで言語-スクリプト-地域の部分が得られます。

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

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

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

console.log(identifier);
// 出力: "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));
// 出力: "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();
}

// ユーザーが「スペイン語」と「メキシコ」を選択
const selectedLocale = createLocaleFromPicker(
  { value: "es" },
  { value: "MX" }
);

console.log(selectedLocale);
// 出力: "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);
// 出力: ["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);
// 出力: "fr-CA"

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

ロケール識別子の正規化

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

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

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

console.log(normalizeLocale("zh_Hans_CN"));
// 出力: null (無効な区切り文字)

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

ユーザー設定でフォーマッタを構成する

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

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));
// 出力: "14:30" (24時間形式)

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

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

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

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

// 固定ロケールに適している
const locale = new Intl.Locale("en-US");

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

// 動的ロケールに適している
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などのプロパティでコンポーネントを読み取ることができます
  • コンストラクタは構文を検証しますが、意味的な正確さは検証しません

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