en-USやfr-CAなどの言語タグの扱い方

より良い国際化の判断を行うためにBCP 47言語タグの構造と意味を理解する

はじめに

言語タグは、特定の言語とその地域的なバリエーションを識別する標準化されたコードです。これらのタグは国際化作業全体に登場します。ユーザーの優先言語を検出すると、ブラウザは言語タグを返します。日付や数値をフォーマットする際には、Intl APIに言語タグを渡します。翻訳をロードする際には、表示するコンテンツを決定するために言語タグを使用します。

これらのタグの仕組みを理解することで、言語選択、フォールバック動作、コンテンツ編成についてより良い判断ができるようになります。このレッスンでは、言語タグの構造を説明し、JavaScriptでそれらを扱う方法を示します。

言語タグとは

enen-USzh-Hans-CNなどの言語タグは、言語を識別し、オプションでスクリプトと地域を指定する文字列です。これらのタグはBCP 47標準に従っており、この標準はInternet Engineering Task ForceとInternet Assigned Numbers Authorityによって維持されています。

BCP 47はBest Current Practice 47の略です。この標準では、サブタグと呼ばれる小さなコンポーネントから言語タグを構築する方法を定義しています。各サブタグは、使用されている言語、使用している書記体系、関連する国など、言語の特定の側面を表します。

すべてのプログラミング言語と国際化ライブラリはBCP 47タグを使用しています。この一貫性により、ブラウザ検出からサーバーサイドのフォーマット、翻訳ファイル名まで、アプリケーション全体で同じ言語識別子を使用できます。

言語タグの構造

言語タグはハイフンで区切られたサブタグで構成されています。最も一般的な3つのサブタグは、言語、スクリプト、地域です。これらのサブタグは存在する場合、常にこの特定の順序で表示されます。

言語サブタグが最初に来て、唯一の必須コンポーネントです。ISO 639からの2文字または3文字のコードを使用します。例えば、enは英語、frはフランス語、zhは中国語を表します。

スクリプトサブタグは存在する場合、2番目に来ます。ISO 15924からの4文字のコードを使用し、書記体系を識別します。例えば、Latnはラテンアルファベット、Cyrlはキリル文字、Hansは簡体字中国語を表します。

地域サブタグは存在する場合、最後に来ます。ISO 3166-1からの2文字のコードを使用し、通常は国を表します。例えば、USはアメリカ合衆国、CAはカナダ、CNは中国を表します。

一般的な言語タグの例

ここでは、言語タグで表現できる様々な詳細レベルを示す例を紹介します。

言語のみのシンプルなタグ:

  • en - 英語(特定の地域やスクリプトなし)
  • fr - フランス語(特定の地域やスクリプトなし)
  • es - スペイン語(特定の地域やスクリプトなし)

言語と地域を含むタグ:

  • en-US - アメリカ合衆国で使用される英語
  • en-GB - イギリスで使用される英語
  • fr-CA - カナダで使用されるフランス語
  • es-MX - メキシコで使用されるスペイン語

言語、スクリプト、地域を含むタグ:

  • zh-Hans-CN - 中国で使用される簡体字中国語
  • zh-Hant-TW - 台湾で使用される繁体字中国語
  • sr-Latn-RS - セルビアでラテン文字を使用するセルビア語
  • sr-Cyrl-RS - セルビアでキリル文字を使用するセルビア語

必要な詳細レベルはアプリケーションによって異なります。テキストの翻訳のみを行う場合は、言語と地域だけで十分かもしれません。複数の表記システムを使用する言語を扱う場合は、スクリプトサブタグが必要です。

言語タグの大文字小文字の規則

言語タグは大文字小文字を区別しません。タグ en-USEN-USen-usEn-Us はすべて同じ言語を表します。ただし、タグを読みやすくするための慣習的な大文字小文字のパターンがあります。

言語サブタグは慣習的に小文字を使用します。ENEn ではなく、en と書きます。

スクリプトサブタグは慣習的に最初の文字を大文字にしたタイトルケースを使用します。latnLATN ではなく、Latn と書きます。

地域サブタグは慣習的に大文字を使用します。usUs ではなく、US と書きます。

これらの規則に従うことで、タグが読みやすくなり、ドキュメントや仕様で使用されている形式と一致します。ただし、この形式は公式には大文字小文字を区別しないため、コードは大文字小文字に関係なく言語タグを受け入れるべきです。

JavaScriptによる言語タグの解析

JavaScriptは言語タグを解析してその構成要素を抽出するためのIntl.Localeコンストラクタを提供しています。このコンストラクタは言語タグ文字列を受け取り、各サブタグのプロパティを持つオブジェクトを返します。

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

console.log(locale.language);
// 出力: "en"

console.log(locale.region);
// 出力: "US"

Intl.Localeオブジェクトは言語タグの各構成要素のプロパティを持っています。対応するサブタグが元のタグに存在しない場合、これらのプロパティはundefinedを返します。

const simple = new Intl.Locale("fr");
console.log(simple.language);
// 出力: "fr"

console.log(simple.region);
// 出力: undefined

同じ方法でスクリプトサブタグを含むタグも解析できます。

const complex = new Intl.Locale("zh-Hans-CN");

console.log(complex.language);
// 出力: "zh"

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

console.log(complex.region);
// 出力: "CN"

この解析機能は、言語タグの特定の構成要素に基づいて判断を行う必要がある場合に役立ちます。例えば、スクリプトに基づいて異なるフォントを読み込んだり、地域に基づいて異なるコンテンツを表示したりする場合などです。

特定のタグと一般的なタグの使い分け

言語タグの適切な詳細レベルの選択は、アプリケーションが扱う必要がある言語と文化の側面によって異なります。

enfrのような言語のみのタグは、その言語のすべての話者に対して機能する単一の翻訳がある場合に使用します。これは、ローカライゼーション予算が限られているアプリケーションや、地域による変化が少ない言語で一般的です。

en-USfr-CAのような言語と地域のタグは、語彙、綴り、または文化的慣習の地域差を考慮する必要がある場合に使用します。イギリス英語とアメリカ英語は多くの単語で異なる綴りを使用します。カナダフランス語とヨーロッパフランス語は異なる語彙や表現を持っています。

zh-Hans-CNのような言語、スクリプト、地域のタグは、複数の表記システムを使用する言語を扱う場合に使用します。中国語は簡体字または繁体字で書くことができます。セルビア語はラテン文字またはキリル文字で書くことができます。スクリプトサブタグはこれらのバリアントを区別します。

翻訳ファイル用の言語コードの抽出

多くの翻訳システムでは、言語コードによってファイルを整理しています。完全な言語タグから言語と地域だけを抽出して、どの翻訳ファイルを読み込むかを決定することができます。

const userLanguage = "zh-Hans-CN";
const locale = new Intl.Locale(userLanguage);

const translationKey = `${locale.language}-${locale.region}`;
console.log(translationKey);
// 出力: "zh-CN"

このアプローチは、ユーザーの言語タグにファイル選択に必要のないコンポーネントが含まれている場合でも機能します。

一部のアプリケーションでは、地域なしの言語コードのみを使用します。

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

const translationKey = locale.language;
console.log(translationKey);
// 出力: "fr"

翻訳ファイル名の構造は、言語タグからコンポーネントを抽出する方法と一致させるべきです。

Intl APIでの言語タグの使用

Intl APIはすべてのコンストラクタで言語タグを直接受け入れます。特定のコンポーネントを検査する必要がない限り、タグを自分で解析する必要はありません。

const date = new Date("2025-03-15");

const usFormat = new Intl.DateTimeFormat("en-US").format(date);
console.log(usFormat);
// 出力: "3/15/2025"

const gbFormat = new Intl.DateTimeFormat("en-GB").format(date);
console.log(gbFormat);
// 出力: "15/03/2025"

Intl APIは言語タグを使用して、どのフォーマット規則を適用するかを決定します。同じ言語を話す地域でも、日付、数字、通貨のフォーマットは異なります。

ブラウザから取得した言語タグをIntlコンストラクタに直接渡すことができます。

const userLanguage = navigator.language;
const formatter = new Intl.NumberFormat(userLanguage);

console.log(formatter.format(1234.5));
// 出力は言語によって異なります
// "en-US"の場合: "1,234.5"
// "de-DE"の場合: "1.234,5"

これはクライアントサイドの国際化で最も一般的なパターンです。ユーザーの言語を検出し、その言語タグをアプリケーション全体で使用して、コンテンツを適切にフォーマットします。

無効な言語タグの処理

Intl.Locale コンストラクタは、無効な言語タグを渡すと RangeError をスローします。信頼できないソースからの言語タグを扱う場合は、このエラーを処理する必要があります。

try {
  const locale = new Intl.Locale("invalid-tag-format");
} catch (error) {
  console.log(error.name);
  // 出力: "RangeError"

  console.log(error.message);
  // 出力: "invalid language tag: invalid-tag-format"
}

ブラウザからの言語タグのほとんどは有効ですが、ユーザー入力や外部データソースには不正な形式のタグが含まれている可能性があります。コンストラクタをエラー処理でラップすることで、これらの無効なタグがアプリケーションをクラッシュさせるのを防ぎます。