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"
  }
  // ... その他の言語
};

これはすぐに維持できなくなります。より良い解決策が必要です。

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);
}

// ユーザーインターフェースが英語の場合
console.log(getLocalizedLanguageName("ja", "en"));
// "Japanese"

// ユーザーインターフェースがフランス語の場合
console.log(getLocalizedLanguageName("ja", "fr"));
// "japonais"

// ユーザーインターフェースがスペイン語の場合
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})`;
}

// ユーザーインターフェースが英語の場合
console.log(createHybridLabel("ja", "en"));
// "日本語 (Japanese)"

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

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

// ユーザーインターフェースがスペイン語の場合
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);
  }

  // 古いブラウザ向けのフォールバック
  const fallbackNames = {
    en: "English",
    es: "español",
    fr: "français",
    de: "Deutsch",
    ja: "日本語"
  };

  return fallbackNames[code] || code;
}

console.log(getLanguageName("es", "en"));
// "español"(またはフォールバックをローカライズ用に調整した場合は「Spanish」)

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