Сложные правила плюрализации

Система плюрализации в Globalize основана на стандартах CLDR (Common Locale Data Repository), где для каждого языка определяются правила выбора формы множественного числа. В отличие от упрощённого подхода «один / много», реальная лингвистическая модель включает несколько категорий, зависящих от языка и числового значения.

Ключевые категории CLDR:

  • zero
  • one
  • two
  • few
  • many
  • other

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

Globalize не «угадывает» правила, а опирается на загруженные CLDR-данные, что делает поведение предсказуемым и локализованным.


Подключение данных CLDR и инициализация плюрализации

Globalize требует явной загрузки необходимых частей CLDR: языковых данных, числовых форматов и правил плюрализации.

import Globalize from "globalize";
import likelySubtags from "cldr-data/supplemental/likelySubtags.json";
import plurals from "cldr-data/supplemental/plurals.json";
import ruNumbers from "cldr-data/main/ru/numbers.json";

Globalize.load(likelySubtags);
Globalize.load(plurals);
Globalize.load(ruNumbers);

const globalize = new Globalize("ru");

Без подключения supplemental/plurals.json механизм выбора форм множественного числа работать не будет, поскольку именно там описаны языковые правила.


Основной механизм выбора формы множественного числа

Globalize использует метод plural(), который возвращает категорию для числа в заданной локали.

globalize.plural(1);   // "one"
globalize.plural(2);   // "few"
globalize.plural(5);   // "many"

Для русского языка логика соответствует правилам:

  • 1 → one (1 книга)
  • 2–4 → few (2, 3, 4 книги)
  • 5–20 → many (5 книг)
  • 0 → many (0 книг)

Важно, что в разных локалях эти правила кардинально отличаются.


Сложные правила в славянских языках

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

Русский язык

Число Категория
1 one
2–4 few
5–20 many
21 one
22–24 few
25+ many

Особенность заключается в модуле 10 и 100:

  • n % 10 === 1 и n % 100 !== 11 → one
  • n % 10 in 2..4 и n % 100 not in 12..14 → few
  • всё остальное → many

Globalize абстрагирует эти вычисления, предоставляя готовую категорию.


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

Plural-правила чаще всего применяются не напрямую, а через шаблоны сообщений.

Базовый пример

const message = {
  one: "{count} книга",
  few: "{count} книги",
  many: "{count} книг"
};

function format(count) {
  const category = globalize.plural(count);
  return message[category].replace("{count}", count);
}

format(1);  // 1 книга
format(3);  // 3 книги
format(10); // 10 книг

Интеграция с messageFormatter

Globalize предоставляет более системный способ через messageFormatter, где plural-ветвление встроено в синтаксис.

const formatter = globalize.messageFormatter({
  one: "{count} книга",
  few: "{count} книги",
  many: "{count} книг"
});

formatter({ count: 1 });
formatter({ count: 3 });
formatter({ count: 10 });

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


Вложенные и сложные структуры сообщений

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

const formatter = globalize.messageFormatter({
  one: "{name} добавил {count} файл",
  few: "{name} добавил {count} файла",
  many: "{name} добавил {count} файлов"
});

formatter({ name: "Иван", count: 2 });

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


Особенности категорий zero и other

Не все языки игнорируют ноль. Например:

  • в английском 0 filesother
  • в арабском языке ноль имеет отдельную форму
  • в некоторых языках zero используется активно

Пример (гипотетическая логика для языков с zero):

{
  zero: "нет файлов",
  one: "{count} файл",
  other: "{count} файлов"
}

Арабская система множественного числа как пример максимальной сложности

Арабский язык использует до шести категорий:

  • zero
  • one
  • two
  • few
  • many
  • other

Правила зависят от диапазонов:

  • 0 → zero
  • 1 → one
  • 2 → two
  • 3–10 → few
  • 11–99 → many
  • остальное → other

Globalize обрабатывает это автоматически при корректно загруженных CLDR-данных.


Отсутствие множественного числа в некоторых языках

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

В таких случаях CLDR возвращает всегда:

  • other

Пример:

globalize.plural(1); // "other"
globalize.plural(5); // "other"

Шаблоны сообщений обычно имеют одну форму:

{
  other: "{count} 件"
}

Числовые границы и нормализация значений

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

  • дробные числа округляются по правилам CLDR
  • отрицательные значения обрабатываются как положительные
  • экспоненциальные формы приводятся к стандартному виду
globalize.plural(-1);   // "one"
globalize.plural(1.0);  // "one"
globalize.plural(1.2);  // зависит от языка

Связь plural-форм с форматированием чисел

Plural-логика часто используется совместно с numberFormatter, поскольку итоговая строка должна содержать корректно локализованное число.

const numberFormatter = globalize.numberFormatter();

const formatter = globalize.messageFormatter({
  one: "{n} элемент",
  few: "{n} элемента",
  many: "{n} элементов"
});

const n = 21;

formatter({ n: numberFormatter(n) });

Это важно для языков, где формат числа влияет на восприятие формы.


Ошибки проектирования при ручной реализации plural-логики

При попытке реализовать plural-правила вручную часто возникают ошибки:

  • использование только n === 1
  • игнорирование диапазонов 11–14 в славянских языках
  • отсутствие поддержки языков с более чем двумя категориями
  • некорректная обработка нуля

Globalize устраняет эти проблемы за счёт стандартизации CLDR.


Производительность вычисления plural-категорий

Определение категории множественного числа в Globalize:

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

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


Композиция plural-правил в сложных интерфейсах

В реальных интерфейсах plural-логика редко используется изолированно. Она комбинируется с:

  • форматированием дат
  • форматированием валют
  • интерполяцией переменных
  • условными блоками сообщений

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

const formatter = globalize.messageFormatter({
  one: "На {date} будет {count} событие",
  few: "На {date} будет {count} события",
  many: "На {date} будет {count} событий"
});

Локализационная согласованность как ключевой фактор

Главная сложность plural-системы заключается не в вычислениях, а в поддержании согласованности:

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

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