Как работать с формой множественного числа в React
Почему форма множественного числа важна в React
Мы часто сталкиваемся с приложениями, в которых появляются неестественные сообщения вроде:
You have 1 new message(s)
Это явный признак того, что разработчик не уделил должного внимания пользовательскому опыту.
Приложениям на React часто приходится работать с формой множественного числа — для счетчиков уведомлений, длины списков или результатов поиска. И делать это правильно не так уж сложно, особенно если вашему приложению нужен только английский язык. Но есть немало ошибок, которые часто допускают начинающие разработчики, особенно при создании многоязычных интерфейсов.
В этом руководстве мы разберем, как корректно работать с формами множественного числа в английском языке, форматировать числа для разных локалей и построить полноценную многоязычную систему множественного числа в React и Next.js.
Проблема с жёстко закодированными формами множественного числа
Во многих проектах — включая, что удивительно, даже крупные и важные — логика множественного числа жёстко закодирована.
export default function Example({ n }) {
return (
<p>
Displaying {n} item{n === 1 ? '' : 's'}
</p>
)
}Но форма множественного числа часто гораздо сложнее, чем просто добавить "s" в конце слова. У некоторых существительных формы множественного числа образуются не по правилу, например "child" и "children". Иногда в зависимости от существительного приходится менять и другие части предложения, например "is" и "are" в зависимости от количества.
В таблице ниже показаны несколько распространенных сценариев:
| Сценарий | Примеры | Примечания |
|---|---|---|
| Количество зрителей | "1 person is watching" "2 people are watching" | Неправильная форма существительного ("person" → "people") и изменение глагола. |
| Удаление элементов | "Delete this message?" "Delete these 2 messages?" | Меняется указательное слово ("this" и "these"), а существительное ставится в форму множественного числа. |
| Результаты поиска | "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 people are watching"
На этом этапе стоит всерьез задуматься о библиотеке интернационализации ("i18n"), не требующей сложного сопровождения.
Хотя разработчики часто считают, что библиотеки i18n нужны только для многоязычных интерфейсов,
они могут быть очень полезны для множественных форм и форматирования переменных даже в одноязычных приложениях.
В основе большинства библиотек i18n лежит встроенный API JavaScript Intl.PluralRules, который определяет правильную форму множественного числа для любого языка.
Для React есть множество библиотек i18n, включая нашу — gt-react (или gt-next, если вы используете Next.js). Показать английское множественное число с помощью gt-react очень просто:
import { Plural } from 'gt-react'
function Example({ count }) {
return (
<Plural n={count} zero={'No one is watching'} one={`${count} person is watching`}>
{count} people are watching
</Plural>
)
}Интерфейс отображается условно в зависимости от значения n.
Большинство библиотек используют JavaScript API Intl.PluralRules, чтобы определить, какую форму числа показывать.
Это означает, что в английском языке имя "one" используется для единственного числа, а "other" — для множественного.
Наш компонент <Plural> использует дочерние элементы по умолчанию, если число, переданное в n, не соответствует ни одному из указанных props.
Использование библиотеки здесь — рекомендуемая практика даже для приложений только на английском языке, и в будущем это значительно упростит интернационализацию.
Форма множественного числа в многоязычных 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>
}Формы множественного числа в разных языках
API Intl.PluralRules в JavaScript поддерживает шесть форм множественного числа: zero, one, two, few, many, other.
Хотя в английском языке используются только one ("единственное число") и other ("множественное число"),
в таких языках, как арабский и польский, таких форм больше двух.
Например, англоязычный пользователь может ожидать:
"Никто не смотрит"
"1 человек смотрит"
"2 человека смотрят"
В то время как пользователь, говорящий по-арабски, может ожидать разные формы для единственного числа, двойственного числа (когда количество равно ровно двум), а также для малых и больших форм множественного числа:
"لا أحد يشاهد"
"1 شخص يشاهد"
"2 شخصان يشاهدان"
"3 أشخاص يشاهدون"
"11 شخصاً يشاهدون"
Именно здесь библиотека интернационализации становится необходимой. У каждого языка свои правила того, когда и как использовать формы множественного числа, поэтому лучше доверить это специализированной библиотеке, чтобы избежать ошибок.
Хорошая библиотека i18n делает две вещи:
- Определяет, какую форму множественного числа (
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 для React-приложений
- кратким руководством по gt-next для приложений Next.js
- справочником по API
<Plural>с полным API компонента
Обработка форм множественного числа — одна из самых частых задач i18n в React; если сделать всё правильно с самого начала, это поможет избежать значительного рефакторинга в будущем.