如何获取支持的日历、货币和时区列表

了解您的 JavaScript 环境支持哪些国际化值

简介

在开发国际化应用时,通常需要创建下拉菜单、选择器或表单等用户界面元素,让用户选择日历、货币、时区或其他本地化选项。为了构建这些组件,您需要了解 JavaScript 环境支持哪些值。

与其维护容易过时或包含浏览器不支持值的硬编码列表,不如利用 JavaScript 在运行时动态获取支持的值。这样可以确保您的应用只向用户提供在其环境中能够正常使用的选项。

Intl.supportedValuesOf() 方法可以返回支持的国际化值列表。您可以通过调用该方法获取当前 JavaScript 环境支持的日历、货币、时区、数字系统、排序类型和计量单位等。

Intl.supportedValuesOf 方法返回内容

Intl.supportedValuesOf() 方法接受一个字符串参数,用于指定要返回哪一类值。它会返回一个字符串数组,表示该类型下所有受支持的值。

const calendars = Intl.supportedValuesOf("calendar");
console.log(calendars);
// Output: ["buddhist", "chinese", "coptic", "dangi", ...]

该方法支持六种不同的键类型:

  • "calendar" 返回支持的日历系统
  • "currency" 返回支持的货币代码
  • "timeZone" 返回支持的时区标识符
  • "numberingSystem" 返回支持的数字系统
  • "collation" 返回支持的排序类型
  • "unit" 返回支持的计量单位

返回的数组始终按字母升序排列,不包含重复值,并且采用符合 Unicode 标准的规范标识符。

如果传入无效的 key,该方法会抛出 RangeError

try {
  Intl.supportedValuesOf("invalid");
} catch (error) {
  console.error(error.name);
  // Output: "RangeError"
}

该错误表示你使用了方法无法识别的 key。

获取支持的日历

日历系统定义了日期的计算和显示方式。不同文化使用不同的日历系统,JavaScript 通过 Intl API 支持多种日历。

"calendar" key 会返回 JavaScript 环境支持的所有日历系统:

const calendars = Intl.supportedValuesOf("calendar");
console.log(calendars);
// Output: ["buddhist", "chinese", "coptic", "dangi", "ethioaa",
//          "ethiopic", "gregory", "hebrew", "indian", "islamic",
//          "islamic-civil", "islamic-rgsa", "islamic-tbla",
//          "islamic-umalqura", "iso8601", "japanese", "persian",
//          "roc"]

最常用的日历是 "gregory",即全球大多数地区使用的公历。其他日历包括:

  • "buddhist":泰国佛历
  • "chinese":中国农历
  • "islamic":伊斯兰历(Hijri)
  • "hebrew":希伯来历
  • "japanese":带有年号的日本历

每种日历都会影响日期的格式化和计算方式。当你在 Intl.DateTimeFormat 中指定某个日历时,日期会按照该日历的规则显示:

const date = new Date("2025-10-15");

const gregorianFormat = new Intl.DateTimeFormat("en-US", {
  calendar: "gregory",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const islamicFormat = new Intl.DateTimeFormat("en-US", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(gregorianFormat.format(date));
// Output: "October 15, 2025"

console.log(islamicFormat.format(date));
// Output: "Rabi' II 13, 1447 AH"

同一个 JavaScript 日期,使用不同日历格式化时会有不同的显示效果。

利用支持的日历构建选择器

在用户界面中构建日历选择器时,可以查询支持的日历并为每个日历创建选项:

const calendars = Intl.supportedValuesOf("calendar");

const select = document.createElement("select");

calendars.forEach(calendar => {
  const option = document.createElement("option");
  option.value = calendar;
  option.textContent = calendar;
  select.appendChild(option);
});

document.body.appendChild(select);

这样会创建一个只包含当前环境可用日历的下拉菜单。

你可以通过 Intl.DisplayNames 显示更易读的日历名称,提升选择器体验:

const calendars = Intl.supportedValuesOf("calendar");
const displayNames = new Intl.DisplayNames(["en-US"], { type: "calendar" });

calendars.forEach(calendar => {
  const option = document.createElement("option");
  option.value = calendar;
  option.textContent = displayNames.of(calendar);
  // Creates options like "Gregorian Calendar", "Islamic Calendar", etc.
});

这样可以显示描述性名称,而不是技术标识符。

获取支持的货币

货币代码用于标识全球范围内的货币体系。JavaScript 支持基于 ISO 4217 标准的数百种货币代码。

"currency" 键会返回 JavaScript 环境支持的所有货币代码:

const currencies = Intl.supportedValuesOf("currency");
console.log(currencies.length);
// Output: typically over 300

console.log(currencies.slice(0, 10));
// Output: ["ADP", "AED", "AFA", "AFN", "ALK", "ALL", "AMD", "ANG", "AOA", "AOK"]

货币代码由三个大写字母组成。常见示例包括:

  • "USD" 表示美元
  • "EUR" 表示欧元
  • "GBP" 表示英镑
  • "JPY" 表示日元
  • "CNY" 表示人民币

该列表包含当前流通的货币以及部分已停止流通的历史货币。

在格式化货币数值时,必须指定所使用的货币:

const amount = 1234.56;

const usdFormat = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});

const eurFormat = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "EUR"
});

console.log(usdFormat.format(amount));
// Output: "$1,234.56"

console.log(eurFormat.format(amount));
// Output: "€1,234.56"

货币代码决定了显示的符号以及数值的格式。

为用户筛选货币

大多数应用只需展示当前常用的货币。你可以筛选支持的货币列表,仅包含对用户相关的货币:

const allCurrencies = Intl.supportedValuesOf("currency");
const commonCurrencies = ["USD", "EUR", "GBP", "JPY", "CNY", "INR", "CAD", "AUD"];

const availableCurrencies = commonCurrencies.filter(currency =>
  allCurrencies.includes(currency)
);

console.log(availableCurrencies);
// Output: ["USD", "EUR", "GBP", "JPY", "CNY", "INR", "CAD", "AUD"]

这样可以确保你只提供既符合需求又受浏览器支持的货币。

显示货币名称

在构建货币选择器时,建议显示描述性名称而不是三字母代码:

const currencies = ["USD", "EUR", "GBP", "JPY"];
const displayNames = new Intl.DisplayNames(["en-US"], { type: "currency" });

currencies.forEach(currency => {
  const name = displayNames.of(currency);
  console.log(`${currency}: ${name}`);
});
// Output:
// USD: US Dollar
// EUR: Euro
// GBP: British Pound
// JPY: Japanese Yen

这样可以通过展示每个代码所代表的内容,提升用户体验。

获取支持的时区

时区表示采用相同标准时间的地理区域。JavaScript 支持基于 IANA 时区数据库的数百种时区标识符。

"timeZone" 键会返回 JavaScript 环境支持的所有时区标识符:

const timeZones = Intl.supportedValuesOf("timeZone");
console.log(timeZones.length);
// Output: typically over 400

console.log(timeZones.slice(0, 10));
// Output: ["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa",
//          "Africa/Algiers", "Africa/Asmara", "Africa/Bamako",
//          "Africa/Bangui", "Africa/Banjul", "Africa/Bissau", "Africa/Blantyre"]

时区标识符遵循 Continent/City 格式,例如 "America/New_York""Europe/London"。部分标识符还包含如 "America/Indiana/Indianapolis" 等附加组件。

在格式化日期和时间时,时区会影响显示的本地时间:

const date = new Date("2025-10-15T12:00:00Z");

const newYorkFormat = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric",
  timeZoneName: "short"
});

const tokyoFormat = new Intl.DateTimeFormat("en-US", {
  timeZone: "Asia/Tokyo",
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric",
  timeZoneName: "short"
});

console.log(newYorkFormat.format(date));
// Output: "October 15, 2025, 8:00 AM EDT"

console.log(tokyoFormat.format(date));
// Output: "October 15, 2025, 9:00 PM JST"

同一时刻在不同的时区会显示为不同的本地时间。

构建时区选择器

时区选择器需要以用户易于理解的方式呈现数百个选项。按区域分组时区可以简化选择过程:

const timeZones = Intl.supportedValuesOf("timeZone");

const grouped = timeZones.reduce((groups, tz) => {
  const [region] = tz.split("/");
  if (!groups[region]) {
    groups[region] = [];
  }
  groups[region].push(tz);
  return groups;
}, {});

console.log(Object.keys(grouped));
// Output: ["Africa", "America", "Antarctica", "Arctic", "Asia",
//          "Atlantic", "Australia", "Europe", "Indian", "Pacific", "Etc"]

这种组织方式按洲分组时区,便于用户查找自己的位置。

通过显示每个时区的当前偏移量,可以进一步提升用户体验:

function getTimeZoneOffset(timeZone) {
  const date = new Date();
  const formatter = new Intl.DateTimeFormat("en-US", {
    timeZone,
    timeZoneName: "shortOffset"
  });
  const parts = formatter.formatToParts(date);
  const offsetPart = parts.find(part => part.type === "timeZoneName");
  return offsetPart ? offsetPart.value : "";
}

const timeZones = ["America/New_York", "Europe/London", "Asia/Tokyo"];

timeZones.forEach(tz => {
  const offset = getTimeZoneOffset(tz);
  console.log(`${tz}: ${offset}`);
});
// Output:
// America/New_York: GMT-4
// Europe/London: GMT+1
// Asia/Tokyo: GMT+9

显示偏移量有助于用户了解各时区之间的时差。

获取支持的数字系统

数字系统定义了数字的显示方式。虽然大多数应用程序使用标准的阿拉伯数字(0-9),但许多语言有各自的传统数字系统。

"numberingSystem" 键会返回 JavaScript 环境支持的所有数字系统:

const numberingSystems = Intl.supportedValuesOf("numberingSystem");
console.log(numberingSystems.slice(0, 10));
// Output: ["adlm", "ahom", "arab", "arabext", "armn", "armnlow",
//          "bali", "beng", "bhks", "brah"]

常见的数字系统包括:

  • "latn":标准拉丁数字(0-9)
  • "arab":阿拉伯-印度数字
  • "thai":泰国数字
  • "deva":印地语使用的天城文数字

不同的数字系统会以不同的数字形状显示相同的数值:

const number = 12345;

const latinFormat = new Intl.NumberFormat("en-US", {
  numberingSystem: "latn"
});

const arabFormat = new Intl.NumberFormat("en-US", {
  numberingSystem: "arab"
});

const thaiFormat = new Intl.NumberFormat("en-US", {
  numberingSystem: "thai"
});

console.log(latinFormat.format(number));
// Output: "12,345"

console.log(arabFormat.format(number));
// Output: "١٢٬٣٤٥"

console.log(thaiFormat.format(number));
// Output: "๑๒,๓๔๕"

相同的数值会以不同的数字形态显示。

获取支持的排序类型

排序类型定义了字符串的排序和比较方式。不同语言有不同的字母排序规则。

"collation" 键会返回 JavaScript 环境支持的所有排序规则类型:

const collations = Intl.supportedValuesOf("collation");
console.log(collations.slice(0, 10));
// Output: ["big5han", "compat", "dict", "direct", "ducet", "emoji",
//          "eor", "gb2312", "phonebk", "phonetic"]

常见的排序规则类型包括:

  • "standard" 表示每种语言的默认排序规则
  • "phonetic" 表示按发音排序
  • "pinyin" 表示中文拼音排序
  • "emoji" 表示 emoji 排序

排序规则会影响字符串数组的排序方式:

const words = ["ä", "z", "a"];

const standardCollator = new Intl.Collator("de-DE", {
  collation: "standard"
});

const phoneticCollator = new Intl.Collator("de-DE", {
  collation: "phonetic"
});

console.log(words.sort(standardCollator.compare));
// Output: ["a", "ä", "z"]

console.log(words.sort(phoneticCollator.compare));
// Output: ["a", "ä", "z"]

不同的排序规则类型会对相同输入产生不同的排序结果。

获取支持的单位

单位标识符表示测量单位,如米、加仑或摄氏度。JavaScript 支持多种测量单位的格式化。

"unit" 键会返回 JavaScript 环境支持的所有单位标识符:

const units = Intl.supportedValuesOf("unit");
console.log(units.slice(0, 10));
// Output: ["acre", "bit", "byte", "celsius", "centimeter", "day",
//          "degree", "fahrenheit", "fluid-ounce", "foot"]

常见单位包括:

  • 长度:"meter""kilometer""mile""foot"
  • 重量:"gram""kilogram""pound""ounce"
  • 温度:"celsius""fahrenheit"
  • 体积:"liter""gallon"
  • 数字:"byte""kilobyte""megabyte"

在格式化测量值时,需要指定所用的单位:

const distance = 1000;

const meterFormat = new Intl.NumberFormat("en-US", {
  style: "unit",
  unit: "meter"
});

const kilometerFormat = new Intl.NumberFormat("en-US", {
  style: "unit",
  unit: "kilometer"
});

console.log(meterFormat.format(distance));
// Output: "1,000 m"

console.log(kilometerFormat.format(1));
// Output: "1 km"

单位决定了数字后显示的缩写或符号。

你可以使用 "unit1-per-unit2" 格式组合单位:

const speed = 100;

const speedFormat = new Intl.NumberFormat("en-US", {
  style: "unit",
  unit: "kilometer-per-hour"
});

console.log(speedFormat.format(speed));
// Output: "100 km/h"

这样可以创建如每小时公里数或每加仑英里数等复合单位。

构建动态用户界面

Intl.supportedValuesOf() 的主要用例是构建能够适应当前 JavaScript 环境的用户界面。与硬编码选项不同,应在运行时查询支持的值。

此示例创建了一个设置表单,用户可以选择其首选的日历、货币和时区:

function buildSettingsForm() {
  const form = document.createElement("form");

  // Calendar selector
  const calendarSelect = buildSelector(
    "calendar",
    Intl.supportedValuesOf("calendar")
  );
  form.appendChild(createFormGroup("Calendar", calendarSelect));

  // Currency selector
  const currencies = ["USD", "EUR", "GBP", "JPY", "CNY"];
  const currencySelect = buildSelector("currency", currencies);
  form.appendChild(createFormGroup("Currency", currencySelect));

  // Time zone selector
  const timeZones = Intl.supportedValuesOf("timeZone");
  const timeZoneSelect = buildSelector("timeZone", timeZones);
  form.appendChild(createFormGroup("Time Zone", timeZoneSelect));

  return form;
}

function buildSelector(name, values) {
  const select = document.createElement("select");
  select.name = name;

  values.forEach(value => {
    const option = document.createElement("option");
    option.value = value;
    option.textContent = value;
    select.appendChild(option);
  });

  return select;
}

function createFormGroup(label, input) {
  const group = document.createElement("div");
  const labelElement = document.createElement("label");
  labelElement.textContent = label;
  group.appendChild(labelElement);
  group.appendChild(input);
  return group;
}

document.body.appendChild(buildSettingsForm());

这将仅使用浏览器支持的值,创建一个完整的设置界面。

使用受支持的值进行特性检测

你可以使用 Intl.supportedValuesOf() 来检测特定值是否受支持,然后再使用它们。这有助于实现渐进增强和回退策略。

检查某个特定日历是否受支持:

function isCalendarSupported(calendar) {
  const supported = Intl.supportedValuesOf("calendar");
  return supported.includes(calendar);
}

if (isCalendarSupported("islamic")) {
  console.log("Islamic calendar is supported");
} else {
  console.log("Islamic calendar is not supported");
}

这种模式适用于任何值类型:

function isValueSupported(type, value) {
  try {
    const supported = Intl.supportedValuesOf(type);
    return supported.includes(value);
  } catch (error) {
    return false;
  }
}

console.log(isValueSupported("currency", "USD"));
// Output: true

console.log(isValueSupported("timeZone", "America/New_York"));
// Output: true

try-catch 代码块用于处理 Intl.supportedValuesOf() 不可用的环境。

你可以用它来有条件地加载 polyfill:

async function ensureCalendarSupport(calendar) {
  if (!isValueSupported("calendar", calendar)) {
    console.log(`Loading polyfill for ${calendar} calendar`);
    await import("./calendar-polyfill.js");
  }
}

await ensureCalendarSupport("persian");

这样只有在需要时才会加载额外的代码。