Форматирование списков

Объект Intl.ListFormat предназначен для локализованного форматирования списков значений с учётом правил языка: союзов, пунктуации и грамматики. Его задача — заменить ручную конкатенацию строк (arr.join(", ")) корректными языковыми конструкциями вроде «A, B и C» или «A, B или C», учитывая особенности конкретной локали.

Базовый принцип работы заключается в том, что форматирование списка делегируется движку интернационализации, который применяет правила выбранного языка для соединения элементов.


Создание экземпляра ListFormat

Конструктор принимает два аргумента:

  • locales — строка или массив локалей
  • options — объект конфигурации форматирования
const formatter = new Intl.ListFormat('ru', {
  style: 'long',
  type: 'conjunction'
});

Если локаль не указана, используется системная по умолчанию.


Типы списков (option type)

Параметр type определяет семантику соединения элементов списка.

conjunction

Используется для перечислений с союзом «и»:

const f = new Intl.ListFormat('ru', { type: 'conjunction' });

f.format(['яблоки', 'бананы', 'груши']);
// "яблоки, бананы и груши"

Этот режим является наиболее распространённым, так как отражает логическое объединение элементов.


disjunction

Используется для альтернатив, где применяется «или»:

const f = new Intl.ListFormat('ru', { type: 'disjunction' });

f.format(['JavaScript', 'TypeScript', 'Dart']);
// "JavaScript, TypeScript или Dart"

Подходит для сценариев выбора или условий.


unit

Применяется для измерений, единиц и технических перечислений:

const f = new Intl.ListFormat('ru', { type: 'unit' });

f.format(['5 кг', '200 г', '30 мг']);
// "5 кг, 200 г, 30 мг"

Особенность режима заключается в том, что язык может использовать менее «разговорные» формы соединения.


Стили форматирования (option style)

Параметр style управляет степенью подробности и формальностью результата.

long

Полная форма с традиционными союзами:

const f = new Intl.ListFormat('ru', {
  style: 'long',
  type: 'conjunction'
});

f.format(['HTML', 'CSS', 'JavaScript']);
// "HTML, CSS и JavaScript"

short

Сокращённый формат, зависящий от локали:

const f = new Intl.ListFormat('en', {
  style: 'short',
  type: 'conjunction'
});

f.format(['apples', 'bananas', 'oranges']);
// "apples, bananas & oranges"

В некоторых языках разница между long и short минимальна, но в английском она может проявляться через символы вроде &.


narrow

Максимально компактный формат:

const f = new Intl.ListFormat('en', {
  style: 'narrow',
  type: 'conjunction'
});

f.format(['A', 'B', 'C']);
// "A, B, C"

Используется в интерфейсах с ограниченным пространством.


Поведение в разных локалях

Локализация влияет не только на союзы, но и на порядок элементов, использование запятых и пробелов.

const ru = new Intl.ListFormat('ru', { type: 'conjunction' });
const en = new Intl.ListFormat('en', { type: 'conjunction' });
const de = new Intl.ListFormat('de', { type: 'conjunction' });

const data = ['HTML', 'CSS', 'JS'];

ru.format(data);
en.format(data);
de.format(data);

Результаты будут отличаться не только словами «и»/«and»/«und», но и типографикой.


Метод format

Основной метод преобразует массив в строку:

const formatter = new Intl.ListFormat('ru', {
  style: 'long',
  type: 'conjunction'
});

formatter.format(['Москва', 'Казань', 'Сочи']);
// "Москва, Казань и Сочи"

Если передаётся один элемент, он возвращается без изменений:

formatter.format(['Москва']);
// "Москва"

Пустой массив возвращает пустую строку.


Метод formatToParts

Позволяет получить структуру результата в виде частей, а не одной строки.

const formatter = new Intl.ListFormat('ru', {
  style: 'long',
  type: 'conjunction'
});

formatter.formatToParts(['HTML', 'CSS', 'JS']);

Результат:

[
  { type: 'element', value: 'HTML' },
  { type: 'literal', value: ', ' },
  { type: 'element', value: 'CSS' },
  { type: 'literal', value: ' и ' },
  { type: 'element', value: 'JS' }
]

Типы частей

  • element — элемент списка
  • literal — разделитель (запятая, союз и пробелы)

Такой формат полезен для кастомного рендеринга интерфейсов, где требуется отдельно стилизовать элементы и разделители.


Практические сценарии использования

Формирование UI-списков

const formatter = new Intl.ListFormat('ru', { type: 'conjunction' });

const users = ['Анна', 'Иван', 'Мария'];

const text = formatter.format(users);
// "Анна, Иван и Мария"

Динамические фильтры

const formatter = new Intl.ListFormat('en', { type: 'disjunction' });

const filters = ['red', 'green', 'blue'];

const label = formatter.format(filters);
// "red, green or blue"

Отображение единиц измерения

const formatter = new Intl.ListFormat('ru', { type: 'unit' });

const values = ['10 см', '20 см', '30 см'];

formatter.format(values);
// "10 см, 20 см, 30 см"

Особенности поведения и ограничения

Форматирование зависит от реализации движка JavaScript. Разные браузеры могут иметь незначительные различия в деталях пунктуации, особенно в режиме short и narrow.

При работе с большими массивами форматирование выполняется синхронно, но обычно не является узким местом производительности по сравнению с DOM-операциями.

Пустые или некорректные значения приводятся к строкам внутренним алгоритмом приведения типов.


Комбинация с другими Intl API

Intl.ListFormat часто используется вместе с:

  • Intl.NumberFormat для числовых списков
  • Intl.DateTimeFormat для перечисления дат
  • Intl.DisplayNames для локализованных названий стран и языков
const list = new Intl.ListFormat('ru', { type: 'conjunction' });
const number = new Intl.NumberFormat('ru-RU');

const data = [number.format(1000), number.format(2000)];

list.format(data);
// "1 000 и 2 000"

Повторное использование экземпляров

Создание экземпляра форматтера дорогостоящее по сравнению с его использованием, поэтому обычно применяется переиспользование:

const formatter = new Intl.ListFormat('ru', { type: 'conjunction' });

function render(items) {
  return formatter.format(items);
}

Это особенно важно при рендеринге списков в циклах или UI-фреймворках.