Как форматировать списки, такие как A, B и C, в JavaScript?
Используйте Intl.ListFormat для форматирования массивов с учетом локалей, используя соответствующие союзы и разделители.
Введение
Когда вы отображаете список элементов пользователям, вам нужно соединить их запятыми и союзом, например, "и". В разных языках существуют разные правила форматирования списков. Английский использует запятые и "and", испанский — "y", французский — "et", а китайский использует совершенно другую пунктуацию.
API Intl.ListFormat форматирует массивы в строки, соответствующие локали, с правильными разделителями и союзами. Это автоматически учитывает культурные различия в форматировании списков.
Проблема ручного форматирования списков
Вы можете соединить элементы массива с помощью запятых, используя метод 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"
Форматтер использует правильные разделители и союзы для указанной локали. Вы передаете локаль в качестве первого аргумента в конструктор.
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 часто полностью опускает союз. Точный вывод зависит от локали.
Как разные локали форматируют списки
Каждая локаль имеет свои правила форматирования списков. Форматировщик применяет эти правила автоматически.
Английский использует запятые и включает оксфордскую запятую перед "and".
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"
Китайский использует символ 和 для "и" и разделительную запятую 、.
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 года в основных браузерах, включая 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 не поддерживается");
}
Для старых браузеров необходимо предоставить альтернативу или использовать полифил. В качестве альтернативы можно использовать простой метод 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" (или "red, green, blue" в старых браузерах)
Это гарантирует, что ваш код будет работать даже в браузерах без поддержки Intl.ListFormat.