Как форматировать порядковые числа: 1-й, 2-й, 3-й
Используйте JavaScript для отображения порядковых чисел с подходящими для локали суффиксами и форматированием
Введение
Порядковые числа обозначают положение или ранг в последовательности. На английском пишут 1st, 2nd, 3rd, 4th, чтобы указать места в гонке или позиции в списке. Эти суффиксы помогают отличать порядковые числа от обычных количественных.
В разных языках используются совершенно разные правила для порядковых чисел. В английском добавляют суффиксы st, nd, rd и th. Во французском используют надстрочные буквы, например 1er и 2e. В немецком после числа ставят точку: 1. и 2. В японском перед числом ставят символ 第. Если вы жёстко прописываете английские суффиксы, вы предполагаете, что все пользователи следуют этим правилам.
JavaScript предоставляет API Intl.PluralRules с типом ordinal, чтобы автоматически учитывать эти различия. В этом уроке объясняется, что такое порядковые числа, почему их форматирование отличается в разных языках и как правильно их отображать для любой локали.
Что такое порядковые числа
Порядковые числа выражают положение, ранг или порядок в последовательности. Они отвечают на вопрос «какой по счёту», а не «сколько». Числа 1-й, 2-й, 3-й обозначают места в гонке. Первый, второй, третий — позиции в списке.
Количественные (кардинальные) числа выражают количество или объём. Они отвечают на вопрос «сколько». Числа 1, 2, 3 обозначают количество объектов. Один, два, три — объёмы.
Одно и то же число может быть и порядковым, и количественным — всё зависит от контекста. Например, 5 — это количественное в «5 яблок», но порядковое в «5-е место». Это различие важно, потому что во многих языках порядковые числа оформляются иначе, чем количественные.
В английском языке порядковые числительные до 10 имеют уникальные формы. First, second, third, fourth, fifth — это отдельные слова. После 10 порядковые образуются с помощью суффиксов. Например: eleventh, twelfth, twentieth, twenty-first — все они строятся по определённым шаблонам, но требуют добавления суффиксов.
Когда порядковые числительные записываются цифрами, к числу добавляются суффиксы st, nd, rd или th. Эти суффиксы выбираются по определённым правилам, которые зависят от последних цифр числа.
Почему форматирование порядковых числительных зависит от языка
В разных языках сложились свои системы выражения порядковых числительных. Эти правила отражают особенности грамматики, письменности и культурных традиций каждого языка.
В английском для порядковых числительных используются четыре разных суффикса. Числа, оканчивающиеся на 1, получают suффикс 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));
// Output: "one"
console.log(rules.select(2));
// Output: "two"
console.log(rules.select(3));
// Output: "few"
console.log(rules.select(4));
// Output: "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'));
// Output: "1st"
console.log(formatOrdinal(2, 'en-US'));
// Output: "2nd"
console.log(formatOrdinal(3, 'en-US'));
// Output: "3rd"
console.log(formatOrdinal(4, 'en-US'));
// Output: "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));
// Output: "1st"
console.log(formatOrdinal(21));
// Output: "21st"
console.log(formatOrdinal(22));
// Output: "22nd"
console.log(formatOrdinal(23));
// Output: "23rd"
API правильно обрабатывает числа, такие как 11, 12 и 13, для которых используется суффикс th, несмотря на их последние цифры.
console.log(formatOrdinal(11));
// Output: "11th"
console.log(formatOrdinal(12));
// Output: "12th"
console.log(formatOrdinal(13));
// Output: "13th"
Правила множественного числа учитывают все особые случаи и исключения для локали. Вам не нужно писать условную логику для обработки этих крайних случаев.
Форматирование порядковых числительных для разных локалей
Категории множественного числа и их значения различаются в зависимости от локали. В некоторых языках категорий меньше, чем в английском. В других — другие правила распределения чисел по категориям.
В валлийском используется другая система категоризации. Правила выделяют больше категорий, каждая из которых соответствует разным формам порядковых числительных.
const enRules = new Intl.PluralRules('en-US', { type: 'ordinal' });
const cyRules = new Intl.PluralRules('cy', { type: 'ordinal' });
console.log(enRules.select(1));
// Output: "one"
console.log(cyRules.select(1));
// Output: "one"
console.log(enRules.select(2));
// Output: "two"
console.log(cyRules.select(2));
// Output: "two"
console.log(enRules.select(5));
// Output: "other"
console.log(cyRules.select(5));
// Output: "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'));
// Output: "1st"
console.log(formatOrdinal(1, 'fr-FR'));
// Output: "1er"
console.log(formatOrdinal(2, 'en-US'));
// Output: "2nd"
console.log(formatOrdinal(2, 'fr-FR'));
// Output: "2e"
Этот подход хорошо работает, если вы управляете строками суффиксов для каждой локали. Однако потребуется поддерживать данные о суффиксах для всех поддерживаемых локалей.
Понимание категорий порядковых множественных чисел
API Intl.PluralRules использует шесть возможных названий категорий. В разных локалях используются разные подмножества этих категорий.
Категории: 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);
// Output: ["one", "two", "few", "other"]
Это показывает, что в английском языке для порядковых числительных используются четыре категории. В других языках наборы категорий могут отличаться.
const rules = new Intl.PluralRules('fr-FR', { type: 'ordinal' });
const options = rules.resolvedOptions();
console.log(options.pluralCategories);
// Output: ["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));
// Output varies by user's locale
Такой подход автоматически подстраивается под языковые предпочтения пользователя. Однако для каждой поддерживаемой локали всё равно нужно предоставить соответствующие суффиксы.
Для локалей без специальных суффиксов можно использовать поведение по умолчанию или просто показывать число без суффикса.
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));
// Output: "1st place"
console.log(formatRanking(42));
// Output: "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));
});
// Output:
// "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));
// Output: "1st"
console.log(formatEnglishOrdinal(2));
// Output: "2nd"
Этот паттерн объединяет объект правил и сопоставление суффиксов, что упрощает повторное использование форматтера в вашем приложении.