Форматирование составных значений

Понятие составного значения в международном форматировании

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

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

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

Globalize опирается на CLDR (Common Locale Data Repository), где описаны правила соединения элементов и языковые особенности для разных локалей, включая пунктуацию, пробелы, типографику и морфологические формы.


Базовая модель составного форматирования

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

Простейший пример:

Globalize.locale("en");

Globalize.formatMessage(
  "{count} items in {category}",
  { count: 5, category: "books" }
);

В разных локалях этот же шаблон может менять:

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

Число + единица измерения

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

const globalize = Globalize("en");

globalize.formatUnit(5, "meter");

Результат зависит от локали:

  • en: 5 meters
  • ru: 5 метров
  • ja: 5 メートル

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

Внутренне система использует правила CLDR Unit Patterns, которые определяют шаблоны вида:

{0} meter
{0} meters

Составные числовые выражения

Диапазоны

Диапазоны представляют собой частный случай составного форматирования, где два значения соединяются в единое выражение.

globalize.formatNumberRange(10, 20);

В зависимости от локали формируется:

  • en: 10–20
  • ru: 10–20
  • de: 10–20

Однако различия могут проявляться в:

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

Проценты в контексте

Процент как составное значение часто зависит от контекста:

globalize.formatNumber(0.25, { style: "percent" });

Результат:

  • en: 25%
  • ru: 25 %

В составных выражениях процент может комбинироваться с другими элементами:

globalize.formatMessage(
  "{value} of {total} ({percent}%)",
  {
    value: 25,
    total: 100,
    percent: 25
  }
);

Здесь формируется единая структура, где каждый элемент локализуется отдельно, но соединяется по правилам шаблона.


Валюта как часть составного выражения

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

globalize.formatCurrency(1000, "USD");

Однако в составе сообщения:

globalize.formatMessage(
  "Price: {price} per item",
  { price: globalize.formatCurrency(1000, "USD") }
);

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

  • позицию символа валюты;
  • разделители тысяч;
  • пробелы и неразрывные пробелы.

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


Списки как составные значения

Списки представляют собой особый класс составных значений, где важна грамматика соединения элементов.

globalize.formatList(["apples", "bananas", "oranges"]);

Результаты:

  • en: apples, bananas, and oranges
  • ru: яблоки, бананы и апельсины

CLDR определяет несколько типов списков:

  • стандартный (standard);
  • узкий (short);
  • короткий (narrow).

Каждый тип влияет на:

  • наличие союзов;
  • использование запятых;
  • сокращённые формы.

Составные сообщения с множественными параметрами

Наиболее гибкий механизм — шаблонные сообщения, где несколько параметров взаимодействуют между собой.

const msg = "{user} has {count} new message(s) in {folder}";

globalize.formatMessage(msg, {
  user: "Alex",
  count: 3,
  folder: "Inbox"
});

Однако реальная сложность появляется при добавлении правил множественного числа.

Плюрализация внутри составных сообщений

const msg =
  "{count, plural, one {# message} other {# messages}} for {user}";

globalize.formatMessage(msg, {
  count: 3,
  user: "Alex"
});

Здесь составное значение включает:

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

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


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

Составные значения могут быть вложенными, образуя иерархические структуры.

globalize.formatMessage(
  "{user} spent {amount} on {items}",
  {
    user: "Maria",
    amount: globalize.formatCurrency(250, "EUR"),
    items: globalize.formatList(["books", "notebooks"])
  }
);

В этом случае:

  • amount уже является составным значением (валюта);
  • items является составным списком;
  • итоговое сообщение объединяет оба результата.

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


Грамматическая согласованность в составных значениях

В языках с развитой морфологией составные значения требуют согласования:

  • падежа;
  • числа;
  • рода;
  • контекстной формы.

Пример:

globalize.formatMessage(
  "{count} {unit} available",
  {
    count: 5,
    unit: "файл"
  }
);

В корректной локализации слово «файл» изменяется:

  • 1 файл;
  • 2 файла;
  • 5 файлов.

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


Составные значения с датами и временем

Даты часто участвуют в сложных выражениях:

globalize.formatDate(new Date());

В составе сообщения:

globalize.formatMessage(
  "Event starts at {time} on {date}",
  {
    time: globalize.formatTime(new Date()),
    date: globalize.formatDate(new Date())
  }
);

Локализация влияет на:

  • порядок даты и времени;
  • формат разделителей;
  • использование предлогов;
  • 12/24-часовой формат.

Смешанные типы составных значений

Реальные приложения часто комбинируют несколько типов форматирования:

globalize.formatMessage(
  "{user} purchased {count} items for {total} on {date}",
  {
    user: "Ivan",
    count: 4,
    total: globalize.formatCurrency(1200, "USD"),
    date: globalize.formatDate(new Date())
  }
);

Такое сообщение включает:

  • число с множественным числом;
  • валюту;
  • дату;
  • текстовые параметры.

Каждый элемент независимо локализуется, но итоговое сообщение зависит от синтаксиса конкретного языка.


Правила композиции составных значений

При работе с составными значениями ключевыми становятся следующие принципы:

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

Ошибки при работе с составными значениями

На практике часто возникают типичные проблемы:

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

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


Структурная модель итогового сообщения

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

  • корневой шаблон сообщения;

  • параметры:

    • числа;
    • валюты;
    • списки;
    • даты;
    • вложенные сообщения;
  • правила локали:

    • порядок слов;
    • грамматика;
    • пунктуация.

Globalize обеспечивает согласованность этой структуры за счёт стандартизированных данных CLDR и единых механизмов форматирования.