不完全なロケールに可能性の高いサブタグを追加する方法

JavaScriptを使用して部分的なロケール識別子に最も可能性の高いスクリプトと地域を追加する

はじめに

ロケール識別子を扱う際、不完全な情報を受け取ることがあります。ユーザーがスクリプトや地域を示さずに、jaのような言語コードのみを指定することがあります。この部分的な識別子は有効ですが、ロケールの比較やフォーマット規則の決定などの特定の操作に必要な特異性が欠けています。

JavaScriptでは、これらの部分的な識別子に最も可能性の高い欠落コンポーネントを追加する方法を提供しています。このプロセスでは、言語データを使用して、その言語のユーザーが一般的に使用するスクリプトと地域を推測します。

このガイドでは、可能性の高いサブタグとは何か、JavaScriptがそれらをどのように決定するか、そしてアプリケーションでこの機能をいつ使用するかについて説明します。

可能性の高いサブタグとは

可能性の高いサブタグとは、特定の言語で最も一般的に表示されるスクリプトコードと地域コードのことです。これらの関連付けは、Unicode コンソーシアムによって維持されている実際の言語使用データに基づいています。

例えば、英語は通常ラテン文字で書かれ、最も一般的にアメリカ合衆国で使用されています。言語コードenだけを持っている場合、可能性の高いサブタグはスクリプトがLatn、地域がUSとなり、完全な識別子はen-Latn-USになります。

この可能性は、話者人口と歴史的な使用パターンに基づいています。アルゴリズムは常に統計的に最も一般的な組み合わせを返します。

可能性の高いサブタグを追加する理由

部分的なロケール識別子は、ほとんどのフォーマット操作で機能します。Intl.DateTimeFormatIntl.NumberFormat APIはenを受け入れ、適切なデフォルト値を適用します。しかし、完全な識別子が必要な状況もあります。

ロケール識別子の比較

2つのロケール識別子が同じ言語と地域を指しているかどうかを比較する場合、部分的な識別子はあいまいさを生じさせます。enen-USと同じことを意味するのか、それとも一方が地域を指定し、もう一方が指定していないため異なるのでしょうか?

可能性の高いサブタグを追加することで、このあいまいさが解消されます。enen-USはどちらもen-Latn-USに最大化され、直接比較できるようになります。

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

console.log(locale1.baseName === locale2.baseName);
// false - 異なって見える

const maximized1 = locale1.maximize();
const maximized2 = locale2.maximize();

console.log(maximized1.baseName === maximized2.baseName);
// true - どちらも "en-Latn-US"

正規形式の保存

データベースや設定ファイルにロケール識別子を保存する場合、完全な形式を使用することで一貫性が確保されます。すべてのフランス語ロケールは fr-Latn-FR、すべての日本語ロケールは ja-Jpan-JP などとなります。

この一貫性により、ロケールによる検索、フィルタリング、グループ化がより信頼性の高いものになります。

スクリプト固有の動作の決定

一部の言語は複数のスクリプトを使用し、スクリプトはテキストのレンダリング、フォントの選択、照合に影響します。中国語は簡体字または繁体字で書くことができ、セルビア語はキリル文字またはラテン文字を使用できます。

可能性の高いサブタグを追加することで、スクリプトが明示的になります。ユーザーがスクリプトを指定せずに zh を提供した場合、最大化すると zh-Hans-CN となり、簡体字中国語が期待されていることを示します。

アルゴリズムの仕組み

Add Likely Subtags アルゴリズムは、言語使用情報のデータベースを使用して不足しているコンポーネントを決定します。このデータベースは、Common Locale Data Repository の一部としてUnicode コンソーシアムによって維持されています。

アルゴリズムは提供された情報を調べ、ギャップを埋めます:

  • 言語のみを提供する場合、その言語に最も一般的なスクリプトと地域が追加されます
  • 言語とスクリプトを提供する場合、その組み合わせに最も一般的な地域が追加されます
  • 言語と地域を提供する場合、その組み合わせに最も一般的なスクリプトが追加されます
  • 3つのコンポーネントすべてを提供する場合、それらは変更されません

これらの決定は、世界中の言語使用に関する統計データに基づいています。

maximize メソッドの使用

maximize() メソッドは Intl.Locale オブジェクトで利用できます。ベース名に可能性の高いサブタグが追加された新しいロケールオブジェクトを返します。

const locale = new Intl.Locale("ja");
const maximized = locale.maximize();

console.log(locale.baseName);
// "ja"

console.log(maximized.baseName);
// "ja-Jpan-JP"

このメソッドは元のロケールオブジェクトを変更しません。新しいオブジェクトを作成して返します。

異なる言語での例

異なる言語は、主に話されている地域や使用される文字体系に基づいて、異なる可能性の高いサブタグを持っています。

ヨーロッパ言語

フランス語はラテン文字でフランスに最大化されます:

const french = new Intl.Locale("fr");
const maximized = french.maximize();

console.log(maximized.baseName);
// "fr-Latn-FR"

ドイツ語はラテン文字でドイツに最大化されます:

const german = new Intl.Locale("de");
const maximized = german.maximize();

console.log(maximized.baseName);
// "de-Latn-DE"

非ラテン文字の言語

日本語は日本の日本語文字に最大化されます:

const japanese = new Intl.Locale("ja");
const maximized = japanese.maximize();

console.log(maximized.baseName);
// "ja-Jpan-JP"

アラビア語はアラビア文字でエジプトに最大化されます:

const arabic = new Intl.Locale("ar");
const maximized = arabic.maximize();

console.log(maximized.baseName);
// "ar-Arab-EG"

文字体系のない中国語は簡体字と中国に最大化されます:

const chinese = new Intl.Locale("zh");
const maximized = chinese.maximize();

console.log(maximized.baseName);
// "zh-Hans-CN"

地域を含む部分的な識別子

言語と地域を指定し、文字体系を指定しない場合、アルゴリズムは文字体系を追加します:

const britishEnglish = new Intl.Locale("en-GB");
const maximized = britishEnglish.maximize();

console.log(maximized.baseName);
// "en-Latn-GB"

地域は指定されたままです。不足している文字体系のみが追加されます。

文字体系を含む部分的な識別子

言語と文字体系を指定し、地域を指定しない場合、アルゴリズムはその文字体系に最も一般的な地域を追加します:

const traditionalChinese = new Intl.Locale("zh-Hant");
const maximized = traditionalChinese.maximize();

console.log(maximized.baseName);
// "zh-Hant-TW"

繁体字中国語は主に台湾で使用されているため、地域としてTWが追加されます。

拡張タグは保持されます

Unicodeの拡張タグは、暦システム、数字体系、時間周期などのフォーマット設定を指定します。これらのタグはロケール識別子の-u-の後に表示されます。

maximize()メソッドは拡張タグを変更しません。言語、文字体系、地域コンポーネントにのみ影響します。

const locale = new Intl.Locale("fr", {
  calendar: "gregory",
  numberingSystem: "latn",
  hourCycle: "h23"
});

console.log(locale.toString());
// "fr-u-ca-gregory-hc-h23-nu-latn"

const maximized = locale.maximize();

console.log(maximized.toString());
// "fr-Latn-FR-u-ca-gregory-hc-h23-nu-latn"

ベース名はfrからfr-Latn-FRに変更されますが、拡張タグは同一のままです。

maximize を使用するタイミング

一貫性や比較の目的で完全なロケール識別子が必要な場合は、maximize() メソッドを使用してください。

ユーザー入力の正規化

ユーザーはさまざまな形式でロケールを入力することがあります。enen-USen-Latn-US などと入力する場合があります。すべての入力を最大化することで、一貫した形式を作成できます:

function normalizeLocale(input) {
  try {
    const locale = new Intl.Locale(input);
    const maximized = locale.maximize();
    return maximized.baseName;
  } catch (error) {
    return null;
  }
}

console.log(normalizeLocale("en"));
// "en-Latn-US"

console.log(normalizeLocale("en-US"));
// "en-Latn-US"

console.log(normalizeLocale("en-Latn-US"));
// "en-Latn-US"

3つの入力はすべて同じ正規化された形式になります。

ロケールのフォールバックチェーンの構築

特定のロケールが利用できない場合、アプリケーションはより一般的なロケールにフォールバックします。最大化はこれらのチェーンを正しく構築するのに役立ちます:

function buildFallbackChain(localeString) {
  const locale = new Intl.Locale(localeString);
  const maximized = locale.maximize();

  const chain = [maximized.toString()];

  if (maximized.script && maximized.region) {
    const withoutRegion = new Intl.Locale(
      `${maximized.language}-${maximized.script}`
    );
    chain.push(withoutRegion.toString());
  }

  if (maximized.region) {
    chain.push(maximized.language);
  }

  chain.push("en");

  return chain;
}

console.log(buildFallbackChain("zh-TW"));
// ["zh-Hant-TW", "zh-Hant", "zh", "en"]

これにより、最も具体的なものから最も一般的なロケールへの適切なフォールバックが作成されます。

ユーザー設定と利用可能なロケールのマッチング

利用可能な翻訳のセットがあり、ユーザー設定に最適なマッチを見つける必要がある場合、両側を最大化することで正確な比較が可能になります:

function findBestMatch(userPreference, availableLocales) {
  const userMaximized = new Intl.Locale(userPreference).maximize();

  const matches = availableLocales.map(available => {
    const availableMaximized = new Intl.Locale(available).maximize();

    let score = 0;
    if (userMaximized.language === availableMaximized.language) score += 1;
    if (userMaximized.script === availableMaximized.script) score += 1;
    if (userMaximized.region === availableMaximized.region) score += 1;

    return { locale: available, score };
  });

  matches.sort((a, b) => b.score - a.score);

  return matches[0].locale;
}

const available = ["en-US", "en-GB", "fr-FR", "de-DE"];
console.log(findBestMatch("en", available));
// "en-US"

この関数はユーザー設定の enen-Latn-US に拡張し、最も近いマッチを見つけます。

maximizeを使用しない場合

フォーマットAPIにロケールを渡す前にロケールをmaximizeする必要はありません。Intl.DateTimeFormatIntl.NumberFormat、およびその他のフォーマット用コンストラクタは、部分的な識別子を正しく処理します。

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

const partial = new Intl.DateTimeFormat("fr").format(date);
const maximized = new Intl.DateTimeFormat("fr-Latn-FR").format(date);

console.log(partial);
// "15/03/2025"

console.log(maximized);
// "15/03/2025"

両方とも同一の出力を生成します。この場合、追加の詳細さはフォーマット動作を変更しません。

maximize()は、組み込みフォーマッタにロケールを渡す場合ではなく、独自のロジックに明示的な情報が必要な場合に使用してください。

ブラウザサポート

maximize()メソッドはすべての最新ブラウザで利用可能です。Chrome、Firefox、Safari、およびEdgeはすべてIntl.Locale APIの一部としてこれをサポートしています。

Node.jsはバージョン12からmaximize()をサポートし、バージョン14以降で完全にサポートしています。

まとめ

可能性の高いサブタグは、特定の言語に対して最も一般的なスクリプトと地域を追加することで、部分的なロケール識別子を完成させます。Intl.Locale.maximize()メソッドは、この拡張を実行するためにUnicodeの「Add Likely Subtags」アルゴリズムを実装しています。

重要なポイント:

  • 可能性の高いサブタグは実世界の言語使用データに基づいています
  • maximize()メソッドは不足しているスクリプトコードと地域コードを追加します
  • カレンダーや数字システムの拡張タグは変更されません
  • ユーザー入力の正規化とロケールの比較にはmaximizationを使用します
  • フォーマットAPIはmaximizeされたロケールを必要としません

maximize()メソッドは、アプリケーションロジックが明示的なスクリプトと地域情報を必要とする場合に、完全なロケール識別子を扱うための標準化された方法を提供します。