Комбинированное отображение

Природа комбинированного отображения

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

Библиотека Globalize решает эту задачу через механизм сообщений (Message Formatting), построенный поверх ICU MessageFormat и данных CLDR. Основной принцип заключается в том, что форматирование не ограничивается отдельными типами данных — числа, даты и единицы могут быть встроены в одну синтаксическую конструкцию.

Ключевая особенность заключается в разделении ответственности:

  • форматирование значений выполняется специализированными форматтерами Globalize
  • объединение значений выполняется через шаблоны сообщений
  • грамматическая адаптация обеспечивается правилами CLDR (включая плюрализацию)

Архитектура объединённого форматирования

Комбинированное отображение в Globalize опирается на несколько уровней:

1. Форматтеры значений

  • formatNumber
  • formatDate
  • formatCurrency
  • formatUnit
  • formatRelativeTime

2. Слой сообщений

  • messageFormatter
  • ICU-подобный синтаксис шаблонов

3. CLDR-данные

  • правила множественных форм
  • локализованные шаблоны
  • региональные особенности записи

Именно слой сообщений связывает независимые форматтеры в единую строку.


Базовый принцип объединения значений

Комбинирование строится вокруг шаблонов, где значения подставляются в заранее определённые позиции:

const formatter = Globalize.messageFormatter(
  "{username} сделал покупку на сумму {amount} {currency}"
);

formatter({
  username: "Иван",
  amount: Globalize( "ru" ).formatNumber(1250),
  currency: "RUB"
});

Несмотря на внешнюю простоту, ключевой момент заключается в том, что каждое значение уже локализовано до подстановки.


Встраивание чисел и валют в сообщения

Наиболее распространённый сценарий комбинированного отображения — финансовые сообщения.

const cldr = Globalize( "ru" );

const message = cldr.messageFormatter(
  "Баланс счета: {balance} {currency}"
);

message({
  balance: cldr.formatNumber(15230.5),
  currency: "RUB"
});

Однако корректная реализация предполагает, что валютное форматирование выполняется отдельно:

const cldr = Globalize( "ru" );

const message = cldr.messageFormatter(
  "Баланс счета: {balance}"
);

message({
  balance: cldr.formatCurrency(15230.5, "RUB")
});

Разделение валюты и числа позволяет учитывать локальные правила:

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

Комбинирование дат и чисел

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

const cldr = Globalize( "ru" );

const message = cldr.messageFormatter(
  "Заказ оформлен {date}, общая сумма {amount}"
);

message({
  date: cldr.formatDate(new Date(2026, 4, 27)),
  amount: cldr.formatNumber(3200)
});

Здесь важно, что дата и число обрабатываются разными форматтерами, но объединяются единым шаблоном.


Плюрализация в комбинированных строках

Одним из самых сложных аспектов является согласование множественных форм. ICU MessageFormat, используемый Globalize, позволяет встроить правила плюрализации прямо в шаблон.

const cldr = Globalize( "ru" );

const message = cldr.messageFormatter(
  "{count, plural, one {# файл загружен} few {# файла загружено} many {# файлов загружено}}"
);

message({ count: 5 });

При комбинировании с другими значениями:

const message = cldr.messageFormatter(
  "{name} загрузил {count, plural, one {# файл} few {# файла} many {# файлов}}"
);

message({
  name: "Алексей",
  count: 3
});

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


Вложенные выражения и компоновка значений

MessageFormat поддерживает вложенные конструкции, что позволяет строить сложные сообщения.

const cldr = Globalize( "ru" );

const message = cldr.messageFormatter(
  "{user} совершил оплату: {amount} ({status})"
);

message({
  user: "Мария",
  amount: cldr.formatCurrency(499.99, "RUB"),
  status: "успешно"
});

Более сложные случаи включают вложенные плюрализации внутри общей структуры сообщения:

const message = cldr.messageFormatter(
  "{user} добавил {count, plural, one {# товар} few {# товара} many {# товаров}} на сумму {amount}"
);

Сочетание единиц измерения и чисел

Отдельного внимания требует комбинирование физических единиц с числовыми значениями.

const cldr = Globalize( "ru" );

const message = cldr.messageFormatter(
  "Скорость соединения: {speed}"
);

message({
  speed: cldr.formatUnit(50, "megabyte-per-second")
});

Такой подход обеспечивает:

  • локализацию обозначений единиц
  • корректное отображение пробелов
  • адаптацию к региональным стандартам

Комбинированное отображение временных интервалов

Relative time часто используется вместе с другими параметрами:

const cldr = Globalize( "ru" );

const message = cldr.messageFormatter(
  "Последняя активность: {time} ({diff})"
);

message({
  time: cldr.formatDate(new Date(2026, 4, 27, 10, 30)),
  diff: cldr.formatRelativeTime(-2, "hour")
});

Здесь соединяются абсолютное и относительное представление времени, что повышает информативность интерфейса.


Сложные шаблоны сообщений

При развитии интерфейса сообщения становятся многоуровневыми:

const cldr = Globalize( "ru" );

const message = cldr.messageFormatter(
  "{user} оформил заказ {orderId} на {amount}. {items, plural, one {# позиция} few {# позиции} many {# позиций}} добавлено в корзину."
);

message({
  user: "Олег",
  orderId: "#A-1034",
  amount: cldr.formatCurrency(7890, "RUB"),
  items: 4
});

Такие конструкции требуют строгого разделения:

  • форматирование значений
  • грамматическая логика
  • структура сообщения

Локализационная согласованность

Комбинированное отображение опирается на единообразие локали. Все форматтеры Globalize используют одну и ту же локализацию:

const cldr = Globalize( "ru" );

cldr.formatNumber(1000);
cldr.formatDate(new Date());
cldr.formatCurrency(1000, "RUB");

При объединении этих значений в одном сообщении исключаются несоответствия:

  • разные разделители тысяч
  • различия в порядке компонентов даты
  • несогласованность обозначений валют

Производительность при комбинированном форматировании

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

const cldr = Globalize( "ru" );

const formatOrderMessage = cldr.messageFormatter(
  "{user} заказал {amount}"
);

Форматтер создаётся один раз и повторно используется, что снижает нагрузку при массовом рендеринге интерфейсов.


Типичные ошибки при объединении форматов

1. Предварительное объединение строк

Ошибочный подход — конкатенация до форматирования:

"Сумма: " + Globalize.formatNumber(1000)

Правильный подход — использование сообщений.


2. Игнорирование локали

Форматирование без единого контекста локали приводит к разнородному выводу.


3. Смешивание форматирования и логики

ICU-шаблоны должны содержать только правила представления, но не бизнес-логику.


Расширенные сценарии композиции

В сложных интерфейсах комбинированное отображение может включать:

  • валюту + процент изменения
  • дату + статус + числовой идентификатор
  • единицы измерения + диапазоны значений
const message = cldr.messageFormatter(
  "{product} вырос в цене на {percent}% ({diff})"
);
message({
  product: "Товар",
  percent: cldr.formatNumber(12.5),
  diff: cldr.formatCurrency(150, "RUB")
});

Роль CLDR в согласовании вывода

CLDR обеспечивает основу для корректного комбинирования значений:

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

Без этих данных комбинированное отображение теряет языковую корректность и становится набором механических подстановок.