Typed messages

В системе интернационализации Globalize сообщения строятся на основе ICU MessageFormat, что позволяет описывать динамические строки с учётом языка, грамматики и контекста. Ключевая идея заключается в том, что строка перестаёт быть простым шаблоном и превращается в структуру, где каждый параметр имеет тип и правила отображения.

Типизированные сообщения в Globalize позволяют:

  • корректно обрабатывать множественные формы (plural rules);
  • учитывать род и выбор вариантов (select);
  • форматировать числа, даты и единицы измерения внутри текста;
  • строить вложенные конструкции без потери локализационной логики.

Базовая структура сообщения

Сообщение определяется строкой с плейсхолдерами:

"cart.items": "В корзине {count} товаров"

При этом значение count не просто подставляется как строка, а проходит через форматирование, соответствующее локали:

const message = Globalize.messageFormatter("cart.items");

message({ count: 5 });
// "В корзине 5 товаров"

Даже в базовом виде параметр считается типизированным значением, так как Globalize определяет его поведение в зависимости от контекста использования.


Числовая типизация и plural rules

Одной из ключевых возможностей является работа с множественными формами. В разных языках правила различаются, и простое добавление s (как в английском) неприменимо.

ICU-формат позволяет задать поведение для разных количеств:

"cart.items": "{count, plural, one {# товар} few {# товара} many {# товаров} other {# товара}}"

Использование:

const message = Globalize.messageFormatter("cart.items");

message({ count: 1 }); // "1 товар"
message({ count: 3 }); // "3 товара"
message({ count: 10 }); // "10 товаров"

Типизация здесь проявляется в том, что count интерпретируется как числовое значение, которое сопоставляется с набором правил языка через CLDR.


select и категориальная типизация

Помимо чисел, сообщения могут опираться на категориальные значения: статус, пол, тип объекта, режим работы.

"notification.status":
"{status, select,
  success {Операция выполнена успешно}
  error {Произошла ошибка}
  pending {Ожидание обработки}
  other {Неизвестный статус}
}"

Использование:

const message = Globalize.messageFormatter("notification.status");

message({ status: "success" });
// "Операция выполнена успешно"

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


Вложенные типы и комбинированные конструкции

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

"tasks.summary":
"{gender, select,
  male {{count, plural, one {Он выполнил # задачу} few {Он выполнил # задачи} many {Он выполнил # задач}}}
  female {{count, plural, one {Она выполнила # задачу} few {Она выполнила # задачи} many {Она выполнила # задач}}}
  other {{count, plural, one {Выполнено # задание} few {Выполнено # задания} many {Выполнено # заданий}}}
}"

Использование:

const message = Globalize.messageFormatter("tasks.summary");

message({ gender: "female", count: 3 });
// "Она выполнила 3 задачи"

В таких структурах типизация становится иерархической: сначала определяется категориальный тип (gender), затем числовой (count), и только после этого выбирается конкретная строка.


Форматирование значений внутри сообщений

Параметры в сообщениях могут быть не просто вставлены, а отформатированы через встроенные механизмы Globalize.

Форматирование чисел

"price": "Цена: {value, number, currency}"
const message = Globalize.messageFormatter("price");

message({ value: 1234.5 });
// "Цена: 1 234,50 ₸" (в зависимости от локали)

Тип number активирует числовой форматтер с указанным стилем.


Форматирование дат

"event.date": "Дата события: {date, date, long}"
const message = Globalize.messageFormatter("event.date");

message({ date: new Date(2026, 4, 28) });
// "Дата события: 28 мая 2026 г."

Здесь параметр date интерпретируется как объект Date и проходит через локализованный форматтер дат.


Экранирование и интерпретация типов

ICU-синтаксис требует строгого соблюдения правил экранирования:

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

Пример:

"info": "У вас {count, plural, one {# сообщение} other {# сообщений}} в системе"

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


Компиляция сообщений и производительность

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

const formatter = Globalize.messageFormatter("cart.items");

В момент создания форматтера:

  • анализируется структура ICU;
  • определяется набор параметров;
  • создаётся функция с ветвлением по типам.

При последующих вызовах происходит только подстановка значений без повторного парсинга строки.


Неявные приведения типов

Несмотря на строгую структуру, Globalize допускает неявные преобразования:

  • строки, содержащие числа, могут интерпретироваться как numeric;
  • Date объекты приводятся к timestamp;
  • отсутствующие параметры заменяются значением other.

Пример:

message({ count: "3" });

Значение "3" будет приведено к числу для определения plural-категории.


Детерминированность выбора формы

Выбор ветки в типизированных сообщениях полностью детерминирован и зависит от CLDR-правил конкретной локали. Это исключает ручное управление грамматикой в коде.

Для одного и того же значения:

  • разные языки могут возвращать разные формы;
  • структура сообщения остаётся неизменной;
  • логика выбора инкапсулируется внутри движка Globalize.

Вложенные форматтеры и композиция типов

Типизированные сообщения допускают вложенные форматтеры:

"report":
"{value, number, percent} завершено к {date, date, short}"
const message = Globalize.messageFormatter("report");

message({
  value: 0.87,
  date: new Date()
});

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


Ограничения модели типизированных сообщений

При всей гибкости ICU-подхода существуют ограничения:

  • отсутствует произвольная логика внутри сообщений;
  • нельзя выполнять вычисления;
  • структура должна быть статически определимой;
  • сложные сценарии требуют подготовки данных до передачи в formatter.

Эти ограничения обеспечивают предсказуемость и кросс-языковую согласованность.


Поведение при неизвестных значениях

Если передано значение, не соответствующее ни одной ветке select или plural, используется fallback other.

"{level, select, high {Высокий} low {Низкий} other {Средний}}"

Это гарантирует стабильность отображения даже при расширении доменной модели приложения.


Согласование типов в сложных сценариях

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

  • числовые параметры → plural rules;
  • строковые → select;
  • даты → date/time formatters;
  • универсальные → литеральная подстановка.

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