ロケール識別子が有効かどうかを確認する方法

日付、数値、通貨をフォーマットする前にロケール識別子を検証する

はじめに

アプリケーションがユーザー入力、APIレスポンス、または設定ファイルからロケール識別子を受け取る場合、使用する前にそれらが有効であることを確認する必要があります。無効なロケール識別子は、フォーマット関数がエラーをスローしたり、予期しない結果を生じさせたりする原因となります。

検証により、en-USのような文字列が国際標準に従って構造的に正しいことを確認します。これにより、Intl APIや他の国際化ライブラリにロケール識別子を渡す際のランタイムエラーを防ぎます。

JavaScriptには、ロケール識別子を検証するための2つの組み込みメソッドが用意されています。どちらのメソッドも、言語タグの形式を定義するBCP 47標準に対して識別子の構造をチェックします。

有効なロケール識別子の条件

ロケール識別子は、言語タグのBCP 47標準に従います。この標準は、言語、スクリプト、地域、拡張コンポーネントを組み合わせるための構造とルールを定義しています。

有効なロケール識別子は、アンダースコアやその他の文字ではなく、ハイフンを使用してコンポーネントを区切ります。言語コードは認識されたISO 639コードでなければならず、地域コードは認識されたISO 3166-1コードでなければなりません。

有効なロケール識別子の例:

  • en(英語)
  • en-US(アメリカ英語)
  • zh-Hans(簡体字中国語)
  • zh-Hans-CN(中国で使用される簡体字中国語)
  • en-US-u-ca-gregory(グレゴリオ暦を使用するアメリカ英語)

無効なロケール識別子の例:

  • en_US(ハイフンの代わりにアンダースコアを使用)
  • english(認識された言語コードではない)
  • en-USA(地域コードは3文字ではなく2文字でなければならない)
  • EN-us(言語コードは小文字でなければならない)
  • abc-XY(存在しない言語コード)

検証メソッドは、これらの構造的ルールをチェックし、コードが認識された標準であることを確認します。

Intl.getCanonicalLocalesを使用した検証

Intl.getCanonicalLocales()メソッドはロケール識別子を検証し、正規形式で返します。このメソッドは単一のロケール識別子または識別子の配列を受け入れます。

const result = Intl.getCanonicalLocales("en-US");
console.log(result);
// 出力: ["en-US"]

このメソッドは入力を標準形式に変換することで正規化します。不正な大文字小文字のロケール識別子を渡しても、正しい正規形式を返します:

const result = Intl.getCanonicalLocales("EN-us");
console.log(result);
// 出力: ["en-US"]

無効なロケール識別子を渡すと、メソッドはRangeErrorをスローします:

try {
  Intl.getCanonicalLocales("en_US");
} catch (error) {
  console.error(error.name);
  // 出力: "RangeError"
  console.error(error.message);
  // 出力: "en_US is not a structurally valid language tag"
}

このエラーは識別子がBCP 47構造に従っていないことを示しています。このエラーをキャッチして無効な入力を適切に処理することができます。

複数のロケール識別子の検証

Intl.getCanonicalLocales()メソッドは配列を受け入れ、複数のロケール識別子を一度に検証します。このメソッドは有効な識別子すべての正規形式の配列を返します。

const locales = ["en-US", "fr-FR", "es-MX"];
const result = Intl.getCanonicalLocales(locales);
console.log(result);
// 出力: ["en-US", "fr-FR", "es-MX"]

このメソッドは結果から重複するロケール識別子も削除します:

const locales = ["en-US", "EN-us", "en-US"];
const result = Intl.getCanonicalLocales(locales);
console.log(result);
// 出力: ["en-US"]

3つの入力値はすべて同じロケールを表すため、メソッドは1つの正規形式のみを返します。

配列内のいずれかのロケールが無効な場合、メソッド全体がエラーをスローします:

try {
  Intl.getCanonicalLocales(["en-US", "invalid", "fr-FR"]);
} catch (error) {
  console.error(error.message);
  // 出力: "invalid is not a structurally valid language tag"
}

ユーザー提供のリストを検証する場合は、どの識別子が無効かを特定するために各ロケールを個別に検証してください。

Intl.Localeコンストラクタを使用した検証

Intl.Localeコンストラクタはロケール識別子を検証するもう一つの方法を提供します。無効な識別子でロケールオブジェクトを作成すると、コンストラクタはRangeErrorをスローします。

try {
  const locale = new Intl.Locale("en-US");
  console.log("Valid locale");
} catch (error) {
  console.error("Invalid locale");
}
// 出力: "Valid locale"

コンストラクタは無効な識別子に対してエラーをスローします:

try {
  const locale = new Intl.Locale("en_US");
  console.log("Valid locale");
} catch (error) {
  console.error("Invalid locale");
  console.error(error.message);
}
// 出力: "Invalid locale"
// 出力: "invalid language subtag: en_US"

Intl.getCanonicalLocales()とは異なり、Intl.Localeコンストラクタは大文字小文字を正規化しません。識別子に正しい大文字小文字の使用が必要です:

const locale1 = new Intl.Locale("en-US");
console.log(locale1.baseName);
// 出力: "en-US"

const locale2 = new Intl.Locale("EN-US");
console.log(locale2.baseName);
// 出力: "en-US"

大文字と小文字の両方の言語コードが受け入れられ、コンストラクタはそれらを正規形式に正規化します。

検証方法の選択

両方の検証方法は異なる目的を果たし、異なる機能を提供します。

以下の場合はIntl.getCanonicalLocales()を使用してください:

  • ロケール識別子を正規形式に正規化する
  • ロケール識別子のリストを検証および重複排除する
  • 一貫性のない大文字小文字を標準形式に変換する
  • オブジェクトを作成せずにロケール識別子を検証する

以下の場合はIntl.Localeコンストラクタを使用してください:

  • ロケール識別子を検証し、すぐにその属性を操作する
  • 識別子から言語、地域、スクリプトなどのコンポーネントを抽出する
  • 他のIntl APIで使用するためのロケールオブジェクトを作成する
  • ロケール識別子を検証および操作する

Intl.Localeコンストラクタはクエリや変更ができるオブジェクトを作成するため、より強力です。ただし、検証と正規化だけが必要な場合は、Intl.getCanonicalLocales()の方がシンプルです。

ユーザー入力の検証

ユーザー入力からロケール識別子を受け取る場合は、アプリケーションで使用する前に検証してください。これによりエラーを防ぎ、ユーザーが無効な値を入力した場合に明確なフィードバックを提供できます。

function validateUserLocale(input) {
  try {
    const canonicalLocales = Intl.getCanonicalLocales(input);
    return {
      valid: true,
      locale: canonicalLocales[0]
    };
  } catch (error) {
    return {
      valid: false,
      error: "Please enter a valid locale identifier like en-US or fr-FR"
    };
  }
}

const result1 = validateUserLocale("en-US");
console.log(result1);
// Output: { valid: true, locale: "en-US" }

const result2 = validateUserLocale("english");
console.log(result2);
// Output: { valid: false, error: "Please enter a valid locale..." }

この関数は入力を検証し、正規化されたロケールまたはユーザーフレンドリーなエラーメッセージを返します。

設定データの検証

アプリケーションは設定ファイルや環境変数からロケール識別子を読み込むことがよくあります。アプリケーション起動時にこれらの値を検証して、設定エラーを早期に発見しましょう。

function loadLocaleConfig(configLocales) {
  const validLocales = [];
  const invalidLocales = [];

  for (const locale of configLocales) {
    try {
      const canonical = Intl.getCanonicalLocales(locale);
      validLocales.push(canonical[0]);
    } catch (error) {
      invalidLocales.push(locale);
    }
  }

  if (invalidLocales.length > 0) {
    console.warn("Invalid locale identifiers found:", invalidLocales);
  }

  return validLocales;
}

const config = ["en-US", "fr-FR", "invalid", "es_MX", "de-DE"];
const locales = loadLocaleConfig(config);
console.log(locales);
// Output: ["en-US", "fr-FR", "de-DE"]
// Warning: Invalid locale identifiers found: ["invalid", "es_MX"]

この関数は無効なロケール識別子をフィルタリングして警告をログに記録し、有効なロケールでアプリケーションを継続できるようにします。

API レスポンスの検証

外部 API からロケール識別子を受け取る場合は、アプリケーションで使用する前に検証してください。API は非標準形式のロケール識別子やエラーを含むロケール識別子を返す可能性があります。

function processApiLocale(apiResponse) {
  const locale = apiResponse.preferredLocale;

  try {
    const validated = new Intl.Locale(locale);
    return {
      success: true,
      language: validated.language,
      region: validated.region,
      baseName: validated.baseName
    };
  } catch (error) {
    console.error("API returned invalid locale:", locale);
    return {
      success: false,
      fallback: "en-US"
    };
  }
}

const response1 = { preferredLocale: "fr-CA" };
console.log(processApiLocale(response1));
// Output: { success: true, language: "fr", region: "CA", baseName: "fr-CA" }

const response2 = { preferredLocale: "invalid_locale" };
console.log(processApiLocale(response2));
// Output: { success: false, fallback: "en-US" }

この関数は API レスポンスを検証し、ロケールに関する構造化された情報を抽出するか、ロケールが無効な場合はフォールバック値を提供します。