Назад

Как обрабатывать плюрализацию в React

Archie McKenzie avatarArchie McKenzie
guidepluralsinternationalizationreactnextjsi18nintl

Почему важно правильно обрабатывать множественное число в 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" меняются в зависимости от количества.

Таблица ниже иллюстрирует несколько распространённых сценариев:

ScenarioExamplesNotes
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 شخصاً يشاهدون"

Именно здесь библиотека для интернационализации становится незаменимой. У каждого языка своя логика того, когда и как отображать формы множественного числа, поэтому лучше полагаться на специализированную библиотеку, чтобы всё работало корректно.

Хорошая библиотека для интернационализации делает две вещи:

  1. Определяет, какую форму множественного числа (one, many, other и т. д.) использовать на основе локали
  2. Находит перевод на нужном языке, который соответствует этой форме

Если у вас уже есть библиотека для интернационализации, изучите её документацию по форматированию множественного числа. Почти у всех библиотек есть отдельные разделы документации, посвящённые рендерингу форм множественного числа.

Полный пример: формы множественного числа в многоязычном приложении на 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‑приложении? Ознакомьтесь с нашими краткими руководствами по началу работы:

Плюрализация — одна из самых распространённых задач i18n в React: если сразу реализовать её правильно, это сэкономит значительные усилия на рефакторинг в дальнейшем.