如何以不同语言显示国家名称?

使用 Intl.DisplayNames 将国家代码转换为国际用户本地化的国家名称。

简介

当你构建收货地址表单、用户资料页或地址选择器时,需要显示国家名称。用户在英文界面看到 "France",在西班牙语界面看到 "Francia",在德语界面看到 "Frankreich",在日语界面看到 "フランス"。每位用户都希望看到用自己语言书写的国家名称。

你的应用会在数据库和 API 中存储像 FRUSJP 这样的国家代码。这些标准化代码可在所有系统中通用,但用户需要可读的国家名称。Intl.DisplayNames API 可以将国家代码转换为任意语言的本地化国家名称,无需维护翻译表或依赖外部库。

了解国家代码

各国通过 ISO 3166-1 alpha-2 标准定义的两位字母代码进行标识。每个国家都有唯一的代码,在所有语言和系统中都保持不变。

// Common country codes
// US = United States
// GB = Great Britain (United Kingdom)
// FR = France
// DE = Germany (Deutschland)
// JP = Japan
// CN = China
// BR = Brazil
// IN = India

这些代码会出现在表单、URL、数据库和 API 中。代码 US 无论在英文、西班牙文、日文或其他语言环境下,始终代表美国。代码为显示名称提供了稳定的标识符,而显示名称会根据用户的语言变化。

使用 Intl.DisplayNames 获取国家名称

Intl.DisplayNames 构造函数可创建格式化器,将国家代码转换为国家名称。你需要指定 locale,并将 type 设置为 "region",以获取国家名称。

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

console.log(countryNames.of("US"));
// "United States"

console.log(countryNames.of("FR"));
// "France"

console.log(countryNames.of("JP"));
// "Japan"

第一个参数是 locale 标识符数组。type: "region" 选项告诉格式化器你需要国家或地区名称。of() 方法接收国家代码并返回其本地化名称。

“地区”一词涵盖国家、领土和地理区域。这包括像法国这样的独立国家、像波多黎各这样的属地,以及像欧盟这样的特殊区域。

在不同语言中显示国家名称

相同的国家代码在不同语言中会显示不同的名称。为不同的本地化设置创建格式化器,可以看到国家名称的变化。

const englishNames = new Intl.DisplayNames(["en"], { type: "region" });
const spanishNames = new Intl.DisplayNames(["es"], { type: "region" });
const germanNames = new Intl.DisplayNames(["de"], { type: "region" });
const japaneseNames = new Intl.DisplayNames(["ja"], { type: "region" });

console.log(englishNames.of("FR"));
// "France"

console.log(spanishNames.of("FR"));
// "Francia"

console.log(germanNames.of("FR"));
// "Frankreich"

console.log(japaneseNames.of("FR"));
// "フランス"

每个格式化器会返回其显示语言下的国家名称。这简化了跨语言维护国家名称翻译的复杂性。

你可以创建一个函数,用于获取任意语言的国家名称。

function getCountryName(countryCode, locale) {
  const names = new Intl.DisplayNames([locale], { type: "region" });
  return names.of(countryCode);
}

console.log(getCountryName("US", "en"));
// "United States"

console.log(getCountryName("US", "fr"));
// "États-Unis"

console.log(getCountryName("US", "ar"));
// "الولايات المتحدة"

console.log(getCountryName("DE", "en"));
// "Germany"

console.log(getCountryName("DE", "de"));
// "Deutschland"

console.log(getCountryName("DE", "es"));
// "Alemania"

该函数适用于任意国家代码和本地化设置的组合。浏览器会自动提供相应的翻译。

构建国家选择器

一个常见的用例是构建一个下拉菜单,让用户选择自己的国家。国家名称应以用户的语言显示。

function createCountrySelector(locale) {
  const countryNames = new Intl.DisplayNames([locale], { type: "region" });

  const countries = [
    "US", "GB", "CA", "AU", "FR", "DE", "ES", "IT",
    "JP", "CN", "KR", "IN", "BR", "MX", "AR", "RU"
  ];

  const select = document.createElement("select");
  select.id = "country";
  select.name = "country";

  const placeholder = document.createElement("option");
  placeholder.value = "";
  placeholder.textContent = "Select a country";
  select.appendChild(placeholder);

  countries.forEach((code) => {
    const option = document.createElement("option");
    option.value = code;
    option.textContent = countryNames.of(code);
    select.appendChild(option);
  });

  return select;
}

const selector = createCountrySelector("en");
document.body.appendChild(selector);

这会创建一个以英文显示国家名称的下拉菜单。将本地化设置更改为 "es",同一个函数会生成以西班牙语显示国家名称的下拉菜单。更改为 "ja" 时,名称会以日语显示。

你可以让选择器根据用户的浏览器语言做出响应。

function createLocalizedCountrySelector() {
  const userLocale = navigator.language;
  return createCountrySelector(userLocale);
}

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

选择器会根据用户的浏览器设置,自动以其首选语言显示国家名称。

获取所有可用国家

Intl.supportedValuesOf() 方法会返回所有支持的国家代码数组。这样就无需维护硬编码的列表。

const allCountries = Intl.supportedValuesOf("region");

console.log(allCountries.length);
// 249 (approximate count as of 2025)

console.log(allCountries.slice(0, 10));
// ["AC", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ"]

该方法返回的代码按代码字母顺序排列,而不是按国家名称。如果需要按特定语言的字母顺序排列,需要根据本地化名称进行排序。

function getSortedCountries(locale) {
  const countryNames = new Intl.DisplayNames([locale], { type: "region" });
  const allCountries = Intl.supportedValuesOf("region");

  return allCountries
    .map((code) => ({
      code,
      name: countryNames.of(code)
    }))
    .sort((a, b) => a.name.localeCompare(b.name, locale));
}

const sortedEnglish = getSortedCountries("en");
console.log(sortedEnglish.slice(0, 5));
// [
//   { code: "AF", name: "Afghanistan" },
//   { code: "AX", name: "Åland Islands" },
//   { code: "AL", name: "Albania" },
//   { code: "DZ", name: "Algeria" },
//   { code: "AS", name: "American Samoa" }
// ]

const sortedSpanish = getSortedCountries("es");
console.log(sortedSpanish.slice(0, 5));
// [
//   { code: "AF", name: "Afganistán" },
//   { code: "AL", name: "Albania" },
//   { code: "DE", name: "Alemania" },
//   { code: "AD", name: "Andorra" },
//   { code: "AO", name: "Angola" }
// ]

不同语言的排序顺序会有所不同,因为各国语名在不同的字母表和排序规则下排序方式不同。

构建全面的国家选择器

Intl.supportedValuesOf()Intl.DisplayNames 结合,创建一个包含所有可用国家的选择器。

function createComprehensiveCountrySelector(locale) {
  const countryNames = new Intl.DisplayNames([locale], { type: "region" });
  const allCountries = Intl.supportedValuesOf("region");

  const sortedCountries = allCountries
    .map((code) => ({
      code,
      name: countryNames.of(code)
    }))
    .sort((a, b) => a.name.localeCompare(b.name, locale));

  const select = document.createElement("select");
  select.id = "country";
  select.name = "country";

  const placeholder = document.createElement("option");
  placeholder.value = "";
  placeholder.textContent = "Select a country";
  select.appendChild(placeholder);

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

  return select;
}

const selector = createComprehensiveCountrySelector("en");
document.body.appendChild(selector);

该选择器包含浏览器支持的全部 249 个国家和地区,并按照用户语言的字母顺序排序。

处理无效的国家代码

并非所有的两位字符串都是有效的国家代码。of() 方法会根据 fallback 选项处理无效代码。

const withCodeFallback = new Intl.DisplayNames(["en"], {
  type: "region",
  fallback: "code"
});

const withNoneFallback = new Intl.DisplayNames(["en"], {
  type: "region",
  fallback: "none"
});

console.log(withCodeFallback.of("US"));
// "United States"

console.log(withCodeFallback.of("XX"));
// "XX"

console.log(withNoneFallback.of("US"));
// "United States"

console.log(withNoneFallback.of("XX"));
// undefined

fallback: "code" 选项在没有对应国家时返回输入的代码。fallback: "none" 选项在代码无效时返回 undefined

当你需要检测无效代码并显式处理时,请使用 fallback: "none"

function getValidatedCountryName(code, locale) {
  const names = new Intl.DisplayNames([locale], {
    type: "region",
    fallback: "none"
  });

  const name = names.of(code);

  if (name === undefined) {
    return "Unknown country";
  }

  return name;
}

console.log(getValidatedCountryName("US", "en"));
// "United States"

console.log(getValidatedCountryName("INVALID", "en"));
// "Unknown country"

此模式有助于你校验用户输入或外部数据源的数据。

使用样式控制国家名称长度

style 选项用于控制国家名称的显示方式。三种取值会产生不同长度的输出。

const longNames = new Intl.DisplayNames(["en"], {
  type: "region",
  style: "long"
});

const shortNames = new Intl.DisplayNames(["en"], {
  type: "region",
  style: "short"
});

const narrowNames = new Intl.DisplayNames(["en"], {
  type: "region",
  style: "narrow"
});

console.log(longNames.of("US"));
// "United States"

console.log(shortNames.of("US"));
// "US"

console.log(narrowNames.of("US"));
// "US"

long 样式为默认值,显示完整国家名称。shortnarrow 样式在可用时返回缩写形式。对于大多数国家,short 和 narrow 样式会直接返回国家代码。

部分国家有独特的简称。

const longNames = new Intl.DisplayNames(["en"], {
  type: "region",
  style: "long"
});

const shortNames = new Intl.DisplayNames(["en"], {
  type: "region",
  style: "short"
});

console.log(longNames.of("GB"));
// "United Kingdom"

console.log(shortNames.of("GB"));
// "UK"

console.log(longNames.of("BO"));
// "Bolivia"

console.log(shortNames.of("BO"));
// "Bolivia"

大多数界面建议使用默认的 long 样式。在移动端导航或紧凑表格等空间有限的场景下,可使用 shortnarrow

显示地区名称

ISO 3166-1 标准不仅包括独立国家,还涵盖了属地、附属地和特殊区域。Intl.DisplayNames API 同样支持这些地区。

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

console.log(regionNames.of("PR"));
// "Puerto Rico"

console.log(regionNames.of("GU"));
// "Guam"

console.log(regionNames.of("HK"));
// "Hong Kong"

console.log(regionNames.of("MQ"));
// "Martinique"

console.log(regionNames.of("GF"));
// "French Guiana"

这些代码的用法与国家代码相同。您的应用可以统一处理它们,浏览器会自动提供相应的本地化名称。

使用数字地区代码

ISO 3166-1 标准还定义了数字代码。Intl.DisplayNames API 除了支持两字母代码外,也接受联合国 M.49 数字地区代码。

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

console.log(regionNames.of("840"));
// "United States"

console.log(regionNames.of("250"));
// "France"

console.log(regionNames.of("392"));
// "Japan"

数字代码还可用于表示更大的地理区域。

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

console.log(regionNames.of("150"));
// "Europe"

console.log(regionNames.of("019"));
// "Americas"

console.log(regionNames.of("142"));
// "Asia"

console.log(regionNames.of("002"));
// "Africa"

console.log(regionNames.of("009"));
// "Oceania"

这些代码可跨语言使用。

const englishRegions = new Intl.DisplayNames(["en"], { type: "region" });
const spanishRegions = new Intl.DisplayNames(["es"], { type: "region" });

console.log(englishRegions.of("150"));
// "Europe"

console.log(spanishRegions.of("150"));
// "Europa"

当需要避免字符编码问题或与使用联合国 M.49 代码的系统对接时,可以使用数字代码。

缓存 DisplayNames 实例以提升性能

创建 Intl.DisplayNames 实例的开销很小,但如果应用需要转换大量国家代码,缓存格式化器会带来性能提升。

const displayNamesCache = new Map();

function getDisplayNames(locale, type) {
  const key = `${locale}-${type}`;

  if (!displayNamesCache.has(key)) {
    displayNamesCache.set(
      key,
      new Intl.DisplayNames([locale], { type })
    );
  }

  return displayNamesCache.get(key);
}

function getCountryName(code, locale) {
  const formatter = getDisplayNames(locale, "region");
  return formatter.of(code);
}

console.log(getCountryName("US", "en"));
// "United States"

console.log(getCountryName("FR", "en"));
// "France"

console.log(getCountryName("US", "es"));
// "Estados Unidos"

缓存会根据 locale 和 type 存储格式化器。后续调用会复用已有的格式化器,而不是每次都新建。

当需要渲染大量国家列表或在表格、数据网格中处理数百个国家代码时,这种优化尤为重要。

处理 locale 回退

Intl.DisplayNames 构造函数可以接受一个 locale 数组。如果第一个 locale 不被支持,浏览器会自动回退到数组中的下一个。

const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "region" });

console.log(names.of("US"));
// "United States"

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

你可以检查格式化器实际使用的是哪个 locale。

const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "region" });

console.log(names.resolvedOptions().locale);
// "en"

resolvedOptions() 方法会返回格式化器在回退处理后最终确定的 locale。

跨语言对比国家名称

不同语言对国家名称的格式化方式各不相同。有些语言会将国家名称首字母大写,有些则不会。有些语言包含冠词,有些则不包含。

const english = new Intl.DisplayNames(["en"], { type: "region" });
const french = new Intl.DisplayNames(["fr"], { type: "region" });
const german = new Intl.DisplayNames(["de"], { type: "region" });

console.log(english.of("US"));
// "United States"

console.log(french.of("US"));
// "États-Unis"

console.log(german.of("US"));
// "Vereinigte Staaten"

console.log(english.of("NL"));
// "Netherlands"

console.log(french.of("NL"));
// "Pays-Bas"

console.log(german.of("NL"));
// "Niederlande"

格式化器会自动处理所有这些语言习惯。你无需了解每种语言的语法规则。

浏览器支持

Intl.DisplayNames API 及 type: "region" 在所有现代浏览器中均可用。从 2021 年起,主流浏览器(包括 Chrome、Firefox、Safari 和 Edge)都已支持。

现代应用可以直接使用该 API,无需 polyfill 或回退。浏览器会维护国家名称数据,并在国家和地区变化时保持更新。

你可以在使用前检查该 API 是否可用。

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

对于需要支持旧版浏览器的应用,可以通过静态国家名称查找表提供回退方案。

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

  const fallbackNames = {
    US: "United States",
    GB: "United Kingdom",
    FR: "France",
    DE: "Germany",
    JP: "Japan"
  };

  return fallbackNames[code] || code;
}

console.log(getCountryName("US", "en"));
// "United States"

这样可以确保你的应用在所有浏览器中都能正常工作,但旧版浏览器将无法自动本地化。