如何在 JavaScript 中使用本地化分隔符格式化数组?

使用 Intl.ListFormat 自动为任意语言应用正确的逗号、空格和标点符号。

简介

当你将数组转换为可读字符串时,需要用逗号或其他标点符号分隔各项。不同语言使用的分隔符不同。英文用逗号和空格,日文用顿号(、),阿拉伯语则使用不同的标点和词序。

Intl.ListFormat API 可以将数组转换为带有本地化分隔符的字符串,确保你的列表在每种语言下都自然易读。

为什么数组分隔符因语言而异

你可能以为所有语言都用逗号分隔列表项,其实并非如此。

英文用逗号和空格分隔各项。

// English: "red, green, blue"

日文用顿号(、)且不加空格。

// Japanese: "赤、緑、青"

中文同样使用顿号(、)作为分隔符。

// Chinese: "红、绿、蓝"

阿拉伯语使用不同的逗号(،),并且从右向左书写。

// Arabic: "أحمر، أخضر، أزرق"

这些差异在 Intl.ListFormat 中会自动处理,无需手动了解每种语言的标点规则。

join() 方法的问题

Array.prototype.join() 方法会用你指定的分隔符将数组转换为字符串。

const colors = ["red", "green", "blue"];
console.log(colors.join(", "));
// "red, green, blue"

这种方式会将英文标点硬编码,逗号加空格的分隔符不适用于其他语言。

const colors = ["赤", "緑", "青"];
console.log(colors.join(", "));
// "赤, 緑, 青" (wrong - should use 、 instead of ,)

你无法根据语言手动切换分隔符,因为这需要维护一份所有语言的标点规则映射表,这既不完整也难以维护。

使用 Intl.ListFormat 实现本地化分隔符

Intl.ListFormat 构造函数会创建一个格式化器,根据不同的本地化设置自动应用正确的分隔符。

const formatter = new Intl.ListFormat("en");
const colors = ["red", "green", "blue"];
console.log(formatter.format(colors));
// "red, green, and blue"

该格式化器会自动使用指定语言环境的正确标点符号。你需要将语言环境代码作为第一个参数传递。

const enFormatter = new Intl.ListFormat("en");
const jaFormatter = new Intl.ListFormat("ja");
const arFormatter = new Intl.ListFormat("ar");

const colors = ["red", "green", "blue"];

console.log(enFormatter.format(colors));
// "red, green, and blue"

console.log(jaFormatter.format(["赤", "緑", "青"]));
// "赤、緑、青"

console.log(arFormatter.format(["أحمر", "أخضر", "أزرق"]));
// "أحمر، أخضر، أزرق"

浏览器会提供标点符号规则,无需维护任何特定于本地化的代码。

不同语言的分隔符差异

格式化器会根据语言环境应用不同的分隔符。以下示例展示了同一个数组在不同语言下的格式化效果。

英语使用逗号、空格和单词 "and"。

const formatter = new Intl.ListFormat("en");
console.log(formatter.format(["apple", "orange", "banana"]));
// "apple, orange, and banana"

西班牙语使用逗号、空格和单词 "y"。

const formatter = new Intl.ListFormat("es");
console.log(formatter.format(["manzana", "naranja", "plátano"]));
// "manzana, naranja y plátano"

法语使用逗号、空格和单词 "et"。

const formatter = new Intl.ListFormat("fr");
console.log(formatter.format(["pomme", "orange", "banane"]));
// "pomme, orange et banane"

德语使用逗号、空格和单词 "und"。

const formatter = new Intl.ListFormat("de");
console.log(formatter.format(["Apfel", "Orange", "Banane"]));
// "Apfel, Orange und Banane"

日语使用顿号 、 和字符 、。

const formatter = new Intl.ListFormat("ja");
console.log(formatter.format(["りんご", "オレンジ", "バナナ"]));
// "りんご、オレンジ、バナナ"

中文使用顿号 、 和词语 和。

const formatter = new Intl.ListFormat("zh");
console.log(formatter.format(["苹果", "橙子", "香蕉"]));
// "苹果、橙子和香蕉"

韩语使用逗号和助词 및。

const formatter = new Intl.ListFormat("ko");
console.log(formatter.format(["사과", "오렌지", "바나나"]));
// "사과, 오렌지 및 바나나"

格式化器会自动处理所有这些差异。你可以为每种语言编写相同的代码。

使用用户的语言环境

你可以从浏览器设置中检测用户的首选语言环境,并用它来格式化列表。

const userLocale = navigator.language;
const formatter = new Intl.ListFormat(userLocale);
const items = ["first", "second", "third"];
console.log(formatter.format(items));

这样可以确保列表使用符合用户期望的分隔符。设置为法语的用户会看到法语标点,设置为日语的用户会看到日语标点。

不带连接词的数组格式化

默认的 Intl.ListFormat 行为会在最后一项前添加类似“和”的连接词。你可以通过使用 unit 类型来禁用此功能。

const formatter = new Intl.ListFormat("en", { type: "unit" });
console.log(formatter.format(["5 km", "12 minutes", "100 calories"]));
// "5 km, 12 minutes, 100 calories"

unit 类型只使用分隔符,不添加连接词。这对于技术列表、计量单位或不适合使用连接词的数据非常有用。

const enFormatter = new Intl.ListFormat("en", { type: "unit" });
const jaFormatter = new Intl.ListFormat("ja", { type: "unit" });

console.log(enFormatter.format(["Item A", "Item B", "Item C"]));
// "Item A, Item B, Item C"

console.log(jaFormatter.format(["項目A", "項目B", "項目C"]));
// "項目A、項目B、項目C"

即使没有连接词,分隔符的标点仍然遵循本地化规则。

创建可复用的格式化器

你可以只创建一次格式化器,并在多个数组中复用。这样比为每个数组都新建一个格式化器更高效。

const formatter = new Intl.ListFormat("en");

console.log(formatter.format(["red", "green"]));
// "red and green"

console.log(formatter.format(["a", "b", "c", "d"]));
// "a, b, c, and d"

console.log(formatter.format(["one"]));
// "one"

同一个格式化器适用于任意长度的数组。它会根据数组项的数量自动应用正确的分隔符和连接词。

处理空数组

当你格式化一个空数组时,格式化器会返回一个空字符串。

const formatter = new Intl.ListFormat("en");
console.log(formatter.format([]));
// ""

如果你需要不同的处理方式,应在格式化前检查数组是否为空。

function formatList(items, locale) {
  if (items.length === 0) {
    return "No items";
  }
  const formatter = new Intl.ListFormat(locale);
  return formatter.format(items);
}

console.log(formatList([], "en"));
// "No items"

console.log(formatList(["apple"], "en"));
// "apple"

这样你就可以灵活控制空数组在用户界面上的显示效果。

浏览器支持

Intl.ListFormat API 在所有现代浏览器中都可用。从 2021 年 4 月起,Chrome、Firefox、Safari 和 Edge 均已支持。

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

if (typeof Intl.ListFormat !== "undefined") {
  const formatter = new Intl.ListFormat("en");
  console.log(formatter.format(["a", "b", "c"]));
} else {
  console.log("Intl.ListFormat is not supported");
}

对于旧版浏览器,可以回退到 join() 方法。这样可以提供基础格式化,但不支持本地化分隔符。

function formatList(items, locale) {
  if (typeof Intl.ListFormat !== "undefined") {
    const formatter = new Intl.ListFormat(locale);
    return formatter.format(items);
  }
  return items.join(", ");
}

console.log(formatList(["red", "green", "blue"], "en"));
// "red, green, and blue" (or "red, green, blue" in older browsers)

这样可以确保您的代码在所有浏览器中都能正常运行,并在现代浏览器中提供最佳体验。