Как обрабатывать плюрализацию в React
Почему важно правильно обрабатывать множественное число в React
Мы часто встречаем приложения, которые отображают корявые сообщения вроде:
У вас 1 новое сообщение
Это явный признак разработчика, который всерьёз не задумывался о пользовательском опыте.
Приложения на React часто нуждаются в поддержке множественного числа — для счетчиков уведомлений, длины списков или результатов поиска. И корректно реализовать работу с формами множественного числа не так уж сложно, особенно если вам нужен интерфейс только на английском. Но существует множество плохих практик, в которые скатываются начинающие разработчики, особенно при создании многоязычных интерфейсов.
В этом руководстве рассматривается, как аккуратно обрабатывать формы множественного числа в английском языке, форматировать числа для разных локалей и построить полноценную многоязычную систему работы с множественным числом в React и Next.js.
Проблема жёстко закодированных форм множественного числа
Многие проекты — в том числе удивительно крупные и важные — жёстко прошивают логику множественного числа прямо в код.
export default function Example({ n }) {
return (
<p>
Отображается {n} элемент{n === 1 ? '' : (n >= 2 && n <= 4 ? 'а' : 'ов')}
</p>
)
}Но множественное число часто образуется не так просто, как просто добавление "s" в конец слова. У некоторых существительных есть неправильные формы множественного числа, как у "child" и "children". Иногда другие части предложения тоже нужно изменить, чтобы согласовать их с существительным — например, "is" и "are" меняются в зависимости от количества.
Таблица ниже иллюстрирует несколько распространённых сценариев:
| Scenario | Examples | Notes |
|---|---|---|
| Viewer count | "1 person is watching" "2 people are watching" | Неправильное существительное ("person" → "people") и изменение глагола. |
| Item deletion | "Delete this message?" "Delete these 2 messages?" | Изменение указательных местоимений ("this" vs. "these") плюс образование множественного числа существительного. |
| Search results | "No results" "1 result found" "2 results found" | Разные формулировки для нуля, одного и нескольких результатов. |
Использование условных выражений очень быстро становится неудобным.
И это превращается в кошмар, когда вам нужно выпустить приложение на других языках. То, что работает для английского, часто полностью ломается в таких языках, как польский или арабский, где действуют совершенно другие правила обработки количеств. Компании, с которыми мы работаем, часто откладывают интернационализацию из‑за боли от рефакторинга такого жёстко захардкоженного UI.
Как работать с множественным числом в английском
В английском языке выбор правильной формы множественного числа в вашем приложении обычно не вызывает сложностей.
Для простой формы образования множественного числа существительных напишите вспомогательную функцию:
export function pluralize(
count: number,
singular: string,
plural: string = singular + 's'
) {
return `${count === 1 ? singular : plural}`;
}Теперь вам достаточно одной функции для обработки всей логики множественного числа, и она работает и с неправильными формами:
pluralize(2, 'user') // "users"
pluralize(2, 'person', 'people') // "people"
pluralize(2, 'child', 'children') // "children"А что, если вам нужна более сложная логика, например:
"No one is watching"
"1 person is watching"
"Смотрят 2 человека"
На этом этапе стоит всерьёз задуматься о библиотеке интернационализации ("i18n"), не требующей сложного обслуживания.
Хотя разработчики часто думают, что библиотеки i18n нужны только для многоязычных интерфейсов,
они могут быть очень полезны для работы с множественным числом и форматирования переменных даже в одноязычных приложениях.
Под капотом большинство библиотек i18n используют встроенный API JavaScript Intl.PluralRules для определения правильной формы множественного числа для любого языка.
Существует множество библиотек i18n для React, включая нашу, gt-react (или gt-next, если вы используете Next.js). Вывести правильную английскую форму множественного числа с помощью gt-react очень просто:
import { Plural } from 'gt-react'
function Example({ count }) {
return (
<Plural n={count} zero={'Никто не смотрит'} one={`Смотрит ${count} человек`}>
Смотрят {count} человек
</Plural>
)
}UI условно рендерится в зависимости от значения n.
Большинство библиотек используют API Intl.PluralRules из JavaScript, чтобы определить, какую форму множественного числа отображать.
Это означает, что в английском вы будете использовать ключ "one" для единственного числа и "other" для множественного.
Наш компонент <Plural> по умолчанию отображает свои дочерние элементы, если число, переданное в n, не соответствует ни одному из указанных пропов.
Использование библиотеки здесь является лучшей практикой даже для приложений только на английском и существенно упрощает будущую интернационализацию.
Формы множественного числа в многоязычных React‑приложениях (i18n)
Если ваше приложение ориентировано только на англоязычных пользователей, описанного выше подхода может быть достаточно — но большинству продакшн‑приложений со временем требуется поддержка нескольких языков. Вот тут и начинается самое интересное.
- В арабском языке существительные имеют разные формы в зависимости от того, ноль, одна, две или много единиц
- В испанском, немецком и итальянском языках в больших числах используются точки вместо запятых, поэтому 1,000,000 записывается как 1.000.000
- В хинди цифры группируются по две, поэтому 1,000,000 записывается как 10,00,000
В интернационализированном приложении следует использовать специализированную i18n‑библиотеку, у которой будет собственная документация по работе с формами множественного числа и форматированием чисел.
Форматирование чисел для разных локалей
Вы можете использовать объект Intl, чтобы форматировать числа для любой локали.
Самый простой способ сделать это — воспользоваться встроенным методом toLocaleString().
По умолчанию он будет использовать текущую локаль среды выполнения:
const n = 1000000
n.toLocaleString() // выводит 1,000,000, когда локаль среды выполнения — "en-US" (американский английский)
n.toLocaleString('de') // 1.000.000, так как локаль указана как "de" (немецкий)gt-react также предоставляет компонент <Num>, который использует Intl.NumberFormat под капотом.
import { Num } from 'gt-react'
// отображает 1,000,000, когда язык — "en-US"
// отображает 1.000.000, когда язык — "de"
// отображает 10,00,000, когда язык — "hi"
export default function Example() {
return <Num>1000000</Num>
}Понимание форм множественного числа в разных языках
В JavaScript API Intl.PluralRules поддерживает шесть форм множественного числа: zero, one, two, few, many, other.
Хотя в английском используются только one («единственное число») и other («множественное число»),
в таких языках, как арабский и польский, форм больше, чем две.
Например, англоязычный пользователь может ожидать:
"No one is watching"
"1 person is watching"
"Смотрят 2 человека"
В то время как арабоязычный пользователь может ожидать разных форм для единственного числа, двойственного числа (когда количество ровно два объекта), а также для малых и больших множественных форм:
"لا أحد يشاهد"
"1 شخص يشاهد"
"2 شخصان يشاهدان"
"3 أشخاص يشاهدون"
"11 شخصاً يشاهدون"
Именно здесь библиотека для интернационализации становится незаменимой. У каждого языка своя логика того, когда и как отображать формы множественного числа, поэтому лучше полагаться на специализированную библиотеку, чтобы всё работало корректно.
Хорошая библиотека для интернационализации делает две вещи:
- Определяет, какую форму множественного числа (
one,many,otherи т. д.) использовать на основе локали - Находит перевод на нужном языке, который соответствует этой форме
Если у вас уже есть библиотека для интернационализации, изучите её документацию по форматированию множественного числа. Почти у всех библиотек есть отдельные разделы документации, посвящённые рендерингу форм множественного числа.
Полный пример: формы множественного числа в многоязычном приложении на React
Если у вас ещё нет библиотеки для интернационализации, присмотритесь к gt-react!
Компонент <Plural> из gt-react:
- простой, декларативный способ корректно выводить формы множественного числа в React
- нативно работает с форматирующим компонентом
<Num> - нативно работает с компонентом перевода
<T>, который интегрирован с нашим бесплатным сервисом перевода и автоматически генерирует формы множественного числа
Собрав все блоки воедино, мы получаем полноценный многоязычный компонент:
import { T, Plural, Num } from 'gt-react'
// Работает сразу после установки на 100+ языках
function Example({ count }) {
return (
<T>
<Plural
n={count}
zero={'Никто не смотрит'}
one={
<>
<Num>{count}</Num> человек смотрит
</>
}
>
<Num>{count}</Num> человек смотрят
</Plural>
</T>
)
}Дальнейшие шаги
Готовы правильно обрабатывать множественное число в своём React‑приложении? Ознакомьтесь с нашими краткими руководствами по началу работы:
- gt-react quickstart для приложений на React
- gt-next quickstart для приложений на Next.js
- Справочник по API
<Plural>с полным описанием API компонента
Плюрализация — одна из самых распространённых задач i18n в React: если сразу реализовать её правильно, это сэкономит значительные усилия на рефакторинг в дальнейшем.