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

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

はじめに

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

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

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

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

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

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

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

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

部分的なロケール識別子は、ほとんどの書式設定操作で機能します。Intl.DateTimeFormatおよびIntl.NumberFormat APIはenを受け入れ、適切なデフォルトを適用します。ただし、完全な識別子が必要な状況もあります。

ロケール識別子の比較

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

likely subtagsを追加することで、この曖昧さが解消されます。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 - they look different

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

console.log(maximized1.baseName === maximized2.baseName);
// true - both are "en-Latn-US"

正規形式の保存

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

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

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

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

likely subtagsを追加することで、スクリプトが明示的になります。ユーザーがスクリプトを指定せずにzhを提供した場合、最大化するとzh-Hans-CNが生成され、簡体字中国語が想定されていることが示されます。

アルゴリズムの仕組み

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

アルゴリズムは提供された情報を検証し、不足部分を補完します。

  • 言語のみを提供した場合、その言語で最も一般的なスクリプトと地域が追加されます
  • 言語とスクリプトを提供した場合、その組み合わせで最も一般的な地域が追加されます
  • 言語と地域を提供した場合、その組み合わせで最も一般的なスクリプトが追加されます
  • 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()メソッドを使用します。

ユーザー入力の正規化

ユーザーはさまざまな形式でロケールを入力する可能性があります。enと入力する人もいれば、en-USと入力する人、en-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以降で完全にサポートされています。

まとめ

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

重要なポイント:

  • Likely subtagsは、実際の言語使用データに基づいています
  • maximize()メソッドは、欠落しているスクリプトコードと地域コードを追加します
  • カレンダーや数値システムの拡張タグは変更されません
  • ユーザー入力の正規化とロケールの比較にmaximizationを使用してください
  • フォーマットAPIは、maximizeされたロケールを必要としません

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