Назад

Как работать с формой множественного числа в React

Archie McKenzie avatarArchie McKenzie
руководствомножественные формыинтернационализацияreactnextjsi18nintl

Почему форма множественного числа важна в 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 делает две вещи:

  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; если сделать всё правильно с самого начала, это поможет избежать значительного рефакторинга в будущем.