ロケールから言語、国、スクリプトを抽出する方法

JavaScriptを使用してロケール識別子を解析し、個々のコンポーネントにアクセスする

はじめに

ロケール識別子(en-USfr-CAzh-Hans-CNなど)は、単一の文字列に複数の情報を符号化しています。これらのコンポーネントは、使用されている言語、その言語が話されている地域、そして時には使用されている表記体系を示します。

国際化されたアプリケーションを構築する際、これらの個々のコンポーネントを抽出する必要がしばしばあります。ユーザーに言語名のみを表示したり、地域別にロケールをグループ化したり、ロケールが使用している文字体系を確認したりすることがあるでしょう。正規表現で手動で文字列を解析する代わりに、JavaScriptはIntl.Locale APIを提供し、コンポーネントを確実に抽出することができます。

このガイドでは、ロケール識別子に存在するコンポーネント、Intl.Locale APIを使用してそれらを抽出する方法、そして実際にこれらのコンポーネントをいつ使用する必要があるかを説明します。

ロケール識別子に存在するコンポーネント

ロケール識別子はBCP 47標準に従っており、この標準は言語と地域のバリエーションを記述するための構造を定義しています。完全なロケール識別子には、ハイフンで区切られたいくつかのコンポーネントが含まれる場合があります。

最も一般的な3つのコンポーネントは次のとおりです:

  • 言語:使用されている主要言語(英語、スペイン語、中国語など)
  • 地域:言語が使用されている地理的領域(アメリカ合衆国、カナダ、中国など)
  • 文字体系:言語を表現するために使用される表記体系(ラテン文字、キリル文字、漢字など)

単純なロケール識別子には言語コードのみが含まれています:

en

ほとんどのロケール識別子には言語と地域が含まれています:

en-US
fr-CA
es-MX

言語が複数の表記体系で書かれる場合、一部のロケール識別子には文字体系が含まれます:

zh-Hans-CN
zh-Hant-TW
sr-Cyrl-RS
sr-Latn-RS

これらのコンポーネントを理解することで、言語のフォールバック、コンテンツの選択、ユーザーインターフェースのカスタマイズに関する決定を行うことができます。

Intl.Localeを使用してコンポーネントを抽出する

Intl.Locale APIはロケール識別子文字列を構造化されたオブジェクトに変換します。ロケールオブジェクトを作成すると、プロパティを通じてそのコンポーネントを読み取ることができます。

識別子をコンストラクタに渡してロケールオブジェクトを作成します:

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

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

ロケールオブジェクトは識別子の各コンポーネントに対応するプロパティを公開します。これらのプロパティは文字列解析を必要とせずに構造化されたアクセスを提供します。

言語コードの抽出

languageプロパティはロケール識別子の言語コンポーネントを返します。これは主要言語を識別する2文字または3文字のコードです。

const english = new Intl.Locale("en-US");
console.log(english.language); // "en"

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

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

言語コードはISO 639標準に従っています。一般的なコードには、英語のen、スペイン語のes、フランス語のfr、ドイツ語のde、日本語のja、中国語のzhなどがあります。

言語コードは有効なロケール識別子には常に存在します。これは唯一の必須コンポーネントです。

const languageOnly = new Intl.Locale("ja");
console.log(languageOnly.language); // "ja"
console.log(languageOnly.region); // undefined

言語コードを抽出すると、翻訳の選択、テキスト処理ルールの決定、またはユーザー向けの言語セレクタの構築に使用できます。

地域コードの抽出

regionプロパティはロケール識別子の地域コンポーネントを返します。これは言語が使用される地理的領域を識別する2文字のコードです。

const americanEnglish = new Intl.Locale("en-US");
console.log(americanEnglish.region); // "US"

const britishEnglish = new Intl.Locale("en-GB");
console.log(britishEnglish.region); // "GB"

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

地域コードはISO 3166-1標準に従っています。国と地域を表すために2つの大文字を使用します。一般的なコードには、アメリカ合衆国のUS、イギリスのGB、カナダのCA、メキシコのMX、フランスのFR、中国のCNなどがあります。

地域コードは日付、数値、通貨のフォーマット方法を変更します。アメリカ英語では月-日-年の日付形式と小数点にピリオドを使用します。イギリス英語では日-月-年の日付形式と桁区切りにカンマを使用します。

地域コードはロケール識別子ではオプションです。ロケールに地域がない場合、regionプロパティはundefinedを返します:

const genericSpanish = new Intl.Locale("es");
console.log(genericSpanish.region); // undefined

地域コードを抽出すると、地域固有のフォーマットのカスタマイズ、地域固有のコンテンツの選択、またはユーザーへの位置情報の表示に使用できます。

スクリプトコードの抽出

scriptプロパティはロケール識別子のスクリプト部分を返します。これは言語の表記システムを識別する4文字のコードです。

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

const traditionalChinese = new Intl.Locale("zh-Hant-TW");
console.log(traditionalChinese.script); // "Hant"

const serbianCyrillic = new Intl.Locale("sr-Cyrl-RS");
console.log(serbianCyrillic.script); // "Cyrl"

const serbianLatin = new Intl.Locale("sr-Latn-RS");
console.log(serbianLatin.script); // "Latn"

スクリプトコードはISO 15924規格に準拠しています。最初の文字が大文字の4文字を使用します。一般的なコードには、ラテン文字のLatn、キリル文字のCyrl、簡体字漢字のHans、繁体字漢字のHant、アラビア文字のArabなどがあります。

ほとんどのロケールではスクリプトコードを省略します。これは各言語にデフォルトの表記システムがあるためです。英語はラテン文字がデフォルトなので、en-Latnではなくenと記述します。ロシア語はキリル文字がデフォルトなので、ru-Cyrlではなくruと記述します。

スクリプトコードは、言語が複数の方法で表記できる場合に表示されます。中国語は簡体字と繁体字の両方を使用します。セルビア語はキリル文字とラテン文字の両方のアルファベットを使用します。これらの場合、スクリプトコードはどの表記システムを使用するかを明確にします。

ロケールに明示的なスクリプトコードがない場合、scriptプロパティはundefinedを返します:

const english = new Intl.Locale("en-US");
console.log(english.script); // undefined

スクリプトコードを抽出すると、フォントの選択、テキストレンダリングの決定、または表記システムによるコンテンツのフィルタリングに使用できます。

コンポーネントがundefinedになる場合の理解

すべてのロケール識別子がすべてのコンポーネントを含むわけではありません。言語コードは必須ですが、地域とスクリプトはオプションです。

識別子にコンポーネントが存在しない場合、対応するプロパティはundefinedを返します:

const locale = new Intl.Locale("fr");

console.log(locale.language); // "fr"
console.log(locale.region); // undefined
console.log(locale.script); // undefined

この動作により、これらの値を使用する前にロケールが地域やスクリプトを指定しているかどうかを確認できます:

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

if (locale.region) {
  console.log(`Region-specific formatting for ${locale.region}`);
} else {
  console.log("Using default formatting");
}

Null合体演算子を使用してデフォルト値を提供できます:

const locale = new Intl.Locale("es");
const region = locale.region ?? "ES";

console.log(region); // "ES"

ロケールのフォールバックチェーンを構築する際、未定義のコンポーネントをチェックすることで代替案を構築するのに役立ちます:

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

  if (locale.region) {
    fallbacks.push(locale.language);
  }

  return fallbacks;
}

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

これにより、最も具体的なものから最も一般的なものへと順序付けられたロケール識別子のリストが作成されます。

コンポーネント抽出の実用的なユースケース

ロケールコンポーネントの抽出は、国際化されたアプリケーションを構築する際のいくつかの一般的な問題を解決します。

言語別のロケールのグループ化

利用可能な言語のリストを表示する際、同じ言語コードを共有するロケールをグループ化します:

const locales = ["en-US", "en-GB", "fr-FR", "fr-CA", "es-ES", "es-MX"];

const grouped = locales.reduce((groups, identifier) => {
  const locale = new Intl.Locale(identifier);
  const language = locale.language;

  if (!groups[language]) {
    groups[language] = [];
  }

  groups[language].push(identifier);
  return groups;
}, {});

console.log(grouped);
// {
//   en: ["en-US", "en-GB"],
//   fr: ["fr-FR", "fr-CA"],
//   es: ["es-ES", "es-MX"]
// }

この構成により、ユーザーは言語内で希望する地域のバリエーションを見つけやすくなります。

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

言語選択のためのユーザーインターフェースを構築する際、意味のあるラベルを表示するためにコンポーネントを抽出します:

function buildLocaleSelector(identifiers) {
  return identifiers.map(identifier => {
    const locale = new Intl.Locale(identifier);

    const languageNames = new Intl.DisplayNames([identifier], {
      type: "language"
    });

    const regionNames = new Intl.DisplayNames([identifier], {
      type: "region"
    });

    return {
      value: identifier,
      language: languageNames.of(locale.language),
      region: locale.region ? regionNames.of(locale.region) : null
    };
  });
}

const options = buildLocaleSelector(["en-US", "en-GB", "fr-FR"]);
console.log(options);
// [
//   { value: "en-US", language: "English", region: "United States" },
//   { value: "en-GB", language: "English", region: "United Kingdom" },
//   { value: "fr-FR", language: "French", region: "France" }
// ]

これにより、各ロケールオプションに人間が読みやすいラベルが提供されます。

地域によるフィルタリング

特定の地域に固有のコンテンツを表示する必要がある場合、ロケールをフィルタリングするために地域コードを抽出します:

function filterByRegion(identifiers, targetRegion) {
  return identifiers.filter(identifier => {
    const locale = new Intl.Locale(identifier);
    return locale.region === targetRegion;
  });
}

const allLocales = ["en-US", "es-US", "en-GB", "fr-FR", "zh-CN"];
const usLocales = filterByRegion(allLocales, "US");

console.log(usLocales); // ["en-US", "es-US"]

これにより、特定の国のユーザーに適したロケールを選択できます。

スクリプト互換性の確認

フォントを選択したりテキストをレンダリングしたりする際に、互換性を確保するためにスクリプトを確認します:

function selectFont(identifier) {
  const locale = new Intl.Locale(identifier);
  const script = locale.script;

  if (script === "Hans" || script === "Hant") {
    return "Noto Sans CJK";
  } else if (script === "Arab") {
    return "Noto Sans Arabic";
  } else if (script === "Cyrl") {
    return "Noto Sans";
  } else {
    return "Noto Sans";
  }
}

console.log(selectFont("zh-Hans-CN")); // "Noto Sans CJK"
console.log(selectFont("ar-SA")); // "Noto Sans Arabic"
console.log(selectFont("en-US")); // "Noto Sans"

これにより、各書記体系に対してテキストが正しくレンダリングされることが保証されます。

言語フォールバックの実装

ユーザーの希望するロケールが利用できない場合、基本言語にフォールバックします:

function selectBestLocale(userPreference, supportedLocales) {
  const user = new Intl.Locale(userPreference);

  if (supportedLocales.includes(userPreference)) {
    return userPreference;
  }

  const languageMatch = supportedLocales.find(supported => {
    const locale = new Intl.Locale(supported);
    return locale.language === user.language;
  });

  if (languageMatch) {
    return languageMatch;
  }

  return supportedLocales[0];
}

const supported = ["en-US", "fr-FR", "es-ES"];

console.log(selectBestLocale("en-GB", supported)); // "en-US"
console.log(selectBestLocale("fr-CA", supported)); // "fr-FR"
console.log(selectBestLocale("de-DE", supported)); // "en-US"

これにより、完全一致が利用できない場合に適切なフォールバックが提供されます。