Комбинирование нескольких API

В реальных приложениях интернационализация почти никогда не ограничивается одной операцией форматирования. Дата, время, числа, валюты, списки, правила множественного числа и локализованная сортировка используются совместно, образуя связанный слой представления данных. Intl API в JavaScript проектировался как набор независимых, но согласованных инструментов, которые можно комбинировать без потери производительности и единообразия локали.

Единая локаль как основа композиции

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

const locale = 'ru-RU';

const dateFormatter = new Intl.DateTimeFormat(locale);
const numberFormatter = new Intl.NumberFormat(locale);
const collator = new Intl.Collator(locale);

Такой подход исключает расхождения в представлении данных: дата, число и сортировка используют одну и ту же культурную модель.

Особое значение имеет объект Intl.Locale, который позволяет централизованно описывать параметры локали:

const locale = new Intl.Locale('ru-RU', {
  hourCycle: 'h23',
  calendar: 'gregory',
});

Этот объект может использоваться как источник конфигурации для других API, снижая дублирование настроек.


Согласование форматирования чисел и валют

При комбинировании Intl.NumberFormat с другими форматерами важно учитывать контекст числа: простое значение, денежная величина, процент или индекс.

const locale = 'ru-RU';

const currencyFormatter = new Intl.NumberFormat(locale, {
  style: 'currency',
  currency: 'RUB',
});

const percentFormatter = new Intl.NumberFormat(locale, {
  style: 'percent',
});

Комбинация с датами часто используется в отчётах и финансовых интерфейсах:

const dateFormatter = new Intl.DateTimeFormat(locale);
const currencyFormatter = new Intl.NumberFormat(locale, {
  style: 'currency',
  currency: 'RUB',
});

function formatTransaction(date, amount) {
  return `${dateFormatter.format(date)} — ${currencyFormatter.format(amount)}`;
}

Здесь объединяются два независимых API, но результат воспринимается как единая семантическая строка.


Интеграция дат, времени и относительных выражений

Intl.RelativeTimeFormat дополняет Intl.DateTimeFormat, позволяя выражать временные интервалы в человеческой форме.

const locale = 'ru-RU';

const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });

Комбинирование с абсолютными датами часто используется в интерфейсах уведомлений:

const dateFormatter = new Intl.DateTimeFormat(locale, {
  day: 'numeric',
  month: 'long',
});

function formatNotification(date, diffInDays) {
  const absolute = dateFormatter.format(date);
  const relative = rtf.format(diffInDays, 'day');

  return `${absolute} (${relative})`;
}

В этом случае два разных представления времени дополняют друг друга: одно даёт точку на временной шкале, другое — интерпретацию относительно текущего момента.


Сортировка и отображение списков в одном контексте

Intl.Collator отвечает за культурно корректную сортировку, а Intl.ListFormat — за представление наборов данных в виде строки.

const locale = 'ru-RU';

const collator = new Intl.Collator(locale);
const listFormatter = new Intl.ListFormat(locale, {
  style: 'long',
  type: 'conjunction',
});

Типичный сценарий — сортировка и последующее отображение:

function formatUserList(users) {
  const sorted = users.sort((a, b) => collator.compare(a, b));
  return listFormatter.format(sorted);
}

Здесь используется последовательное применение API: сначала структурная операция (сортировка), затем семантическое представление (форматирование списка).


Множественное число и форматирование строк

Intl.PluralRules часто комбинируется с Intl.NumberFormat для построения грамматически корректных сообщений.

const locale = 'ru-RU';

const pluralRules = new Intl.PluralRules(locale);
const numberFormatter = new Intl.NumberFormat(locale);

Пример построения сообщений:

const forms = {
  one: 'файл',
  few: 'файла',
  many: 'файлов',
  other: 'файла',
};

function formatFiles(count) {
  const category = pluralRules.select(count);
  const word = forms[category] || forms.other;

  return `${numberFormatter.format(count)} ${word}`;
}

Здесь происходит связка трёх уровней:

  • числовое значение
  • грамматическая категория
  • локализованная строка

Комбинирование форматирования и сегментации текста

Intl.Segmenter используется для корректной работы с границами слов и предложений, особенно в многоязычных текстах.

const locale = 'ru-RU';

const segmenter = new Intl.Segmenter(locale, { granularity: 'word' });
const collator = new Intl.Collator(locale);

Пример комбинирования сегментации и сортировки:

function sortWords(text) {
  const words = Array.from(segmenter.segment(text))
    .filter(s => s.isWordLike)
    .map(s => s.segment);

  return words.sort((a, b) => collator.compare(a, b));
}

Здесь сегментация обеспечивает корректное разбиение текста, а collator — культурную сортировку.


Кэширование форматтеров при композиции API

При комбинировании нескольких Intl API критическим становится вопрос производительности. Конструкторы форматтеров достаточно тяжёлые, поэтому повторное создание объектов внутри функций приводит к лишним затратам.

Оптимальная стратегия — кэширование:

const cache = new Map();

function getFormatter(type, locale, options) {
  const key = JSON.stringify({ type, locale, options });

  if (!cache.has(key)) {
    let formatter;

    switch (type) {
      case 'date':
        formatter = new Intl.DateTimeFormat(locale, options);
        break;
      case 'number':
        formatter = new Intl.NumberFormat(locale, options);
        break;
      case 'list':
        formatter = new Intl.ListFormat(locale, options);
        break;
    }

    cache.set(key, formatter);
  }

  return cache.get(key);
}

Такой подход особенно важен при построении сложных UI, где форматирование выполняется многократно.


Согласование опций между API

При совместном использовании нескольких форматеров важно избегать рассинхронизации параметров локали.

Например, различие в hourCycle или calendar может привести к несогласованному отображению данных:

const locale = new Intl.Locale('ru-RU', {
  hourCycle: 'h23',
  calendar: 'gregory',
});

const dateFormatter = new Intl.DateTimeFormat(locale.toString(), {
  hour: '2-digit',
  minute: '2-digit',
});

Использование одного объекта Intl.Locale как источника конфигурации снижает риск расхождений между форматерами.


Композиция форматирования в прикладных сценариях

В реальных интерфейсах комбинирование Intl API проявляется в построении комплексных строк.

Пример формирования карточки события:

const locale = 'ru-RU';

const dateFormatter = new Intl.DateTimeFormat(locale, {
  weekday: 'long',
  day: 'numeric',
  month: 'long',
});

const numberFormatter = new Intl.NumberFormat(locale);

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

function formatEvent(event) {
  const date = dateFormatter.format(event.date);
  const attendees = listFormatter.format(event.participants);

  return `${date}, участников: ${numberFormatter.format(event.count)} (${attendees})`;
}

Здесь одновременно используются три независимых API, но результат формируется как единая семантическая конструкция.


Порядок применения форматеров

При композиции важно учитывать последовательность операций:

  1. Сначала структурирование данных (сортировка, фильтрация, сегментация)
  2. Затем локализованное преобразование чисел и дат
  3. В конце — построение строк и списков

Нарушение этого порядка приводит к потере семантики или некорректному отображению.


Использование форматеров как строительных блоков

Intl API можно рассматривать как набор независимых функций, которые становятся строительными блоками более сложных систем форматирования.

const formatters = {
  date: new Intl.DateTimeFormat('ru-RU'),
  number: new Intl.NumberFormat('ru-RU'),
  list: new Intl.ListFormat('ru-RU'),
  collator: new Intl.Collator('ru-RU'),
};

function formatDashboard(data) {
  const sorted = [...data.items].sort((a, b) =>
    formatters.collator.compare(a.name, b.name)
  );

  const names = formatters.list.format(sorted.map(i => i.name));

  return `${formatters.date.format(data.date)}: ${names}`;
}

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