Правила множественного числа

Механизм формирования множественного числа в разных языках не является универсальным: он зависит от грамматики конкретной локали. В одних языках существует только две формы (единственное и множественное число), в других — три и более, а в некоторых языках выбор формы определяется не только количеством, но и контекстом. Библиотека Globalize учитывает эти различия и предоставляет доступ к правилам pluralization через CLDR (Unicode Common Locale Data Repository).


Локализационная модель множественного числа

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

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

  • zero — нулевая форма (используется в ограниченном числе языков)
  • one — форма единственного числа
  • two — двойственное число (в некоторых языках)
  • few — несколько объектов
  • many — большое количество
  • other — универсальная форма по умолчанию

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


Получение правил множественного числа

В Globalize правила множественного числа доступны через загрузку соответствующих CLDR-данных и использование метода:

Globalize.pluralGenerator(locale)

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

Пример:

const Globalize = require("globalize");

Globalize.load(
  require("cldr-data/supplemental/likelySubtags"),
  require("cldr-data/supplemental/plurals"),
  require("cldr-data/main/en/numbers")
);

const en = new Globalize("en");
const plural = en.pluralGenerator();

plural(1);   // "one"
plural(2);   // "other"
plural(0);   // "other"

Функция не выполняет форматирование текста — она только определяет категорию.


Зависимость правил от локали

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

Английская локаль

В английском языке правило максимально простое:

  • one — только для значения 1
  • other — для всех остальных значений
plural(1); // "one"
plural(5); // "other"

Русская локаль

Русский язык использует три основные формы: one, few, many.

Правила зависят от последних цифр числа:

  • one — 1, 21, 31, 41…
  • few — 2-4, 22-24…
  • many — 0, 5-20, 25-30…

Пример:

const ru = new Globalize("ru");
const pluralRu = ru.pluralGenerator();

pluralRu(1);  // "one"
pluralRu(2);  // "few"
pluralRu(5);  // "many"
pluralRu(21); // "one"

Эти правила полностью определяются CLDR и не задаются вручную в коде.


Использование в форматировании сообщений

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

Globalize использует совместно с ICU MessageFormat:

const message = new Globalize("en").messageFormatter(
  "{count, plural, one {# file} other {# files}}"
);

message({ count: 1 }); // "1 file"
message({ count: 5 }); // "5 files"

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


Интерполяция значения числа

Символ # внутри шаблона заменяется на числовое значение, переданное в сообщение.

"{count, plural, one {# item} other {# items}}"

Результат:

  • count = 11 item
  • count = 33 items

Комбинация с другими типами сообщений

Plural-правила могут комбинироваться с другими конструкциями ICU:

С вложенными условиями

"{count, plural,
  one {# file}
  few {# files (few)}
  other {# files}
}"

С дополнительными переменными

"{count, plural,
  one {# file in # folder}
  other {# files in # folders}
}"

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


Особенности CLDR-правил

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

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

Пример логики для некоторых языков:

  • n % 10 == 1one
  • n % 10 in 2..4few
  • n % 100 in 11..14many

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


Обработка дробных чисел

Некоторые языки учитывают дробные значения отдельно от целых.

plural(1.0); // может быть "one"
plural(1.5); // обычно "other"

CLDR определяет, как именно трактовать дроби в каждой локали.


Кеширование pluralGenerator

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

Рекомендуется создавать его один раз на локаль:

const pluralEn = new Globalize("en").pluralGenerator();
const pluralRu = new Globalize("ru").pluralGenerator();

Повторное использование одной функции предпочтительнее повторного вызова конструктора.


Использование в масштабируемых приложениях

При работе с большими интерфейсами, где множество строк зависит от чисел, plural-логика становится центральным элементом интернационализации.

Типичная схема:

  • получение локали пользователя
  • создание Globalize-инстанса
  • получение pluralGenerator
  • использование в messageFormatter
const g = new Globalize(userLocale);
const plural = g.pluralGenerator();
const format = g.messageFormatter(MESSAGE);

Согласование с другими форматами чисел

Plural-правила не зависят от форматирования чисел, но часто используются совместно с:

  • numberFormatter
  • currencyFormatter
  • unitFormatter

Пример:

const number = new Globalize("en").numberFormatter();

const msg = new Globalize("en").messageFormatter(
  "{count, plural, one {# file} other {# files}}"
);

msg({ count: number(1200) });

Важно понимать, что plural работает с сырым числовым значением, а не с отформатированной строкой.


Ошибки при использовании

Наиболее распространённые ошибки:

Использование отформатированного числа

// неправильно
msg({ count: "1,200" });

Попытка вручную реализовать правила

// нежелательно
if (n === 1) { ... }

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

Игнорирование категорий CLDR

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


Поведение при отсутствии категории

Если локаль не содержит явного правила для категории, используется other.

"{count, plural, one {# file} other {# files}}"

В языках без категории few или many всё равно будет работать корректно через other.


Взаимодействие с динамическими локалями

При смене языка необходимо пересоздавать Globalize-инстанс, так как pluralGenerator привязан к конкретной локали:

const g = new Globalize("en");
const pluralEn = g.pluralGenerator();

const gRu = new Globalize("ru");
const pluralRu = gRu.pluralGenerator();

Переключение локали требует пересборки всех зависимых форматтеров.


Роль plural в архитектуре интернационализации

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

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

Globalize обеспечивает единый доступ к этим правилам через стандартизированные CLDR-данные, исключая необходимость ручной поддержки языковых особенностей.