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はすべて同じ言語を表します。ただし、タグを読みやすくする慣例的な大文字小文字のパターンがあります。

言語サブタグは慣例的に小文字を使用します。enと記述し、ENEnとは記述しません。

文字体系サブタグは慣例的に最初の文字を大文字にしたタイトルケースを使用します。Latnと記述し、latnLATNとは記述しません。

地域サブタグは慣例的に大文字を使用します。USと記述し、usUsとは記述しません。

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

JavaScriptによる言語タグの解析

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

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

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

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

Intl.Localeオブジェクトには、言語タグの各コンポーネントに対応するプロパティがあります。これらのプロパティは、元のタグに対応するサブタグが存在しない場合、undefinedを返します。

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

console.log(simple.region);
// Output: undefined

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

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

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

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

console.log(complex.region);
// Output: "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);
// Output: "zh-CN"

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

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

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

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

翻訳ファイル名に選択する構造は、言語タグからコンポーネントを抽出する方法と一致させる必要があります。

Intl APIでの言語タグの使用

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

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

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

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

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

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

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

console.log(formatter.format(1234.5));
// Output varies by language
// For "en-US": "1,234.5"
// For "de-DE": "1.234,5"

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

無効な言語タグの処理

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

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

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

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