В JavaScript для форматирования списков в зависимости от локали
используется объект Intl.ListFormat. Он решает задачу,
которая раньше требовала ручной склейки строк с учётом запятых, союзов и
особенностей языка. Главная ценность заключается в том, что
форматирование списка становится зависимым от правил конкретной локали,
а не от жёстко заданной логики приложения.
Список в контексте Intl.ListFormat — это набор значений,
которые должны быть объединены в человекочитаемую строку. Например:
Разные языки используют разные правила соединения элементов. Например, в английском:
A, B, and CВ русском:
A, B и CПри этом в зависимости от стиля список может быть оформлен по-разному: с союзами, с разделителями или в компактной форме.
Создание форматтера происходит через конструктор:
new Intl.ListFormat(locale, options)
Параметры:
locale — строка локали (например, "ru",
"en", "de"), либо массив локалейoptions — объект конфигурации форматированияКлючевой параметр, определяющий смысловую нагрузку списка:
Используется для обычных перечислений.
Пример:
const formatter = new Intl.ListFormat("ru", { type: "conjunction" });
formatter.format(["яблоки", "груши", "сливы"]);
// "яблоки, груши и сливы"
Смысл — объединение элементов как единого набора.
Используется для альтернатив.
Пример:
const formatter = new Intl.ListFormat("ru", { type: "disjunction" });
formatter.format(["Chrome", "Firefox", "Safari"]);
// "Chrome, Firefox или Safari"
Смысл — выбор одного из вариантов.
Используется для единиц измерения и технических значений.
Пример:
const formatter = new Intl.ListFormat("ru", { type: "unit" });
formatter.format(["5 кг", "200 г"]);
// "5 кг, 200 г"
В этом режиме союз обычно не используется, а форматирование становится максимально нейтральным.
Параметр style определяет степень полноты
форматирования.
Полный, развернутый стиль. Используются все грамматические элементы языка.
const formatter = new Intl.ListFormat("ru", {
style: "long",
type: "conjunction"
});
formatter.format(["HTML", "CSS", "JavaScript"]);
// "HTML, CSS и JavaScript"
Особенности:
Это стиль по умолчанию в большинстве локалей.
Укороченный вариант, где возможны более компактные формы записи.
const formatter = new Intl.ListFormat("en", {
style: "short",
type: "conjunction"
});
formatter.format(["apples", "bananas", "oranges"]);
// "apples, bananas & oranges"
Особенности:
&)Максимально компактный стиль.
const formatter = new Intl.ListFormat("en", {
style: "narrow",
type: "conjunction"
});
formatter.format(["A", "B", "C"]);
// "A B C"
Особенности:
Один и тот же код даёт разные результаты в разных языках.
const values = ["red", "green", "blue"];
new Intl.ListFormat("en", { type: "conjunction" }).format(values);
// "red, green, and blue"
new Intl.ListFormat("ru", { type: "conjunction" }).format(values);
// "red, green и blue"
new Intl.ListFormat("de", { type: "conjunction" }).format(values);
// "red, green und blue"
Логика соединений определяется языковыми нормами, а не разработчиком.
Отдельное внимание уделяется спискам из двух элементов, так как в разных языках для них часто используются особые правила.
const formatter = new Intl.ListFormat("ru", { type: "conjunction" });
formatter.format(["яблоко", "груша"]);
// "яблоко и груша"
Даже при двух элементах применяется союз, а не запятая.
const formatter = new Intl.ListFormat("ru", { type: "conjunction" });
formatter.format(["яблоко"]);
// "яблоко"
Форматирование не добавляет лишних символов.
formatter.format([]);
// ""
Результат — пустая строка, без ошибок и дополнительных символов.
Метод formatToParts возвращает структурированное
представление списка, а не строку.
const formatter = new Intl.ListFormat("ru", { type: "conjunction" });
formatter.formatToParts(["A", "B", "C"]);
Результат:
[
{ type: "element", value: "A" },
{ type: "literal", value: ", " },
{ type: "element", value: "B" },
{ type: "literal", value: " и " },
{ type: "element", value: "C" }
]
Типы:
element — элемент спискаliteral — разделители и союзыЭто позволяет:
Разные комбинации параметров дают различные эффекты.
new Intl.ListFormat("ru", {
style: "long",
type: "disjunction"
});
или
new Intl.ListFormat("en", {
style: "short",
type: "unit"
});
Результат зависит от:
Создание Intl.ListFormat — относительно дорогая
операция. В высоконагруженных приложениях форматтеры обычно создаются
один раз и переиспользуются.
const formatter = new Intl.ListFormat("ru", {
style: "long",
type: "conjunction"
});
function render(items) {
return formatter.format(items);
}
Это снижает накладные расходы при рендеринге списков.
Режим unit часто используется в технических
интерфейсах.
new Intl.ListFormat("ru", {
type: "unit",
style: "long"
}).format(["10 км", "5 м"]);
// "10 км, 5 м"
В некоторых локалях могут применяться специальные правила группировки или сокращения.
Ручная сборка списка обычно выглядит так:
function join(items) {
if (items.length === 0) return "";
if (items.length === 1) return items[0];
if (items.length === 2) return items.join(" и ");
return items.slice(0, -1).join(", ") + " и " + items.at(-1);
}
Проблемы такого подхода:
Intl.ListFormat устраняет необходимость подобной
логики.
При работе с API или пользовательским вводом список может содержать:
Форматтер не изменяет типы данных, а приводит их к строковому
представлению через стандартные механизмы ToString.
formatter.format([1, 2, 3]);
// "1, 2 и 3"
Несмотря на это, результат остаётся согласованным внутри каждой локали.
В интерфейсах с высокой плотностью информации списки часто комбинируются:
Применение Intl.ListFormat позволяет унифицировать
отображение всех подобных структур, исключая расхождения между
компонентами интерфейса.