В интернационализации приложений часто возникает задача корректного отображения списков. Простое соединение строк через запятую или фиксированное слово «и» приводит к грамматическим ошибкам в разных языках. В одних языках союз ставится перед последним элементом, в других меняется порядок слов, а в некоторых используются специальные разделители в зависимости от контекста.
В экосистеме FormatJS для решения этой задачи используется механизм
списков, основанный на стандарте Intl.ListFormat. Он
обеспечивает локализованное форматирование последовательностей значений
с учётом языка, контекста и типа соединения элементов.
Intl.ListFormat — это часть стандарта ECMAScript
Internationalization API, на котором строится работа списков в FormatJS.
В библиотеке FormatJS этот механизм используется как низкоуровневая
основа для форматирования через react-intl и
@formatjs/intl.
Создание форматтера:
const formatter = new Intl.ListFormat('ru', {
style: 'long',
type: 'conjunction'
});
Пример использования:
formatter.format(['яблоки', 'бананы', 'груши']);
// "яблоки, бананы и груши"
В FormatJS и Intl.ListFormat ключевую роль играет
параметр type. Он определяет логический смысл списка и
влияет на выбор союзов и разделителей.
Тип conjunction используется для перечислений, где
элементы объединяются логическим «и».
Основная семантика:
Пример:
const f = new Intl.ListFormat('ru', { type: 'conjunction' });
f.format(['HTML', 'CSS', 'JavaScript']);
// "HTML, CSS и JavaScript"
Особенности:
Пример для английского:
new Intl.ListFormat('en', { type: 'conjunction' })
.format(['apples', 'bananas', 'oranges']);
// "apples, bananas, and oranges"
Тип disjunction используется для альтернатив, где
элементы взаимоисключающие или представляют выбор.
Семантика:
Пример:
const f = new Intl.ListFormat('ru', { type: 'disjunction' });
f.format(['Chrome', 'Firefox', 'Safari']);
// "Chrome, Firefox или Safari"
Особенности:
Пример английской локали:
new Intl.ListFormat('en', { type: 'disjunction' })
.format(['red', 'green', 'blue']);
// "red, green, or blue"
Тип unit предназначен для форматирования измерений,
количественных выражений и составных единиц.
Семантика:
Примеры использования:
Пример:
const f = new Intl.ListFormat('en', { type: 'unit' });
f.format(['5', 'kilometers']);
// "5 kilometers"
В русском:
new Intl.ListFormat('ru', { type: 'unit' })
.format(['5', 'метров']);
// "5 метров"
Особенности:
Помимо type, формат списков зависит от параметра
style.
new Intl.ListFormat('en', {
type: 'conjunction',
style: 'short'
});
Основные варианты:
Пример различий:
const items = ['A', 'B', 'C'];
new Intl.ListFormat('en', { type: 'conjunction', style: 'long' }).format(items);
// "A, B, and C"
new Intl.ListFormat('en', { type: 'conjunction', style: 'short' }).format(items);
// "A, B, & C"
В FormatJS списки используются через API formatList или
FormattedList.
Пример с useIntl:
import { useIntl } from 'react-intl';
function Component() {
const intl = useIntl();
const value = intl.formatList(['JS', 'TS', 'Node.js'], {
type: 'conjunction'
});
return <div>{value}</div>;
}
import { FormattedList } from 'react-intl';
<FormattedList
value={['React', 'Vue', 'Svelte']}
style="long"
type="conjunction"
/>
Этот подход полезен при рендеринге без явного вызова API форматтера.
Поведение трёх типов определяется не только переводом союзов, но и семантической моделью:
Это критично при переводе интерфейсов, где ошибка типа списка может изменить смысл сообщения:
Каждая локаль имеет собственные правила:
Например:
new Intl.ListFormat('de', { type: 'conjunction' })
.format(['Eins', 'Zwei', 'Drei']);
// "Eins, Zwei und Drei"
new Intl.ListFormat('fr', { type: 'disjunction' })
.format(['rouge', 'bleu', 'vert']);
// "rouge, bleu ou vert"
Типы списков используются в интерфейсах для:
FormatJS оборачивает стандартный Intl.ListFormat,
добавляя:
Некорректное применение type приводит к искажению
смысла:
conjunction для вариантов выбораdisjunction для перечисления обязательных
элементовКорректный подход всегда опирается на семантику данных, а не на визуальный результат.