Типы списков: conjunction, disjunction, unit

В интернационализации приложений часто возникает задача корректного отображения списков. Простое соединение строк через запятую или фиксированное слово «и» приводит к грамматическим ошибкам в разных языках. В одних языках союз ставится перед последним элементом, в других меняется порядок слов, а в некоторых используются специальные разделители в зависимости от контекста.

В экосистеме FormatJS для решения этой задачи используется механизм списков, основанный на стандарте Intl.ListFormat. Он обеспечивает локализованное форматирование последовательностей значений с учётом языка, контекста и типа соединения элементов.


Базовая концепция 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(['яблоки', 'бананы', 'груши']);
// "яблоки, бананы и груши"

Типы списков: conjunction, disjunction, unit

В FormatJS и Intl.ListFormat ключевую роль играет параметр type. Он определяет логический смысл списка и влияет на выбор союзов и разделителей.


conjunction (соединительный список)

Тип conjunction используется для перечислений, где элементы объединяются логическим «и».

Основная семантика:

  • все элементы входят в набор
  • порядок не изменяет смысл
  • используется для описания полного списка

Пример:

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

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

Особенности:

  • последний элемент соединяется союзом
  • промежуточные элементы разделяются запятыми или локальными аналогами
  • союз зависит от языка (в английском — “and”, в немецком — “und”)

Пример для английского:

new Intl.ListFormat('en', { type: 'conjunction' })
  .format(['apples', 'bananas', 'oranges']);
// "apples, bananas, and oranges"

disjunction (разделительный список)

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

Семантика:

  • «или»
  • выбор одного или нескольких вариантов
  • логическое объединение через OR

Пример:

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 (единичный список)

Тип unit предназначен для форматирования измерений, количественных выражений и составных единиц.

Семантика:

  • не логическое соединение
  • не выбор
  • визуальное объединение единиц или значений

Примеры использования:

  • расстояния
  • скорости
  • составные единицы
  • величины времени

Пример:

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

f.format(['5', 'kilometers']);
// "5 kilometers"

В русском:

new Intl.ListFormat('ru', { type: 'unit' })
  .format(['5', 'метров']);
// "5 метров"

Особенности:

  • отсутствуют союзы «и» / «или»
  • формат может зависеть от грамматических правил языка
  • часто используется для числовых и измерительных данных

Поведение style: long, short, narrow

Помимо type, формат списков зависит от параметра style.

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

Основные варианты:

  • long — полная форма («and», «or»)
  • short — сокращённая форма (может быть символ «&» в некоторых локалях)
  • narrow — максимально компактная форма

Пример различий:

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 (react-intl)

В 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>;
}

Декларативный вариант через FormattedList

import { FormattedList } from 'react-intl';

<FormattedList
  value={['React', 'Vue', 'Svelte']}
  style="long"
  type="conjunction"
/>

Этот подход полезен при рендеринге без явного вызова API форматтера.


Отличия типов списков на уровне логики

Поведение трёх типов определяется не только переводом союзов, но и семантической моделью:

  • conjunction — объединение элементов в единый набор
  • disjunction — выбор между альтернативами
  • unit — синтаксическое объединение без логического союза

Это критично при переводе интерфейсов, где ошибка типа списка может изменить смысл сообщения:

  • «Chrome и Firefox» (оба браузера) — conjunction
  • «Chrome или Firefox» (выбор) — disjunction
  • «5 километров» — unit

Влияние локали на форматирование

Каждая локаль имеет собственные правила:

  • порядок слов
  • наличие или отсутствие запятых
  • форма союзов
  • пробелы и пунктуация

Например:

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

FormatJS оборачивает стандартный Intl.ListFormat, добавляя:

  • единый API для React и non-React окружений
  • fallback для старых браузеров
  • интеграцию с ICU MessageFormat
  • консистентность с другими форматтерами (date, number, relative time)

Типичные ошибки при использовании списков

Некорректное применение type приводит к искажению смысла:

  • использование conjunction для вариантов выбора
  • использование disjunction для перечисления обязательных элементов
  • попытка вручную добавлять союзы вместо форматтера
  • игнорирование локали при интернационализации

Корректный подход всегда опирается на семантику данных, а не на визуальный результат.