ロケールから言語、国、スクリプトを抽出する方法
JavaScript を使ってロケール識別子を分解し、それぞれの要素にアクセスする
はじめに
en-US、fr-CA、そして zh-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 と書くだけでよく、en-Latn のように記述する必要はありません。ロシア語はキリル文字が標準なので、ru だけを書き、ru-Cyrl とはしません。
言語が複数の文字体系で表記できる場合、スクリプトコードが使われます。中国語は簡体字と繁体字両方があり、セルビア語はキリル文字とラテン文字の両方が使われます。このような場合、どの文字体系を使うかを明確にするためにスクリプトコードを指定します。
ロケールに明示的なスクリプトコードがない場合、script プロパティは undefined を返します:
const english = new Intl.Locale("en-US");
console.log(english.script); // 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"]
// }
この分類により、ユーザーは自分の好みに合った地域バリエーションを見つけやすくなります。
ロケールセレクターの作成
言語選択UIを作成するときは、各コンポーネントを抽出して分かりやすいラベルを表示しましょう。
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"
これにより、完全一致が見つからなくてもスムーズにフォールバックできます。