Языки с нестандартной плюрализацией

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

CLDR вводит универсальную модель категорий:

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

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


Механизм выбора формы числа в Globalize

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

const Globalize = require("globalize");

// предполагается предварительная загрузка CLDR-данных
Globalize.locale("ru");

const pluralGenerator = Globalize.pluralGenerator();

Функция генератора возвращает определение категории для конкретного числа:

pluralGenerator(1);   // "one"
pluralGenerator(2);   // "few"
pluralGenerator(5);   // "many"

Эти категории затем используются при формировании строк через messageFormatter.


Архитектура языков с нестандартной плюрализацией

Под «нестандартной плюрализацией» понимаются языки, в которых:

  • количество форм превышает две;
  • выбор формы зависит не только от последней цифры;
  • присутствуют диапазонные и модульные правила;
  • значение 0, 1, 2 и составные числа трактуются по-разному.

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


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

Русский язык

Русский язык использует три категории:

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

Функция выбора в CLDR учитывает последние две цифры числа:

  • если заканчивается на 1, но не 11 → one
  • если заканчивается на 2–4, но не 12–14 → few
  • иначе → many

Пример использования Globalize:

const formatter = Globalize.messageFormatter({
  one: "{count} файл",
  few: "{count} файла",
  many: "{count} файлов"
});

formatter({ count: 1 });  // 1 файл
formatter({ count: 3 });  // 3 файла
formatter({ count: 10 }); // 10 файлов

Польский язык

Польская система ещё более детализирована:

  • one — 1
  • few — 2–4, кроме 12–14
  • many — 0, 5–21 (с оговорками по диапазонам)

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

const formatter = Globalize.messageFormatter({
  one: "{count} plik",
  few: "{count} pliki",
  many: "{count} plików"
});

Арабские языки: расширенная шестиформная система

Арабская локаль представляет один из самых сложных случаев в CLDR:

  • zero — 0
  • one — 1
  • two — 2
  • few — 3–10
  • many — 11–99
  • other — числа вне диапазонов

Globalize использует эти категории напрямую, без упрощения.

const formatter = Globalize.messageFormatter({
  zero: "لا ملفات",
  one: "ملف واحد",
  two: "ملفان",
  few: "{count} ملفات",
  many: "{count} ملفًا",
  other: "{count} ملف"
});

Особенность заключается в том, что даже числа 100 и выше могут попадать в категорию other, что требует строгого следования CLDR-правилам.


Языки с двойственным числом

Некоторые языки сохраняют категорию dual, формально отображаемую как two.

Примеры:

  • словенский
  • арабский (частично)
  • западноармянские диалекты в исторических формах

В CLDR эта категория унифицирована, но поведение зависит от локали.


Исландский язык: синтаксическая чувствительность

Исландская система демонстрирует зависимость от грамматической формы, а не только от числового значения.

Категории:

  • one
  • other

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

const formatter = Globalize.messageFormatter({
  one: "{count} skrá",
  other: "{count} skrár"
});

Особенности обработки нуля

Нулевая форма (zero) не универсальна. В ряде языков:

  • совпадает с many (русский, польский);
  • выделена отдельно (арабский);
  • не используется вовсе (английский).

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


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

Типичный поток работы включает:

  1. Определение локали
  2. Загрузка CLDR данных
  3. Создание генератора категорий
  4. Формирование сообщений
Globalize.locale("ru");

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

const result = formatter({ count: 23 });

Внутренне происходит:

  • вычисление категории через pluralGenerator
  • выбор соответствующего ключа в шаблоне
  • интерполяция значения

Комплексные диапазоны и числовые выражения

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

const formatter = Globalize.messageFormatter({
  one: "{count} запись",
  few: "{count} записи",
  many: "{count} записей",
  "one..few": "{count} записи (диапазон)"
});

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


Влияние десятичной части числа

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

pluralGenerator(1.2); // "other"
pluralGenerator(0.5); // "other"

Однако в некоторых языках (например, в французском) поведение может быть смещено в сторону one при округлении.


Сравнение моделей плюрализации

Тип языка Количество категорий Характеристика
Английский 2 простая бинарная модель
Русский 3 зависимость от последних цифр
Польский 3–4 сложные диапазоны
Арабский 5–6 многоступенчатая система
Исландский 2 синтаксическая зависимость

Ошибки при ручной реализации

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

  • игнорирование исключений (11–14 в славянских языках);
  • неправильная обработка нуля;
  • отсутствие поддержки дробных чисел;
  • жесткая зависимость от английской модели.

Globalize устраняет эти ошибки за счёт полной делегации правил внешнему репозиторию CLDR.


Структура данных CLDR для плюрализации

CLDR хранит правила в виде выражений:

i = integer part
v = visible fraction digits
n = number
t = trailing zeros

Пример правила:

n mod 10 in 2..4 and n mod 100 not in 12..14 -> few

Эти выражения интерпретируются библиотекой и не требуют ручной обработки.


Поведение при отсутствии локализационных данных

Если данные CLDR для локали неполные:

  • Globalize возвращает категорию other
  • шаблоны fallback применяются автоматически
  • возможна деградация до английской модели

Это обеспечивает предсказуемость даже при частичной загрузке данных.


Связь плюрализации с форматированием сообщений

Плюрализация редко используется изолированно. В Globalize она интегрируется с:

  • форматированием чисел;
  • интерполяцией параметров;
  • обработкой дат в сообщениях.
const formatter = Globalize.messageFormatter({
  one: "{count} файл загружен {date}",
  few: "{count} файла загружено {date}",
  many: "{count} файлов загружено {date}"
});

Система комбинирует несколько локализационных подсистем в едином шаблоне.