Плюрализация в React: основы
Что такое множественное число?
Я часто встречаю приложения, которые отображают корявые сообщения вроде:
У вас 1 новое сообщение
Это явный признак разработчика, который всерьёз не задумывался о пользовательском опыте.
Приложения на React часто нуждаются в поддержке множественного числа — для счетчиков уведомлений, длины списков или результатов поиска. И корректно реализовать работу с формами множественного числа не так уж сложно, особенно если вам нужен интерфейс только на английском. Но существует множество плохих практик, в которые скатываются начинающие разработчики, особенно при создании многоязычных интерфейсов.
Жёстко закодированные формы множественного числа
Многие проекты — в том числе удивительно крупные и важные — жёстко прошивают логику множественного числа прямо в код.
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"Но что, если вам нужна более сложная логика, например:
"Никто не смотрит"
"Смотрит 1 человек"
"Смотрят 2 человека"
На этом этапе вам стоит всерьёз задуматься о библиотеке интернационализации («i18n») с низкими затратами на поддержку.
Хотя разработчики часто думают, что библиотеки i18n нужны только для многоязычных интерфейсов, они могут быть очень полезны для работы с формами множественного числа и форматированием переменных даже в однозначных приложениях.
Существует множество библиотек 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>
)
}Интерфейс условно рендерится в зависимости от значения n.
Большинство библиотек используют объект JavaScript Intl, чтобы определить, какую форму множественного числа отображать.
Это означает, что в английском вы будете использовать ключ "one" для единственного числа и "other" для множественного.
Наш компонент <Plural> по умолчанию рендерит своих потомков, если число, переданное в n, не соответствует ни одному из переданных пропсов.
Использование библиотеки здесь является наилучшей практикой даже для приложений только на английском и значительно упрощает будущую интернационализацию.
Интернационализация (i18n) и формы множественного числа
Поддержка многоязычного интерфейса значительно усложняет работу с формами множественного числа.
- В арабском языке существительные имеют разные формы в зависимости от того, ноль, одна, две или много единиц
- В испанском, немецком и итальянском языках в больших числах используются точки вместо запятых, поэтому 1,000,000 записывается как 1.000.000
- В хинди цифры группируются по две, поэтому 1,000,000 записывается как 10,00,000
В интернационализированном приложении следует использовать специализированную библиотеку, у которой будет собственная документация по работе с формами множественного числа и форматированием чисел.
Форматирование чисел для разных языков
Вы также можете использовать объект 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>
}Отображение альтернативных форм множественного числа
Объект Intl в JavaScript поддерживает шесть форм множественного числа: zero, one, two, few, many, other.
Хотя в английском используются только one («единственное число») и other («множественное число»),
в таких языках, как арабский и польский, форм больше, чем две.
Например, англоязычный пользователь может ожидать:
"Никто не смотрит"
"Смотрит 1 человек"
"Смотрят 2 человека"
В то время как арабоязычный пользователь может ожидать разных форм для единственного числа, двойственного числа (когда количество ровно два объекта), а также для малых и больших множественных форм:
"Никто не смотрит"
"Смотрит 1 человек"
"Смотрят 2 человека"
"Смотрят 3 человека"
"Смотрят 11 человек"
Именно здесь библиотека для интернационализации становится незаменимой. У каждого языка своя логика того, когда и как отображать формы множественного числа, поэтому лучше полагаться на специализированную библиотеку, чтобы всё работало корректно.
Хорошая библиотека для интернационализации делает две вещи:
- Определяет, какую форму множественного числа (
one,many,otherи т. д.) использовать на основе локали - Находит перевод на нужном языке, который соответствует этой форме
Если у вас уже есть библиотека для интернационализации, изучите её документацию по форматированию множественного числа. Почти у всех библиотек есть отдельные разделы документации, посвящённые рендерингу форм множественного числа.
Собираем все блоки воедино
Если у вас ещё нет библиотеки для интернационализации, присмотритесь к gt-react!
Компонент <Plural> из gt-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>
)
}Хотите узнать больше? Ознакомьтесь с документацией по настройке gt-react или gt-next.