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

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

はじめに

アプリケーションがユーザー入力、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);
// Output: ["en-US"]

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

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

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

try {
  Intl.getCanonicalLocales("en_US");
} catch (error) {
  console.error(error.name);
  // Output: "RangeError"
  console.error(error.message);
  // Output: "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);
// Output: ["en-US", "fr-FR", "es-MX"]

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

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

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

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

try {
  Intl.getCanonicalLocales(["en-US", "invalid", "fr-FR"]);
} catch (error) {
  console.error(error.message);
  // Output: "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");
}
// Output: "Valid locale"

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

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

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

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

const locale2 = new Intl.Locale("EN-US");
console.log(locale2.baseName);
// Output: "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レスポンスを検証し、ロケールに関する構造化された情報を抽出します。ロケールが無効な場合はフォールバック値を提供します。