如何在 JavaScript 中格式化类似 A、B、C 的列表?
使用 Intl.ListFormat 按照本地化规则和分隔符格式化数组。
简介
当你向用户展示一个项目列表时,需要用逗号和类似“和”这样的连接词将它们连接起来。不同语言在列表格式上有不同的习惯。英文使用逗号和 "and",西班牙语用 "y",法语用 "et",而中文则有完全不同的标点用法。
Intl.ListFormat API 可以将数组格式化为符合本地语言习惯的字符串,自动处理分隔符和连接词的差异,适配不同文化的列表格式。
手动格式化列表的问题
你可以用 join() 方法用逗号连接数组元素。
const fruits = ["apples", "oranges", "bananas"];
const list = fruits.join(", ");
console.log(list);
// "apples, oranges, bananas"
这种方式有两个问题。第一,没有在最后一个项目前加连接词。第二,使用了英文的标点符号,不适用于其他语言。
你可以手动在最后一个项目前加上“和”。
const fruits = ["apples", "oranges", "bananas"];
const lastFruit = fruits[fruits.length - 1];
const otherFruits = fruits.slice(0, -1);
const list = otherFruits.join(", ") + ", and " + lastFruit;
console.log(list);
// "apples, oranges, and bananas"
这段代码只适用于英文。西班牙语用户会看到 "apples, oranges, and bananas",而不是 "apples, oranges y bananas"。法语用户会看到 "and" 而不是 "et"。不同语言的标点和连接词规则各不相同。
使用 Intl.ListFormat 格式化列表
Intl.ListFormat 构造函数可以创建一个格式化器,将数组转换为符合本地语言习惯的列表字符串。
const formatter = new Intl.ListFormat("en");
const fruits = ["apples", "oranges", "bananas"];
console.log(formatter.format(fruits));
// "apples, oranges, and bananas"
格式化器会根据指定的 locale 使用正确的分隔符和连接词。你可以将 locale 作为构造函数的第一个参数传入。
const enFormatter = new Intl.ListFormat("en");
const esFormatter = new Intl.ListFormat("es");
const frFormatter = new Intl.ListFormat("fr");
const fruits = ["apples", "oranges", "bananas"];
console.log(enFormatter.format(fruits));
// "apples, oranges, and bananas"
console.log(esFormatter.format(fruits));
// "apples, oranges y bananas"
console.log(frFormatter.format(fruits));
// "apples, oranges et bananas"
格式化器会自动应用每种语言的标点和连接词规则。
使用“和”格式化列表
Intl.ListFormat 的默认行为是在项目之间使用“和”或其他语言中的等效词。这种方式称为连词格式化。
const formatter = new Intl.ListFormat("en", { type: "conjunction" });
const items = ["bread", "milk", "eggs"];
console.log(formatter.format(items));
// "bread, milk, and eggs"
type 选项用于控制项目之间的连接词。取值 "conjunction" 时,列表会使用“和”。这是默认值,因此可以省略。
理解列表类型选项
type 选项接受三个值,用于控制项目之间的连接方式。
"conjunction" 类型会使用“和”或其等效词。
const formatter = new Intl.ListFormat("en", { type: "conjunction" });
console.log(formatter.format(["red", "green", "blue"]));
// "red, green, and blue"
"disjunction" 类型会使用“或”或其等效词。
const formatter = new Intl.ListFormat("en", { type: "disjunction" });
console.log(formatter.format(["red", "green", "blue"]));
// "red, green, or blue"
"unit" 类型用于格式化测量值或数量的列表,不使用连词。
const formatter = new Intl.ListFormat("en", { type: "unit" });
console.log(formatter.format(["5 pounds", "12 ounces"]));
// "5 pounds, 12 ounces"
unit 类型会采用适合技术或计量数据的最简标点。
理解样式选项
style 选项用于控制格式化输出的长度和正式程度。该选项有三种取值。
"long" 样式会使用完整单词和标准标点。这是默认值。
const formatter = new Intl.ListFormat("en", { style: "long" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, and Carol"
"short" 样式会在可用时使用缩写形式。
const formatter = new Intl.ListFormat("en", { style: "short" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, & Carol"
"narrow" 样式会使用最紧凑的形式。
const formatter = new Intl.ListFormat("en", { style: "narrow" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, Carol"
narrow 样式通常会省略连词。具体输出取决于本地化规则。
不同语言环境的列表格式
每种语言环境都有自己的列表格式规则。格式化器会自动应用这些规则。
英语使用逗号,并在“和”前加牛津逗号。
const formatter = new Intl.ListFormat("en");
console.log(formatter.format(["coffee", "tea", "juice"]));
// "coffee, tea, and juice"
西班牙语使用逗号和连词 "y"。
const formatter = new Intl.ListFormat("es");
console.log(formatter.format(["café", "té", "jugo"]));
// "café, té y jugo"
法语使用逗号和连词 "et"。
const formatter = new Intl.ListFormat("fr");
console.log(formatter.format(["café", "thé", "jus"]));
// "café, thé et jus"
中文使用 "和" 作为 "and",并用顿号 、 作为分隔符。
const formatter = new Intl.ListFormat("zh");
console.log(formatter.format(["咖啡", "茶", "可乐"]));
// "咖啡、茶和可乐"
德语使用逗号和连词 "und"。
const formatter = new Intl.ListFormat("de");
console.log(formatter.format(["Kaffee", "Tee", "Saft"]));
// "Kaffee, Tee und Saft"
格式化器能够自动处理这些差异,无需你了解每种语言的规则。
使用 formatToParts 获取各个部分
formatToParts() 方法会返回一个对象数组,每个对象代表格式化列表的一个部分。当你需要分别为不同部分设置样式时,这非常有用。
const formatter = new Intl.ListFormat("en");
const parts = formatter.formatToParts(["red", "green", "blue"]);
console.log(parts);
输出是一个包含 type 和 value 属性的对象数组。
[
{ type: "element", value: "red" },
{ type: "literal", value: ", " },
{ type: "element", value: "green" },
{ type: "literal", value: ", and " },
{ type: "element", value: "blue" }
]
每个列表项的类型为 "element",分隔符和连词的类型为 "literal"。你可以利用这些类型自定义样式。
const formatter = new Intl.ListFormat("en");
const parts = formatter.formatToParts(["red", "green", "blue"]);
const html = parts
.map((part) => {
if (part.type === "element") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// "<strong>red</strong>, <strong>green</strong>, and <strong>blue</strong>"
这种方式让你能够精确控制格式,同时保持符合本地习惯的分隔符和连词。
浏览器支持
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");
}
对于旧版浏览器,你需要提供降级方案或使用 polyfill。降级方案可以使用简单的 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)
这样可以确保你的代码即使在不支持 Intl.ListFormat 的浏览器中也能正常运行。