English、Español、日本語のような言語名を表示するには?

Intl.DisplayNamesを使用して、言語セレクターや国際化されたインターフェースで言語名をネイティブスクリプトで表示します。

はじめに

言語セレクターを構築したり、利用可能な言語のリストを表示したりする場合、ユーザーが認識できる方法で各言語名を表示する必要があります。フランス語話者は「Français」を探し、スペイン語話者は「Español」を探し、日本語話者は「日本語」を探します。ユーザーは、ネイティブスクリプトとスペルで自分の言語を識別します。

これらの翻訳をハードコーディングすることはスケールしません。すべての言語名を他のすべての言語に翻訳して維持する必要があります。Intl.DisplayNames APIは、言語、国、スクリプト、通貨の標準化されたロケール対応の名前を提供することで、この問題を解決します。

言語名をハードコーディングする問題

オブジェクトに言語名をハードコーディングして言語セレクターを作成できます。

const languageNames = {
  en: "English",
  es: "Spanish",
  fr: "French",
  de: "German",
  ja: "Japanese"
};

console.log(languageNames.en);
// "English"

このアプローチには3つの問題があります。第一に、これらの名前は英語話者にのみ機能します。第二に、ユーザーは英語で表示された言語を認識できません。日本語ユーザーは自分の言語を探すとき、「Japanese」という単語ではなく日本語の文字を探します。第三に、すべての言語を他のすべての言語に翻訳して手動で維持する必要があります。

const languageNames = {
  en: {
    en: "English",
    es: "Spanish",
    fr: "French",
    de: "German",
    ja: "Japanese"
  },
  es: {
    en: "Inglés",
    es: "Español",
    fr: "Francés",
    de: "Alemán",
    ja: "Japonés"
  }
  // ... more languages
};

これはすぐに保守不可能になります。より良いソリューションが必要です。

Intl.DisplayNamesを使用して言語名を取得する

Intl.DisplayNamesコンストラクターは、言語コードを人間が読める名前に変換するフォーマッターを作成します。ロケールと表示したい名前のタイプを指定します。

const names = new Intl.DisplayNames(["en"], { type: "language" });
console.log(names.of("en"));
// "English"

最初の引数はロケール識別子の配列です。2番目の引数はオプションオブジェクトで、type: "language"はフォーマッターに言語名が必要であることを伝えます。of()メソッドは言語コードを受け取り、その名前を返します。

ロケールを変更することで、任意の言語で名前を取得できます。

const enNames = new Intl.DisplayNames(["en"], { type: "language" });
const esNames = new Intl.DisplayNames(["es"], { type: "language" });
const frNames = new Intl.DisplayNames(["fr"], { type: "language" });

console.log(enNames.of("es"));
// "Spanish"

console.log(esNames.of("es"));
// "español"

console.log(frNames.of("es"));
// "espagnol"

各フォーマッターは、その表示ロケールで言語名を返します。これにより、言語名の翻訳を維持する複雑さがすべて処理されます。

言語名をネイティブ形式で表示する

言語セレクターのベストプラクティスは、各言語をその言語固有の文字で表示することです。ユーザーは、見慣れた文字で書かれた言語をより速く認識できます。

const names = new Intl.DisplayNames(["ja"], { type: "language" });
console.log(names.of("ja"));
// "日本語"

各言語のネイティブ名を取得するには、対象言語を表示ロケールとして使用してフォーマッターを作成します。

function getNativeName(languageCode) {
  const names = new Intl.DisplayNames([languageCode], { type: "language" });
  return names.of(languageCode);
}

console.log(getNativeName("en"));
// "English"

console.log(getNativeName("es"));
// "español"

console.log(getNativeName("fr"));
// "français"

console.log(getNativeName("de"));
// "Deutsch"

console.log(getNativeName("ja"));
// "日本語"

console.log(getNativeName("ar"));
// "العربية"

console.log(getNativeName("zh"));
// "中文"

このパターンは、任意の言語コードで機能します。フォーマッターは、ネイティブスピーカーが使用する文字と形式で名前を返します。

言語コード形式を理解する

of()メソッドは、複数の形式の言語コードを受け入れます。"en"のような基本的な言語コードや、"en-US"のような完全なロケール識別子を使用できます。

const names = new Intl.DisplayNames(["en"], { type: "language" });

console.log(names.of("en"));
// "English"

console.log(names.of("en-US"));
// "American English"

console.log(names.of("en-GB"));
// "British English"

console.log(names.of("zh"));
// "Chinese"

console.log(names.of("zh-Hans"));
// "Simplified Chinese"

console.log(names.of("zh-Hant"));
// "Traditional Chinese"

フォーマッターは、短いコードと、地域または文字サブタグを含む拡張識別子の両方を認識します。これにより、言語のバリエーションを区別できます。

言語名の表示方法を制御する

languageDisplayオプションは、返される名前の詳細レベルを制御します。2つの値を受け入れます。

"standard"値は、方言情報を含む完全な名前を返します。これがデフォルトです。

const names = new Intl.DisplayNames(["en"], {
  type: "language",
  languageDisplay: "standard"
});

console.log(names.of("en-US"));
// "American English"

console.log(names.of("en-GB"));
// "British English"

console.log(names.of("pt-BR"));
// "Brazilian Portuguese"

console.log(names.of("pt-PT"));
// "European Portuguese"

"dialect"値も、方言情報を含む完全な名前を返します。ほとんどの場合、"standard"と同じ出力を生成します。

const names = new Intl.DisplayNames(["en"], {
  type: "language",
  languageDisplay: "dialect"
});

console.log(names.of("en-US"));
// "American English"

console.log(names.of("pt-BR"));
// "Brazilian Portuguese"

言語セレクターの場合、標準形式は、複数の方言が利用可能な場合にユーザーが正しいバリエーションを選択するのに役立ちます。

ユーザーインターフェース用にローカライズされた言語名を取得する

設定ページや言語セレクターを構築する際、ユーザーの現在のインターフェース言語で言語名を表示する必要があります。ユーザーのロケールを使用してフォーマッターを作成します。

function getLocalizedLanguageName(languageCode, userLocale) {
  const names = new Intl.DisplayNames([userLocale], { type: "language" });
  return names.of(languageCode);
}

// User interface is in English
console.log(getLocalizedLanguageName("ja", "en"));
// "Japanese"

// User interface is in French
console.log(getLocalizedLanguageName("ja", "fr"));
// "japonais"

// User interface is in Spanish
console.log(getLocalizedLanguageName("ja", "es"));
// "japonés"

このアプローチにより、ユーザーが理解できる言語で説明的な名前が表示されます。これをネイティブ名と組み合わせて、「日本語 (Japanese)」のようなハイブリッドラベルを作成することで、ネイティブスピーカーとそれ以外の両方に対応できます。

ネイティブ名を使用した言語セレクターの構築

一般的なユースケースは、ユーザーが希望する言語を選択するドロップダウンまたはリストの構築です。各言語をネイティブ形式で表示することで、ユーザーが自分のオプションを素早く見つけられるようにします。

const supportedLanguages = ["en", "es", "fr", "de", "ja", "ar", "zh"];

function createLanguageOptions() {
  return supportedLanguages.map((code) => {
    const names = new Intl.DisplayNames([code], { type: "language" });
    const nativeName = names.of(code);
    return { code, name: nativeName };
  });
}

const options = createLanguageOptions();
console.log(options);

これにより、ネイティブ名を持つ言語オプションの配列が生成されます。

[
  { code: "en", name: "English" },
  { code: "es", name: "español" },
  { code: "fr", name: "français" },
  { code: "de", name: "Deutsch" },
  { code: "ja", name: "日本語" },
  { code: "ar", name: "العربية" },
  { code: "zh", name: "中文" }
]

これらのオプションをHTMLでレンダリングして、言語セレクターを作成できます。

function renderLanguageSelector() {
  const options = createLanguageOptions();
  const select = document.createElement("select");
  select.id = "language-selector";

  options.forEach((option) => {
    const optionElement = document.createElement("option");
    optionElement.value = option.code;
    optionElement.textContent = option.name;
    select.appendChild(optionElement);
  });

  return select;
}

const selector = renderLanguageSelector();
document.body.appendChild(selector);

これにより、各言語がネイティブスクリプトで表示されるドロップダウンが作成され、ユーザーが自分の言語を簡単に識別できるようになります。

ハイブリッド言語ラベルの作成

一部のインターフェースでは、ネイティブ名とユーザーの言語での翻訳の両方を表示します。これにより、すべてのスクリプトを認識できないユーザーを支援し、インターフェースのアクセシビリティも向上します。

function createHybridLabel(languageCode, userLocale) {
  const nativeNames = new Intl.DisplayNames([languageCode], {
    type: "language"
  });
  const localizedNames = new Intl.DisplayNames([userLocale], {
    type: "language"
  });

  const nativeName = nativeNames.of(languageCode);
  const localizedName = localizedNames.of(languageCode);

  if (nativeName === localizedName) {
    return nativeName;
  }

  return `${nativeName} (${localizedName})`;
}

// User interface is in English
console.log(createHybridLabel("ja", "en"));
// "日本語 (Japanese)"

console.log(createHybridLabel("ar", "en"));
// "العربية (Arabic)"

console.log(createHybridLabel("en", "en"));
// "English"

// User interface is in Spanish
console.log(createHybridLabel("ja", "es"));
// "日本語 (japonés)"

このパターンは、ネイティブ名の認識メリットとローカライズされた翻訳の明確性を組み合わせています。

フォールバックロケールの処理

Intl.DisplayNamesコンストラクターは、ロケールの配列を受け入れます。最初のロケールが利用できない場合、フォーマッターは配列内の次のロケールにフォールバックします。

const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "language" });
console.log(names.of("fr"));
// "French"

フォーマッターは最初に"xx-XX"を試行しますが、これは存在しないため、"en"にフォールバックします。これにより、要求されたロケールが利用できない場合でもコードが機能することが保証されます。

フォーマッターが実際に使用するロケールは、resolvedOptions()メソッドで確認できます。

const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "language" });
console.log(names.resolvedOptions().locale);
// "en"

これは、フォールバック後にフォーマッターが英語に解決されたことを示しています。

各ロケールにおける言語名のフォーマット方法

各ロケールには、言語名に関する独自の規則があります。フォーマッターはこれらを自動的に適用します。

const supportedLanguages = ["en", "es", "fr", "de", "ja"];

function showLanguageNamesInLocale(locale) {
  const names = new Intl.DisplayNames([locale], { type: "language" });
  return supportedLanguages.map((code) => ({
    code,
    name: names.of(code)
  }));
}

console.log(showLanguageNamesInLocale("en"));
// [
//   { code: "en", name: "English" },
//   { code: "es", name: "Spanish" },
//   { code: "fr", name: "French" },
//   { code: "de", name: "German" },
//   { code: "ja", name: "Japanese" }
// ]

console.log(showLanguageNamesInLocale("es"));
// [
//   { code: "en", name: "inglés" },
//   { code: "es", name: "español" },
//   { code: "fr", name: "francés" },
//   { code: "de", name: "alemán" },
//   { code: "ja", name: "japonés" }
// ]

console.log(showLanguageNamesInLocale("ja"));
// [
//   { code: "en", name: "英語" },
//   { code: "es", name: "スペイン語" },
//   { code: "fr", name: "フランス語" },
//   { code: "de", name: "ドイツ語" },
//   { code: "ja", name: "日本語" }
// ]

フォーマッターは、各言語の大文字小文字の使い分け、文字体系、言語規則を自動的に処理します。

ブラウザサポート

Intl.DisplayNames APIは、すべてのモダンブラウザで利用可能です。Chrome、Firefox、Safari、Edgeを含む主要ブラウザで2021年3月からサポートされています。

APIを使用する前に、利用可能かどうかを確認できます。

if (typeof Intl.DisplayNames !== "undefined") {
  const names = new Intl.DisplayNames(["en"], { type: "language" });
  console.log(names.of("fr"));
} else {
  console.log("Intl.DisplayNames is not supported");
}

古いブラウザの場合は、フォールバックを提供するか、ポリフィルを使用する必要があります。シンプルなフォールバックでは、言語コードと名前のハードコードされたマッピングを使用します。

function getLanguageName(code, locale) {
  if (typeof Intl.DisplayNames !== "undefined") {
    const names = new Intl.DisplayNames([locale], { type: "language" });
    return names.of(code);
  }

  // Fallback for older browsers
  const fallbackNames = {
    en: "English",
    es: "español",
    fr: "français",
    de: "Deutsch",
    ja: "日本語"
  };

  return fallbackNames[code] || code;
}

console.log(getLanguageName("es", "en"));
// "español" (or "Spanish" if you adjust the fallback for localization)

これにより、Intl.DisplayNamesをサポートしていないブラウザでもコードが動作しますが、自動ローカライゼーション機能は失われます。