Порядковые числительные

В JavaScript работа с порядковыми числительными строится вокруг двух основных механизмов международного API: Intl.PluralRules и Intl.NumberFormat. Оба инструмента решают задачу языковой адаптации чисел, но делают это на разных уровнях: первый определяет грамматическую категорию числа, второй формирует конечное отображение строки с учётом локали.

Порядковые числительные (first, second, third; первый, второй, третий и т.д.) в разных языках подчиняются различным грамматическим правилам. В английском языке достаточно различать суффиксы st, nd, rd, th, тогда как в славянских языках система сложнее и зависит от падежных и числовых форм.


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

Интерфейс Intl.PluralRules предоставляет метод классификации чисел по языковым категориям. Для порядковых числительных используется тип ordinal.

Базовый набор категорий включает:

  • one — первая форма
  • two — вторая форма
  • few — ограниченное количество (в некоторых языках)
  • other — остальные случаи

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

  • английский язык: one, two, few, other фактически сводятся к различению суффиксов
  • русский язык: используется система one, few, many, other, но для порядковых форм поведение отличается от количественных

Использование Intl.PluralRules для порядковых числительных

Основной инструмент для определения порядковой формы:

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

rules.select(1);  // "one"
rules.select(2);  // "two"
rules.select(3);  // "few"
rules.select(4);  // "other"

Фактическая интерпретация значений зависит от локали. В английском языке эти категории затем сопоставляются с суффиксами:

function getOrdinalSuffix(n) {
  const pr = new Intl.PluralRules('en-US', { type: 'ordinal' });

  const category = pr.select(n);

  const suffixes = {
    one: 'st',
    two: 'nd',
    few: 'rd',
    other: 'th'
  };

  return `${n}${suffixes[category]}`;
}

getOrdinalSuffix(1);  // "1st"
getOrdinalSuffix(2);  // "2nd"
getOrdinalSuffix(3);  // "3rd"
getOrdinalSuffix(4);  // "4th"

Особенности английской локали

Английская система порядковых числительных содержит исключения:

  • 11 → 11th
  • 12 → 12th
  • 13 → 13th

Они не подчиняются стандартным правилам 1 → st, 2 → nd, 3 → rd. Поэтому корректная реализация должна учитывать диапазон 10–20:

function getEnglishOrdinal(n) {
  const mod100 = n % 100;

  if (mod100 >= 11 && mod100 <= 13) {
    return `${n}th`;
  }

  const mod10 = n % 10;

  switch (mod10) {
    case 1:
      return `${n}st`;
    case 2:
      return `${n}nd`;
    case 3:
      return `${n}rd`;
    default:
      return `${n}th`;
  }
}

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


Intl.NumberFormat и форматирование порядковых числительных

Современный подход в браузерах включает использование Intl.NumberFormat с параметром style: 'ordinal'.

const formatter = new Intl.NumberFormat('en-US', {
  style: 'ordinal'
});

formatter.format(1); // "1st"
formatter.format(2); // "2nd"
formatter.format(3); // "3rd"
formatter.format(4); // "4th"

Этот способ скрывает внутреннюю логику определения суффиксов и полностью делегирует её движку интернационализации.


Локализация порядковых форм

Английский язык

Для en-US и en-GB поведение предсказуемо и основано на суффиксах:

const f = new Intl.NumberFormat('en-GB', { style: 'ordinal' });

f.format(21); // "21st"
f.format(22); // "22nd"
f.format(23); // "23rd"
f.format(24); // "24th"

Русский язык

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

const pr = new Intl.PluralRules('ru', { type: 'ordinal' });

pr.select(1); // "one"
pr.select(2); // "few"
pr.select(5); // "many"

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


Отличие cardinal и ordinal

В Intl.PluralRules существует два типа:

  • cardinal — количественные числительные (1 яблоко, 2 яблока)
  • ordinal — порядковые (1-й, 2-й, 3-й)

Пример сравнения:

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

cardinal.select(1); // "one"
ordinal.select(1);   // "one" (но другая семантика)

Несмотря на совпадение категорий в некоторых локалях, интерпретация различается.


Практическое разделение логики

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

  1. Определение грамматической категории
  2. Преобразование категории в строку
  3. Локализация результата

Пример архитектуры:

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

const suffixMap = new Map([
  ['one', 'st'],
  ['two', 'nd'],
  ['few', 'rd'],
  ['other', 'th']
]);

function formatOrdinal(n) {
  const category = rules.select(n);
  return `${n}${suffixMap.get(category)}`;
}

Такой подход позволяет заменять локаль без изменения бизнес-логики.


Производительность и кэширование

Создание экземпляров Intl.PluralRules и Intl.NumberFormat связано с затратами на инициализацию локали. В высоконагруженных системах используется кэширование:

const cache = new Map();

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

Аналогичный подход применяется к форматтерам чисел.


Ограничения API

Несмотря на универсальность, существуют ограничения:

  • не все локали поддерживают полноценные порядковые формы в Intl.NumberFormat
  • Intl.PluralRules не возвращает готовую текстовую форму числительного
  • сложные языки требуют внешних словарей
  • поведение может различаться между движками (V8, SpiderMonkey, JavaScriptCore)

Комбинирование с другими API интернационализации

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

  • Intl.DateTimeFormat (даты: 1st January)
  • Intl.RelativeTimeFormat (1st day ago)
  • Intl.ListFormat (списки элементов с порядком)

Пример интеграции:

const ordinal = new Intl.NumberFormat('en-US', { style: 'ordinal' });

const day = ordinal.format(1);
const month = new Intl.DateTimeFormat('en-US', { month: 'long' }).format(new Date());

`${day} ${month}`; // "1st May"

Обработка пользовательских интерфейсов

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

  • рейтингов (1st place, 2nd place)
  • списков шагов (Step 1, Step 2)
  • дат и событий

При этом предпочтительно отделять форматирование от бизнес-логики, чтобы UI мог переключать локали без переработки данных.

function formatStep(n, locale) {
  const formatter = new Intl.NumberFormat(locale, { style: 'ordinal' });
  return `Step ${formatter.format(n)}`;
}

Поведение в разных движках JavaScript

Разные среды исполнения могут по-разному поддерживать style: 'ordinal':

  • современные браузеры поддерживают полностью
  • Node.js требует актуальной версии ICU
  • серверные окружения могут иметь ограниченный набор локалей

Поэтому часто используется резервный механизм через Intl.PluralRules.


Обобщённая модель работы с порядковыми числительными

Архитектурно процесс можно описать как цепочку:

  1. входное число
  2. определение локали
  3. выбор грамматической категории (Intl.PluralRules)
  4. преобразование в строку (суффикс или слово)
  5. вывод результата

Такая модель позволяет масштабировать интернационализацию на десятки языков без изменения основной логики приложения.