優先ロケールが利用できない場合のロケールフォールバックの処理方法

ユーザーがサポートされていないロケールを好む場合に、サポートされている言語を自動的に選択する

はじめに

すべてのウェブアプリケーションが世界中のすべての言語をサポートしているわけではありません。ユーザーがアプリケーションでサポートされていない言語を希望する場合、エラーや未翻訳のテキストを表示する代わりに、次に最適な言語でコンテンツを表示するためのフォールバックメカニズムが必要です。

ロケールフォールバックとは、希望するロケールが利用できない場合に代替ロケールを選択するプロセスです。JavaScriptのIntl APIは、複数のロケールオプションを受け入れ、サポートする最初のロケールを選択することで、これを自動的に処理します。これにより、正確に希望するロケールが利用できない場合でも、アプリケーションは常に適切にフォーマットされたコンテンツを表示することができます。

このレッスンでは、JavaScriptでのロケールフォールバックの仕組み、効果的な実装方法、および特定のロケールサポート要件を持つアプリケーション向けのカスタムフォールバックロジックの構築方法について説明します。

サポートされていないロケールの問題

ロケール識別子をIntl APIに渡す場合、JavaScriptランタイムはそのロケールをサポートしてコンテンツを正しくフォーマットする必要があります。ノルウェー語ニーノシュクのフォーマットをリクエストしても、ランタイムがノルウェー語ブークモールのみをサポートしている場合、フォーマッターはこれを適切に処理する方法が必要です。

フォールバックがなければ、アプリケーションはサポートされていないロケールに遭遇した際にコンテンツの表示に失敗したり、未翻訳のテキストを表示したりします。あまり一般的でない言語バリアントの地域のユーザーは、インターフェースの不具合を経験することになります。

カナダフランス語を話すユーザーを考えてみましょう。アプリケーションがヨーロッパフランス語のみをサポートしている場合、フォーマッターが完全に失敗するのではなく、ヨーロッパフランス語の規則を使用することが望ましいです。完璧ではありませんが、これはローカライゼーションがまったくない場合よりも良い体験を提供します。

Intl APIがフォールバックを自動的に処理する方法

すべてのIntlコンストラクターは、単一のロケール文字列またはロケール文字列の配列のいずれかを受け入れます。配列を渡すと、ランタイムは各ロケールを順番に評価し、サポートする最初のロケールを使用します。

const locales = ["fr-CA", "fr-FR", "en-US"];
const formatter = new Intl.DateTimeFormat(locales);

const date = new Date("2025-03-15");
console.log(formatter.format(date));
// fr-CAが利用可能な場合はそれを使用
// fr-CAが利用できない場合はfr-FRにフォールバック
// どちらのフランス語バリアントも利用できない場合はen-USにフォールバック

ランタイムは配列を左から右へ調べます。カナダフランス語をサポートしている場合は、それを使用します。そうでない場合は、ヨーロッパフランス語を試します。どちらのフランス語バリアントも利用できない場合は、アメリカ英語にフォールバックします。

この自動フォールバックにより、特定のロケールをリクエストする際に手動でサポートを確認したり、エラーを処理したりする必要がありません。Intl APIは、サポートされているロケールを選択するか、システムのデフォルトにフォールバックすることを保証します。

複数のロケールオプションの提供

フォールバックを実装する最も簡単な方法は、優先順位の配列としてロケールを渡すことです。これはDateTimeFormatNumberFormatCollatorなどすべてのIntlコンストラクタで動作します。

const locales = ["es-MX", "es-ES", "es", "en"];
const numberFormatter = new Intl.NumberFormat(locales, {
  style: "currency",
  currency: "USD"
});

console.log(numberFormatter.format(1234.56));
// 可能であればメキシコスペイン語を使用
// スペインのスペイン語にフォールバック
// 一般的なスペイン語にフォールバック
// 最終オプションとして英語にフォールバック

このパターンは段階的な劣化パスを提供します。ユーザーは可能であれば好みの方言でコンテンツを取得し、次に彼らの言語のより広い変種、そして共通のフォールバック言語を取得します。

順序が重要です。ランタイムはサポートする最初のロケールを選択するため、最も具体的で優先されるロケールを最初に配置してください。

ロケールマッチングの仕組みを理解する

複数のロケールを提供すると、JavaScriptランタイムはロケールマッチングアルゴリズムを使用して、最適な利用可能なオプションを選択します。このアルゴリズムは、要求されたロケールとランタイムがサポートするロケールのセットを比較します。

要求されたロケールがサポートされているロケールと完全に一致する場合、ランタイムはそれをすぐに使用します。完全一致がない場合、ランタイムは言語と地域コードに基づいて関連するロケールを選択することがあります。

例えば、en-AU(オーストラリア英語)をリクエストしたが、ランタイムがen-USen-GBのみをサポートしている場合、完全に異なる言語にフォールバックするのではなく、それらの英語のバリアントの1つを選択します。

const locales = ["en-AU", "en"];
const formatter = new Intl.DateTimeFormat(locales);

const resolvedLocale = formatter.resolvedOptions().locale;
console.log(resolvedLocale);
// ランタイムによって「en-US」または「en-GB」を表示する可能性があります
// ランタイムはサポートされている英語のバリアントを選択しました

resolvedOptions()メソッドはフォーマッタが使用している実際のロケールを返します。これにより、フォールバック後にどのロケールが選択されたかを確認できます。

サポートされているロケールの確認

supportedLocalesOf() 静的メソッドは、特定の Intl コンストラクタがリストからどのロケールをサポートしているかを確認します。このメソッドはサポートされているロケールのみを含む配列を返します。

const requestedLocales = ["fr-CA", "fr-FR", "de-DE", "ja-JP"];
const supportedLocales = Intl.DateTimeFormat.supportedLocalesOf(requestedLocales);

console.log(supportedLocales);
// 出力はランタイムのサポート状況によって異なります
// 例: ["fr-FR", "de-DE", "ja-JP"]
// カナダフランス語はサポートされていませんでしたが、他はサポートされていました

このメソッドは、ランタイムがデフォルトにフォールバックすることなく使用できるロケールを示すために、要求されたロケールをフィルタリングします。サポートされていないロケールは返される配列から削除されます。

フォーマッタを作成する前にサポート状況を確認したり、ユーザーが利用できる言語オプションを表示したりするためにこのメソッドを使用できます。

const availableLocales = ["en-US", "es-MX", "fr-FR", "de-DE", "ja-JP"];
const supported = Intl.NumberFormat.supportedLocalesOf(availableLocales);

console.log("このランタイムがサポートしているのは:", supported);
// この環境でアプリケーションのどのロケールが機能するかを表示します

各 Intl コンストラクタには独自の supportedLocalesOf() メソッドがあります。これは、ロケールのサポートが異なる国際化機能間で異なる可能性があるためです。ランタイムは数値フォーマット用のフランス語をサポートしていても、テキストセグメンテーション用にはサポートしていない場合があります。

ロケール識別子からフォールバックチェーンを構築する

アプリケーションが特定のロケールをサポートしていることがわかっている場合、徐々に具体性が低くなるフォールバックチェーンを構築できます。このパターンは完全なロケール識別子から始まり、一致するものが見つかるまでコンポーネントを削除していきます。

function buildFallbackChain(locale) {
  const chain = [locale];

  const parts = locale.split("-");
  if (parts.length > 1) {
    chain.push(parts[0]);
  }

  chain.push("en");

  return chain;
}

const fallbacks = buildFallbackChain("zh-Hans-CN");
console.log(fallbacks);
// ["zh-Hans-CN", "zh", "en"]

const formatter = new Intl.DateTimeFormat(fallbacks);
// 中国の簡体字中国語を試みる
// 一般的な中国語にフォールバック
// 英語にフォールバック

この関数は、ロケール識別子から言語コードを抽出し、最終的な英語のフォールバックを追加することでフォールバックチェーンを作成します。アプリケーションがサポートするロケールに基づいて、より洗練されたフォールバックルールを含めるようにこのロジックを拡張できます。

言語の複数のバリエーションをサポートするアプリケーションの場合、英語にジャンプする前に関連する方言にフォールバックすることをお勧めします。

function buildSmartFallbackChain(locale) {
  const chain = [locale];

  if (locale.startsWith("es-")) {
    chain.push("es-MX", "es-ES", "es");
  } else if (locale.startsWith("fr-")) {
    chain.push("fr-FR", "fr-CA", "fr");
  } else if (locale.startsWith("zh-")) {
    chain.push("zh-Hans-CN", "zh-Hant-TW", "zh");
  }

  const parts = locale.split("-");
  if (parts.length > 1 && !chain.includes(parts[0])) {
    chain.push(parts[0]);
  }

  if (!chain.includes("en")) {
    chain.push("en");
  }

  return chain;
}

const fallbacks = buildSmartFallbackChain("es-AR");
console.log(fallbacks);
// ["es-AR", "es-MX", "es-ES", "es", "en"]
// アルゼンチンスペイン語を試みる
// メキシコスペイン語にフォールバック
// ヨーロッパスペイン語にフォールバック
// 一般的なスペイン語にフォールバック
// 英語にフォールバック

このアプローチにより、ユーザーは英語にフォールバックする前に、自分の言語の関連する方言でコンテンツを見ることができます。

ロケール照合アルゴリズムの選択

Intl APIは、lookupとbest fitという2つのロケール照合アルゴリズムをサポートしています。フォーマッターを作成する際にlocaleMatcherオプションを通じて使用するアルゴリズムを指定できます。

lookupアルゴリズムはBCP 47 Lookup仕様に従います。ロケール識別子を体系的に比較し、最初の完全一致を選択する厳密な照合を行います。

const locales = ["de-DE", "en-US"];
const formatter = new Intl.NumberFormat(locales, {
  localeMatcher: "lookup"
});

console.log(formatter.resolvedOptions().locale);
// 厳密なlookup照合ルールを使用

best fitアルゴリズムは、ランタイムが独自の照合ロジックを使用してロケールを選択することを可能にします。このアルゴリズムは、完全一致でなくても、ユーザーのニーズに最も適したロケールについて知的な判断を下すことができます。

const locales = ["de-DE", "en-US"];
const formatter = new Intl.NumberFormat(locales, {
  localeMatcher: "best fit"
});

console.log(formatter.resolvedOptions().locale);
// ランタイムのbest fitアルゴリズムを使用
// より知的に関連するロケールを選択する可能性がある

デフォルトのアルゴリズムはbest fitです。ほとんどのアプリケーションでは、異なるJavaScriptランタイム間でより良い結果を提供するため、デフォルトを使用すべきです。予測可能な厳密な照合動作が必要な場合にのみlookupを使用してください。

自動フォールバックのためのブラウザ言語設定の使用

navigator.languagesプロパティは、ユーザーの優先言語を優先順に配列として返します。この配列をIntlコンストラクタに直接渡すことで、ブラウザ設定に基づいた自動フォールバックを実装できます。

const formatter = new Intl.DateTimeFormat(navigator.languages);

const date = new Date("2025-03-15");
console.log(formatter.format(date));
// 自動的にユーザーの優先サポート言語を使用

このアプローチでは、ブラウザがすべてのフォールバックロジックを処理します。ユーザーの第一優先言語がサポートされていない場合、Intl APIは自動的に第二優先言語、次に第三優先言語というように試みます。

このパターンは、手動でフォールバックチェーンを構築せずに、ユーザーのすべての言語設定を尊重したい場合に適しています。

console.log(navigator.languages);
// ["fr-CA", "fr", "en-US", "en"]

const numberFormatter = new Intl.NumberFormat(navigator.languages, {
  style: "currency",
  currency: "USD"
});

console.log(numberFormatter.format(1234.56));
// 最初にカナダフランス語を試す
// ユーザーの完全な優先リストを通じてフォールバックする

Intl APIは配列内の各言語を評価し、サポートする最初の言語を選択するため、多様なユーザー設定に対応する堅牢なソリューションとなります。

アプリケーションサポートとユーザー設定の組み合わせ

ロケールサポートが限られているアプリケーションでは、フォーマッターを作成する前に、サポートされているロケールに合わせてユーザー設定をフィルタリングできます。これにより、アプリケーションが処理できるロケールのみを使用することが保証されます。

const supportedLocales = ["en-US", "es-MX", "fr-FR", "de-DE"];

function findBestLocale(userPreferences, appSupported) {
  const preferences = userPreferences.map(pref => {
    const parts = pref.split("-");
    return [pref, parts[0]];
  }).flat();

  for (const pref of preferences) {
    if (appSupported.includes(pref)) {
      return pref;
    }
  }

  return appSupported[0];
}

const userLocale = findBestLocale(navigator.languages, supportedLocales);
const formatter = new Intl.DateTimeFormat(userLocale);

console.log(formatter.resolvedOptions().locale);
// 選択されたロケールはユーザー設定とアプリケーションサポートの両方に一致します

この関数は、ユーザー設定とサポートされているロケールの間で最初の一致を見つけます。より広範なマッチングのために、地域コードなしの言語コードも試みます。

より高度なマッチングのために、ユーザーの設定のうちどれがこのランタイムのIntl APIによってサポートされているかを確認できます。

const supportedLocales = ["en-US", "es-MX", "fr-FR", "de-DE"];

function findBestSupportedLocale(userPreferences, appSupported) {
  const runtimeSupported = Intl.DateTimeFormat.supportedLocalesOf(appSupported);

  for (const pref of userPreferences) {
    if (runtimeSupported.includes(pref)) {
      return pref;
    }

    const lang = pref.split("-")[0];
    const match = runtimeSupported.find(s => s.startsWith(lang));
    if (match) {
      return match;
    }
  }

  return runtimeSupported[0] || "en";
}

const userLocale = findBestSupportedLocale(navigator.languages, supportedLocales);
const formatter = new Intl.DateTimeFormat(userLocale);

このアプローチにより、選択されたロケールがアプリケーションとJavaScriptランタイムの両方でサポートされていることが保証されます。

ロケールが一致しない場合の処理

要求されたロケールのいずれもサポートされていない場合、Intl APIはシステムのデフォルトロケールにフォールバックします。このデフォルトは環境によって異なり、オペレーティングシステム、ブラウザ、またはNode.jsの設定によって決定されます。

const unsupportedLocales = ["non-existent-locale"];
const formatter = new Intl.DateTimeFormat(unsupportedLocales);

console.log(formatter.resolvedOptions().locale);
// システムのデフォルトロケールを表示
// システムによって「en-US」または他のロケールになる可能性があります

システムのデフォルトにより、完全に無効またはサポートされていないロケールリストであっても、フォーマッターは常に機能します。アプリケーションはロケールの問題によってエラーを投げることはありません。

システムのデフォルトに依存する代わりに特定のフォールバックを確保するには、ロケール配列の最後に「en」や「en-US」などの広くサポートされているロケールを常に含めてください。

const locales = ["xyz-INVALID", "en"];
const formatter = new Intl.DateTimeFormat(locales);

console.log(formatter.resolvedOptions().locale);
// 最初のロケールが無効なので「en」を使用
// システムのデフォルトではなく英語への保証されたフォールバック

このパターンにより、アプリケーションの動作が異なる環境間でより予測可能になります。

本番アプリケーションのための実用的なパターン

本番アプリケーションを構築する際は、複数のフォールバック戦略を組み合わせて、多様なユーザーベース全体で堅牢なロケール処理を確保しましょう。

一般的なパターンでは、ユーザー設定、アプリケーションがサポートするロケール、そして確実な最終フォールバックを含む完全なフォールバックチェーンでフォーマッターを作成します。

class LocaleManager {
  constructor(supportedLocales) {
    this.supportedLocales = supportedLocales;
    this.defaultLocale = "en-US";
  }

  buildLocaleChain(userPreference) {
    const chain = [];

    if (userPreference) {
      chain.push(userPreference);

      const lang = userPreference.split("-")[0];
      if (lang !== userPreference) {
        chain.push(lang);
      }
    }

    chain.push(...navigator.languages);
    chain.push(...this.supportedLocales);
    chain.push(this.defaultLocale);

    const unique = [...new Set(chain)];
    return unique;
  }

  createDateFormatter(userPreference, options = {}) {
    const locales = this.buildLocaleChain(userPreference);
    return new Intl.DateTimeFormat(locales, options);
  }

  createNumberFormatter(userPreference, options = {}) {
    const locales = this.buildLocaleChain(userPreference);
    return new Intl.NumberFormat(locales, options);
  }
}

const manager = new LocaleManager(["en-US", "es-MX", "fr-FR", "de-DE"]);
const dateFormatter = manager.createDateFormatter("pt-BR");

console.log(dateFormatter.resolvedOptions().locale);
// 包括的なフォールバックチェーンを使用
// ブラジルポルトガル語を試行
// ポルトガル語にフォールバック
// navigator.languagesを通じてフォールバック
// サポートされているロケールを通じてフォールバック
// 他に何も一致しない場合、en-USを使用することが保証される

このクラスはロケールフォールバックロジックをカプセル化し、アプリケーション全体で一貫した動作を保証します。

実行時にユーザーのロケール変更に対応する必要があるアプリケーションでは、ロケールマネージャーとイベントリスナーを組み合わせます。

class LocaleAwareFormatter {
  constructor(supportedLocales) {
    this.supportedLocales = supportedLocales;
    this.updateFormatters();

    window.addEventListener("languagechange", () => {
      this.updateFormatters();
    });
  }

  updateFormatters() {
    const locales = [...navigator.languages, ...this.supportedLocales, "en"];

    this.dateFormatter = new Intl.DateTimeFormat(locales);
    this.numberFormatter = new Intl.NumberFormat(locales);
  }

  formatDate(date) {
    return this.dateFormatter.format(date);
  }

  formatNumber(number) {
    return this.numberFormatter.format(number);
  }
}

const formatter = new LocaleAwareFormatter(["en-US", "es-MX", "fr-FR"]);
console.log(formatter.formatDate(new Date()));
// ユーザーが言語設定を変更すると自動的に更新される

このパターンは、ブラウザの言語変更と同期するフォーマッターを作成し、アプリケーションが常に現在の設定に従ってコンテンツを表示することを保証します。

概要

ロケールフォールバックは、ユーザーが明示的にサポートしていないロケールを好む場合でも、アプリケーションが適切にフォーマットされたコンテンツを表示することを保証します。Intl APIは、ロケール設定の配列を受け入れ、サポートされている最初のオプションを選択することで、自動的にフォールバックを処理します。

主要な概念:

  • 自動フォールバックのためにIntlコンストラクタにロケールの配列を渡す
  • ランタイムは配列から最初にサポートされているロケールを選択する
  • supportedLocalesOf()を使用して、どのロケールが利用可能かを確認する
  • 特定のロケールから一般的なロケールへと進むフォールバックチェーンを構築する
  • localeMatcherオプションはマッチングアルゴリズムを制御する
  • 自動的なユーザー設定処理のためにnavigator.languagesを直接渡す
  • 英語のような広くサポートされている最終的なフォールバックを常に含める
  • ロケールが一致しない場合、ランタイムはシステムのデフォルトにフォールバックする

ほとんどのアプリケーションではロケール配列による自動フォールバックを使用してください。どのロケールをどの順序で試すかについて特定の制御が必要な場合は、カスタムフォールバックロジックを実装してください。