Как форматировать порядковые числа, такие как 1-й, 2-й, 3-й

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

Введение

Порядковые числительные указывают на позицию или ранг в последовательности. В английском языке вы пишете 1st, 2nd, 3rd, 4th, чтобы описать финишные позиции в гонке или элементы в списке. Эти суффиксы помогают отличить порядковые числительные от обычных чисел для подсчета.

Разные языки используют совершенно разные правила для порядковых числительных. В английском добавляются суффиксы, такие как st, nd, rd и th. Во французском используются надстрочные буквы, такие как 1er и 2e. В немецком после числа ставится точка, например, 1. и 2. В японском перед числом добавляется символ 第. Когда вы жестко задаете английские порядковые суффиксы, вы предполагаете, что все пользователи следуют одной и той же системе.

JavaScript предоставляет API Intl.PluralRules с типом ordinal, чтобы автоматически учитывать эти различия. Этот урок объясняет, что такое порядковые числительные, почему их форматирование различается в разных языках и как правильно их форматировать для любого региона.

Что такое порядковые числительные

Порядковые числительные выражают позицию, ранг или порядок в последовательности. Они отвечают на вопрос "какой по счету", а не "сколько". Числа 1st, 2nd, 3rd описывают позиции в гонке. Первый, второй, третий описывают элементы в списке.

Количественные числительные выражают количество или объем. Они отвечают на вопрос "сколько". Числа 1, 2, 3 описывают количество объектов. Один, два, три описывают объемы.

Одно и то же числовое значение используется для обоих случаев в зависимости от контекста. Число 5 является количественным в "5 яблок", но порядковым в "5-е место". Это различие важно, так как во многих языках порядковые числительные форматируются иначе, чем количественные.

В английском языке порядковые числительные до 10 имеют уникальные словесные формы. Первый, второй, третий, четвертый, пятый — это отдельные слова. Выше 10 английский формирует порядковые числительные, добавляя суффиксы. Одиннадцатый, двенадцатый, двадцатый, двадцать первый следуют определенным правилам, но требуют суффиксов.

При записи порядковых числительных в виде чисел, а не слов, в английском добавляются суффиксы st, nd, rd или th. Эти суффиксы следуют определенным правилам в зависимости от последних цифр числа.

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

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

В английском языке порядковые числительные используют четыре разных суффикса. Числа, оканчивающиеся на 1, используют st, на 2 — nd, на 3 — rd, а все остальные — th. Однако числа, оканчивающиеся на 11, 12 или 13, всегда используют th. Это создает такие формы, как 1st, 2nd, 3rd, 4th, 11th, 12th, 13th, 21st, 22nd, 23rd.

Во французском языке порядковые числительные используют надстрочные сокращения. Первое число обозначается как 1er для мужского рода или 1re для женского. Все остальные порядковые числительные используют e в надстрочном формате, например, 2e, 3e, 4e. Форматирование включает надстрочную типографику, а не только суффиксы.

В немецком языке порядковые числительные используют точку после числа. Обозначения 1., 2., 3. представляют собой первый, второй, третий. Эта точка сигнализирует, что читатель должен мысленно добавить соответствующее грамматическое окончание при чтении вслух.

В испанском языке порядковые числительные используют гендерные надстрочные индикаторы. Мужские порядковые числительные обозначаются как 1.º, 2.º, 3.º, а женские — как 1.ª, 2.ª, 3.ª. Точка отделяет число от индикатора.

В японском языке порядковые числительные добавляют префикс 第 перед числом. Первый, второй, третий обозначаются как 第一, 第二, 第三. Этот префикс меняет значение с количественного на порядковое.

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

Понимание Intl.PluralRules с типом ordinal

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

Конструктор принимает идентификатор локали и объект с опциями. Установите опцию type в значение "ordinal", чтобы работать с порядковыми числительными вместо количественных.

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

Это создает объект правил, который понимает английские порядковые шаблоны. Метод select() возвращает имя категории для любого числа, которое вы передаете.

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

console.log(rules.select(1));
// Вывод: "one"

console.log(rules.select(2));
// Вывод: "two"

console.log(rules.select(3));
// Вывод: "few"

console.log(rules.select(4));
// Вывод: "other"

Возвращаемые категории — это лингвистические термины, а не фактические суффиксы. Категория "one" соответствует числам, которые в английском языке используют суффикс st. Категория "two" соответствует суффиксу nd. Категория "few" соответствует суффиксу rd. Категория "other" соответствует суффиксу th.

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

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

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

function formatOrdinal(number, locale) {
  const rules = new Intl.PluralRules(locale, { type: 'ordinal' });
  const category = rules.select(number);

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

  const suffix = suffixes[category];
  return `${number}${suffix}`;
}

console.log(formatOrdinal(1, 'en-US'));
// Вывод: "1st"

console.log(formatOrdinal(2, 'en-US'));
// Вывод: "2nd"

console.log(formatOrdinal(3, 'en-US'));
// Вывод: "3rd"

console.log(formatOrdinal(4, 'en-US'));
// Вывод: "4th"

Эта функция создает новый экземпляр PluralRules каждый раз при запуске. Для повышения производительности создайте объект правил один раз и используйте его для нескольких чисел.

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

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

function formatOrdinal(number) {
  const category = rules.select(number);
  const suffix = suffixes[category];
  return `${number}${suffix}`;
}

console.log(formatOrdinal(1));
// Вывод: "1st"

console.log(formatOrdinal(21));
// Вывод: "21st"

console.log(formatOrdinal(22));
// Вывод: "22nd"

console.log(formatOrdinal(23));
// Вывод: "23rd"

API корректно обрабатывает числа, такие как 11, 12 и 13, которые используют суффикс th, несмотря на их последние цифры.

console.log(formatOrdinal(11));
// Вывод: "11th"

console.log(formatOrdinal(12));
// Вывод: "12th"

console.log(formatOrdinal(13));
// Вывод: "13th"

Правила множественного числа кодируют все особые случаи и исключения для локали. Вам не нужно писать условную логику для обработки этих крайних случаев.

Форматирование порядковых чисел для разных локалей

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

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

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

console.log(enRules.select(1));
// Вывод: "one"

console.log(cyRules.select(1));
// Вывод: "one"

console.log(enRules.select(2));
// Вывод: "two"

console.log(cyRules.select(2));
// Вывод: "two"

console.log(enRules.select(5));
// Вывод: "other"

console.log(cyRules.select(5));
// Вывод: "many"

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

const ordinalSuffixes = {
  'en-US': {
    one: 'st',
    two: 'nd',
    few: 'rd',
    other: 'th'
  },
  'fr-FR': {
    one: 'er',
    other: 'e'
  }
};

function formatOrdinal(number, locale) {
  const rules = new Intl.PluralRules(locale, { type: 'ordinal' });
  const category = rules.select(number);
  const suffixes = ordinalSuffixes[locale];
  const suffix = suffixes[category] || suffixes.other;
  return `${number}${suffix}`;
}

console.log(formatOrdinal(1, 'en-US'));
// Вывод: "1st"

console.log(formatOrdinal(1, 'fr-FR'));
// Вывод: "1er"

console.log(formatOrdinal(2, 'en-US'));
// Вывод: "2nd"

console.log(formatOrdinal(2, 'fr-FR'));
// Вывод: "2e"

Этот подход хорошо работает, если вы контролируете строки суффиксов для каждой локали. Однако он требует поддержания данных о суффиксах для каждой поддерживаемой локали.

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

Intl.PluralRules API использует шесть возможных названий категорий. Разные локали используют разные подмножества этих категорий.

Категории включают zero, one, two, few, many и other. Не все языки различают все шесть категорий. Английские порядковые числительные используют только четыре: one, two, few и other.

Названия категорий не соответствуют напрямую числовым значениям. Категория "one" включает 1, 21, 31, 41 и все числа, оканчивающиеся на 1, кроме 11. Категория "two" включает 2, 22, 32, 42 и все числа, оканчивающиеся на 2, кроме 12.

Вы можете проверить, какие категории использует локаль, вызвав метод resolvedOptions() и изучив свойство pluralCategories.

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

console.log(options.pluralCategories);
// Вывод: ["one", "two", "few", "other"]

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

const rules = new Intl.PluralRules('fr-FR', { type: 'ordinal' });
const options = rules.resolvedOptions();

console.log(options.pluralCategories);
// Вывод: ["one", "other"]

Французские порядковые числительные различают только one и other. Это упрощённое разделение отражает более простые правила суффиксов во французском языке.

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

Вместо жёсткого кодирования определённой локали вы можете использовать предпочтительный язык пользователя из браузера. Свойство navigator.language возвращает предпочтительный язык пользователя.

const userLocale = navigator.language;
const rules = new Intl.PluralRules(userLocale, { type: 'ordinal' });

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

function formatOrdinal(number) {
  const category = rules.select(number);
  const suffix = suffixes[category] || suffixes.other;
  return `${number}${suffix}`;
}

console.log(formatOrdinal(1));
// Вывод зависит от локали пользователя

Этот подход автоматически адаптируется к языковым предпочтениям пользователя. Однако вам всё равно нужно предоставить соответствующие суффиксы для каждой локали, поддерживаемой вашим приложением.

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

function formatOrdinal(number, locale = navigator.language) {
  const rules = new Intl.PluralRules(locale, { type: 'ordinal' });
  const category = rules.select(number);

  const localeMapping = ordinalSuffixes[locale];

  if (!localeMapping) {
    return String(number);
  }

  const suffix = localeMapping[category] || localeMapping.other || '';
  return `${number}${suffix}`;
}

Эта функция возвращает только число, если для локали отсутствует соответствующий суффикс.

Основные случаи использования порядковых числительных

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

Рейтинги и таблицы лидеров показывают позиции пользователей. Приложение для игр отображает "1-е место", "2-е место", "3-е место" вместо "место 1", "место 2", "место 3".

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

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

  const suffix = suffixes[category];
  return `${position}${suffix} place`;
}

console.log(formatRanking(1));
// Вывод: "1st place"

console.log(formatRanking(42));
// Вывод: "42nd place"

Форматирование дат иногда использует порядковые числительные для обозначения дня месяца. В некоторых локалях пишут "1 января" вместо "1 января".

Пошаговые инструкции используют порядковые числительные для нумерации каждого шага. В руководстве отображается "1-й шаг: Установите программное обеспечение", "2-й шаг: Настройте параметры", "3-й шаг: Запустите приложение".

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

Повторное использование объектов правил для повышения производительности

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

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

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

function formatOrdinal(number) {
  const category = rules.select(number);
  const suffix = suffixes[category];
  return `${number}${suffix}`;
}

const positions = [1, 2, 3, 4, 5];

positions.forEach(position => {
  console.log(formatOrdinal(position));
});
// Вывод:
// "1st"
// "2nd"
// "3rd"
// "4th"
// "5th"

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

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

function createOrdinalFormatter(locale, suffixMapping) {
  const rules = new Intl.PluralRules(locale, { type: 'ordinal' });

  return function(number) {
    const category = rules.select(number);
    const suffix = suffixMapping[category] || suffixMapping.other || '';
    return `${number}${suffix}`;
  };
}

const formatEnglishOrdinal = createOrdinalFormatter('en-US', {
  one: 'st',
  two: 'nd',
  few: 'rd',
  other: 'th'
});

console.log(formatEnglishOrdinal(1));
// Вывод: "1st"

console.log(formatEnglishOrdinal(2));
// Вывод: "2nd"

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