Кардинальные числительные

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

В JavaScript работа с кардинальными числительными реализована через Intl.PluralRules, где определяется набор правил выбора грамматической формы в зависимости от числового значения и локали.


Кардинальная и порядковая категории

Механизм Intl.PluralRules оперирует двумя основными типами:

  • cardinal — количественные числительные (сколько предметов)
  • ordinal — порядковые числительные (какой по счёту)

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

const rules = new Intl.PluralRules('en', { type: 'cardinal' });

rules.select(0); // "other"
rules.select(1); // "one"
rules.select(2); // "other"

Тип cardinal задаёт модель, в которой число сопоставляется с грамматической категорией, а не с конкретной словоформой.


Грамматические категории plural rules

Каждая локаль определяет собственный набор категорий множественного числа. В спецификации Unicode CLDR выделяются следующие основные формы:

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

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

  • one
  • other
const en = new Intl.PluralRules('en');

en.select(1);  // "one"
en.select(5);  // "other"
en.select(0);  // "other"

Поведение кардинальных правил в разных языках

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

Английский язык использует минимальную систему различий:

const en = new Intl.PluralRules('en');

const samples = [0, 1, 2, 10];

samples.map(n => [n, en.select(n)]);
// [
//   [0, "other"],
//   [1, "one"],
//   [2, "other"],
//   [10, "other"]
// ]

Единственное значение, выделяемое грамматически — единица.


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

Русский язык использует трёхчастную систему:

  • one — 1, 21, 31…
  • few — 2–4, 22–24…
  • many — 0, 5–20, 25–30…
  • other — используется редко, в основном для дробных значений
const ru = new Intl.PluralRules('ru');

[
  1, 2, 5, 11, 21, 22, 25
].map(n => [n, ru.select(n)]);

Результат:

  • 1 → one
  • 2 → few
  • 5 → many
  • 11 → many
  • 21 → one
  • 22 → few
  • 25 → many

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


Арабская локаль

Арабская система является одной из наиболее сложных:

  • zero
  • one
  • two
  • few
  • many
  • other
const ar = new Intl.PluralRules('ar');

[0, 1, 2, 3, 11, 100].map(n => [n, ar.select(n)]);

Механика определения категории

Intl.PluralRules не выполняет простое математическое сравнение. Вычисление категории основано на CLDR-правилах, учитывающих:

  • целую и дробную часть числа
  • диапазоны значений
  • модульные операции (например, n % 10)
  • особенности локали

Пример дробных чисел:

const ru = new Intl.PluralRules('ru');

ru.select(1.5); // "many"
ru.select(0.1); // "many"

Дробные значения почти всегда переходят в категорию many или other, в зависимости от языка.


Использование с текстовыми шаблонами

Intl.PluralRules не формирует строки самостоятельно. Его задача — определить категорию, которая затем используется для выбора правильной формы слова.

const ru = new Intl.PluralRules('ru');

const forms = {
  one: 'сообщение',
  few: 'сообщения',
  many: 'сообщений'
};

function formatMessages(count) {
  const form = ru.select(count);
  return `${count} ${forms[form]}`;
}

Примеры результатов:

  • 1 сообщение
  • 2 сообщения
  • 5 сообщений
  • 21 сообщение

Связь с Intl.NumberFormat

Хотя Intl.NumberFormat не определяет грамматические категории, он часто используется совместно с PluralRules для построения локализованных строк.

const nf = new Intl.NumberFormat('ru');
const pr = new Intl.PluralRules('ru');

const forms = {
  one: 'файл',
  few: 'файла',
  many: 'файлов'
};

function formatFiles(n) {
  return `${nf.format(n)} ${forms[pr.select(n)]}`;
}

Intl.NumberFormat отвечает за визуальное представление числа, включая:

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

Intl.PluralRules отвечает за грамматическую корректность.


Кэширование экземпляров правил

Создание Intl.PluralRules может быть затратным при массовом использовании. Поэтому часто применяется кэширование:

const cache = new Map();

function getRules(locale) {
  if (!cache.has(locale)) {
    cache.set(locale, new Intl.PluralRules(locale));
  }
  return cache.get(locale);
}

Такой подход особенно актуален при рендеринге больших списков или серверной генерации текста.


Сравнение числовых категорий между локалями

Локаль Категории
en one, other
ru one, few, many, other
fr one, other
ar zero, one, two, few, many, other
sl one, two, few, other

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


Обработка больших значений

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

const en = new Intl.PluralRules('en');

en.select(1000001); // "one"
en.select(1000002); // "other"

В языках с модульной логикой важны последние цифры:

const ru = new Intl.PluralRules('ru');

ru.select(101); // "one"
ru.select(111); // "many"

Интеграция с форматированием UI-строк

Кардинальные числительные часто используются в интерфейсах:

  • счётчики уведомлений
  • количество товаров
  • результаты поиска
  • статистика

Типовой паттерн формирования строки:

function createLabel(locale, count, forms) {
  const pr = new Intl.PluralRules(locale);
  return `${count} ${forms[pr.select(count)]}`;
}

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


Поведение с отрицательными и нецелыми значениями

Отрицательные числа обрабатываются так же, как положительные, поскольку учитывается абсолютное значение.

const ru = new Intl.PluralRules('ru');

ru.select(-1); // "one"
ru.select(-5); // "many"

Дробные значения чаще всего попадают в обобщённые категории:

ru.select(2.3); // "many"

Ограничения кардинальной модели

Модель кардинальных числительных не решает задачи:

  • выбора падежа (требуется отдельная грамматика)
  • согласования прилагательных
  • контекстной семантики
  • сокращений и аббревиатур

Она строго ограничена задачей выбора числовой формы существительного.


Взаимодействие с форматами данных

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

const pr = new Intl.PluralRules('ru');

pr.select(Number("21")); // "one"

Некорректные значения приводят к NaN, который обычно трактуется как other.

pr.select(Number("abc")); // "other"