Множественное число в ICU MessageFormat в FormatJS основано на
стандартизации правил CLDR (Common Locale Data Repository), где каждая
локаль описывается через набор категорий множественного числа. Эти
категории интерпретируются движком Intl.PluralRules, а
затем используются в react-intl и других пакетах FormatJS
для выбора корректной языковой формы.
Базовая идея заключается в том, что числовое значение не преобразуется напрямую в строку, а проходит через классификатор языковых правил, возвращающий категорию, соответствующую локали.
Основные категории множественного числа:
Не каждая локаль использует все категории. Конкретный набор зависит от языка и правил CLDR.
В основе механизма лежит Intl.PluralRules, который
определяет категорию числа:
const pr = new Intl.PluralRules('en');
pr.select(1); // "one"
pr.select(2); // "other"
FormatJS использует этот механизм внутри ICU-сообщений, где разработчик описывает все возможные формы:
import { defineMessages } from 'react-intl';
const messages = defineMessages({
apples: {
id: 'fruits.apples',
defaultMessage: 'Есть {count, plural, one {# яблоко} other {# яблок}}'
}
});
Знак # автоматически подставляет текущее значение
count.
Английский язык использует минимальную систему категорий:
Правило крайне простое:
const pr = new Intl.PluralRules('en');
pr.select(1); // one
pr.select(0); // other
pr.select(5); // other
ICU-сообщение:
{count, plural,
one {# file}
other {# files}
}
Особенность английской модели заключается в отсутствии различий между
нулём и множественными формами — всё, кроме единицы, считается
other.
Русский язык использует четыре категории:
Поведение определяется правилами остатка от деления:
const pr = new Intl.PluralRules('ru');
pr.select(1); // one
pr.select(2); // few
pr.select(5); // many
pr.select(21); // one
ICU-шаблон в FormatJS:
{count, plural,
one {# файл}
few {# файла}
many {# файлов}
other {# файла}
}
Особенность системы заключается в контекстной зависимости от
последних цифр числа и исключении нуля из формы one.
Польский язык также использует несколько категорий:
Пример:
const pr = new Intl.PluralRules('pl');
pr.select(1); // one
pr.select(2); // few
pr.select(5); // many
pr.select(12); // many
ICU-конструкция:
{count, plural,
one {# plik}
few {# pliki}
many {# plików}
other {# pliku}
}
Сложность польской системы связана с исключениями в диапазонах 12–14, которые не следуют базовому правилу малых чисел.
Арабский язык обладает одной из самых детализированных систем:
const pr = new Intl.PluralRules('ar');
pr.select(0); // zero
pr.select(1); // one
pr.select(2); // two
pr.select(3); // few
pr.select(11); // many
pr.select(100); // other
ICU-форма:
{count, plural,
zero {لا ملفات}
one {ملف واحد}
two {ملفان}
few {# ملفات}
many {# ملفاً}
other {# ملف}
}
Арабская система требует полного покрытия всех категорий, иначе часть чисел может отображаться некорректно.
Некоторые языки не различают формы единственного и множественного числа в грамматике. Примером служат восточноазиатские языки, включая японский и китайский.
В таких локалях Intl.PluralRules всегда возвращает
other:
const pr = new Intl.PluralRules('ja');
pr.select(1); // other
pr.select(5); // other
ICU-сообщение упрощается до одной формы:
{count} ファイル
В FormatJS условное ветвление фактически не требуется, однако синтаксис plural сохраняется для унификации.
FormatJS использует синтаксис ICU, где plural — это выражение с ключами категорий:
{variable, plural,
category {message}
}
Алгоритм обработки включает несколько этапов:
Intl.PluralRules# и переменныхoffset в plural-форматахICU поддерживает смещение, позволяющее исключить текущее значение из вычислений:
{count, plural, offset:1
one {Ты и ещё # человек}
other {Ты и ещё # человек}
}
Если count = 1, то отображается:
Ты и ещё 0 человек
Это используется в сценариях, где текущее значение уже учитывается в тексте.
FormatJS позволяет комбинировать plural с другими ICU-конструкциями:
{gender, select,
male {{count, plural, one {Он добавил # файл} other {Он добавил # файлов}}}
female {{count, plural, one {Она добавила # файл} other {Она добавила # файлов}}}
other {{count, plural, one {Добавлено # файл} other {Добавлено # файлов}}}
}
Такая структура используется для мультиязычных и мультиязыковых сообщений, где одновременно учитываются грамматический род и число.
Intl.PluralRules поддерживает параметризацию:
new Intl.PluralRules('ru', { type: 'cardinal' });
new Intl.PluralRules('ru', { type: 'ordinal' });
Ordinal-формы используются для порядковых числительных:
const pr = new Intl.PluralRules('en', { type: 'ordinal' });
pr.select(1); // "one" -> 1st
pr.select(2); // "two" -> 2nd
FormatJS может комбинировать cardinal и ordinal правила в зависимости от контекста интерфейса.
CLDR определяет правила через выражения диапазонов:
one: n mod 10 = 1 and n mod 100 != 11
few: n mod 10 in 2..4 and n mod 100 not in 12..14
many: n mod 10 = 0 or n mod 10 in 5..9 or n mod 100 in 11..14
Эти правила интерпретируются движком JavaScript и не требуют ручной реализации.
Для дробных значений большинство языков используют категорию
other:
const pr = new Intl.PluralRules('ru');
pr.select(1.5); // other
pr.select(2.3); // other
ICU-шаблоны обычно не предусматривают отдельные формы для дробных чисел, за исключением отдельных локалей с особыми правилами.
Если в ICU-шаблоне отсутствует подходящая категория, используется
other как fallback:
{count, plural,
one {# item}
other {# items}
}
Даже при языках с несколькими категориями отсутствие ветки не приводит к ошибке — выбирается наиболее универсальная форма.
Внутри FormatJS объекты Intl.PluralRules обычно
кешируются по локали, поскольку их создание может быть затратным:
const cache = new Map();
function getPluralRules(locale) {
if (!cache.has(locale)) {
cache.set(locale, new Intl.PluralRules(locale));
}
return cache.get(locale);
}
Это особенно важно при серверном рендеринге и частых вычислениях интерфейса.
Plural-формы не форматируют числа автоматически. Для отображения
чисел с локализацией используется Intl.NumberFormat:
{count, plural,
one {{count, number} файл}
other {{count, number} файлов}
}
Это позволяет одновременно применять языковые правила и локализованное форматирование чисел (разделители тысяч, десятичные знаки).