如何显示语言名称,如 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"

这种方式有三个问题。首先,这些名称只适用于英语用户。其次,用户在看到英文时无法识别自己的语言。例如,日本用户查找时会寻找日文字符,而不是“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"

第一个参数是本地标识符数组。第二个参数是选项对象,其中 type: "language" 指定格式化器返回语言名称。of() 方法接收语言代码并返回其名称。

只需更改 locale,即可获取任意语言的名称。

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"

每个 formatter 都会根据显示 locale 返回对应的语言名称,无需手动维护语言名称的翻译。

以本地形式显示语言名称

语言选择器的最佳实践是用各自的文字脚本显示每种语言。用户在看到熟悉的字符时能更快识别自己的语言。

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

要获取每种语言的本地名称,请使用目标语言作为显示 locale 创建 formatter。

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"));
// "中文"

这种模式适用于任意语言代码。formatter 会以母语者常用的脚本和形式返回名称。

了解语言代码格式

of() 方法支持多种语言代码格式。你可以使用基础语言代码(如 "en"),也可以用完整的 locale 标识符(如 "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"

formatter 能识别简短代码和带有地区或脚本子标签的扩展标识符,从而区分不同的语言变体。

控制语言名称的显示方式

languageDisplay 选项用于控制返回名称的详细程度,可设置两个值。

"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"

对于语言选择器,标准格式有助于用户在存在多种方言时选择正确的变体。

获取用于用户界面的本地化语言名称

在构建设置页面或语言选择器时,需要以用户当前界面语言显示语言名称。请使用用户的 locale 创建格式化器。

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 构造函数接受一个 locale 数组。如果第一个 locale 不可用,格式化器会回退到数组中的下一个 locale。

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

格式化器会先尝试 "xx-XX"(该项不存在),然后回退到 "en"。这样可以确保即使请求的 locale 不可用,代码也能正常工作。

你可以使用 resolvedOptions() 方法来检查格式化器实际使用的是哪个 locale。

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

这表明在回退后,格式化器解析为英文。

不同 locale 如何格式化语言名称

每个 locale 都有自己的语言名称规范。格式化器会自动应用这些规范。

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 在所有现代浏览器中都可用。从 2021 年 3 月起,主流浏览器(包括 Chrome、Firefox、Safari 和 Edge)都已支持。

你可以在使用前检查该 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");
}

对于旧版浏览器,你需要提供回退方案或使用 polyfill。一个简单的回退方式是使用硬编码的语言代码到名称的映射。

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 的浏览器中也能运行,但会失去自动本地化功能。