戻る

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" を使い分けるように、 名詞に合わせて文中のほかの部分も変えなければならないことがあります。

以下の表は、よくあるパターンをいくつか示したものです。

ScenarioExamplesNotes
視聴者数"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"
0件、1件、複数件で表現が変わります。

条件式で対応しようとすると、すぐに複雑になります。

さらに、アプリをほかの言語でも展開しようとすると、事態は一気に厄介になります。 英語ではうまく機能しても、数量の扱いにまったく異なるルールを持つポーランド語やアラビア語のような言語では、完全に破綻しがちです。 私たちが支援している企業でも、このようにハードコードされた UI をリファクタリングする負担の大きさから、 国際化への対応を後回しにしてしまうことがよくあります。


英語の複数形の扱い方

英語では、アプリ内で適切な複数形を使うのは通常それほど難しくありません。

単純な名詞の複数形については、ユーティリティ関数を作成します。

export function pluralize(
  count: number,
  singular: string,
  plural: string = singular + 's'
) {
  return `${count === 1 ? singular : plural}`;
}

これで、複数形の処理はすべて1つの関数で扱えるようになり、 不規則な複数形にも対応できます:

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 ライブラリが JavaScript 組み込みの Intl.PluralRules API を使って、各言語に応じた適切な複数形を判定しています。

React 向けの i18n ライブラリは数多くあり、その中には私たちの gt-react も含まれます (Next.js を使っている場合は gt-next です) 。 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>
  )
}

UI は、n の値に応じて条件付きでレンダリングされます。

ほとんどのライブラリでは、どの複数形を表示するかの判定に JavaScript の Intl.PluralRules API を使用します。 つまり英語では、単数形には "one"、複数形には "other" という名前を使います。 n に渡された数値が指定された props のどれにも一致しない場合、<Plural> コンポーネントは子要素にフォールバックします。

ここでライブラリを使うのは、英語のみのアプリケーションであってもベストプラクティスであり、将来的な国際化もはるかに容易になります。


多言語対応のReactアプリ (i18n) における複数形処理

アプリの提供先が英語話者だけであれば、上記の方法だけで十分かもしれません。しかし、実運用のアプリの多くは、いずれ複数言語への対応が必要になります。そこで事情が少し複雑になります。

  • アラビア語では、0、1、2、またはそれ以上のどれを指すかによって名詞の形が変わります
  • スペイン語、ドイツ語、イタリア語では、大きな数値にカンマではなくピリオドを使うため、1,000,000 は 1.000.000 になります
  • ヒンディー語では、数字は 2 桁ごとに区切られるため、1,000,000 は 10,00,000 になります

国際化対応のアプリでは、専用の i18n ライブラリを使うべきです。複数形の扱いや数値の書式設定の方法については、そのライブラリ固有のドキュメントを参照してください。

さまざまなロケール向けに数値をフォーマットする

Intl オブジェクトを使えば、任意のロケール向けに数値をフォーマットできます。 これを行う最も簡単な方法は、組み込みの toLocaleString() メソッドを使うことです。

デフォルトでは、runtime の現在のロケールが使用されます。

const n = 1000000
n.toLocaleString() // runtimeのロケールが "en-US"(アメリカ英語)の場合、1,000,000 と表示される
n.toLocaleString('de') // ロケールが "de"(ドイツ語)に指定されているため、1.000.000 と表示される

gt-react は、内部で Intl.NumberFormat を利用する <Num> コンポーネントも提供しています (実装の詳細はこちら) 。

import { Num } from 'gt-react'

// 言語が "en-US" の場合は 1,000,000 と表示される
// 言語が "de" の場合は 1.000.000 と表示される
// 言語が "hi" の場合は 10,00,000 と表示される
export default function Example() {
  return <Num>1000000</Num>
}

言語ごとの複数形を理解する

JavaScript の Intl.PluralRules API がサポートする複数形は、zeroonetwofewmanyother の 6 種類です。 英語で使われるのは one (「単数形」) と other (「複数形」) だけですが、 アラビア語やポーランド語のように、この 2 つ以外の形式も持つ言語があります。

たとえば、英語話者のユーザーは次のような表現を期待するかもしれません。

"No one is watching"
"1 person is watching"
"2 people are watching"

アラビア語話者のユーザーは、単数形、 双数形 (数がちょうど2つの場合) 、さらに少数を表す複数形と多数を表す複数形で異なる表現を期待するかもしれません。

"لا أحد يشاهد"
"1 شخص يشاهد"
"2 شخصان يشاهدان"
"3 أشخاص يشاهدون"
"11 شخصاً يشاهدون"

ここで国際化ライブラリが重要になります。 言語ごとに、複数形をいつどのように表示するかのルールが異なるため、 これを正しく扱うには専用ライブラリに任せるのが確実です。

優れた i18n ライブラリは、次の 2 つを行います。

  1. ロケールに基づいて、どの複数形 (onemanyother など) を使うかを判断する
  2. その形式に対応する、適切な言語の翻訳を取得する

すでに国際化ライブラリを使っている場合は、 複数形のフォーマットについてドキュメントを確認してください。 ほとんどのライブラリには、複数形の表示に関する専用ドキュメントがあります。

完全な例: 多言語 React アプリでの複数形

まだ国際化ライブラリを導入していない場合は、gt-react の利用を検討してください。

gt-react の <Plural> コンポーネント:

  • React で複数形を正しく表示するための、シンプルで宣言的な方法です
  • <Num> 書式設定コンポーネントと自然に連携します
  • <T> 翻訳コンポーネントとも自然に連携し、当社の無料翻訳サービスと組み合わせることで複数形を自動生成できます

これらの要素をすべて組み合わせると、次のような完全な多言語コンポーネントになります:

import { T, Plural, Num } from 'gt-react'

// 100以上の言語ですぐに使えます
function Example({ count }) {
  return (
    <T>
      <Plural
        n={count}
        zero={'No one is watching'}
        one={
          <>
            <Num>{count}</Num> person is watching
          </>
        }
      >
        <Num>{count}</Num> people are watching
      </Plural>
    </T>
  )
}

次のステップ

React アプリで複数形を適切に扱う準備ができたら、以下のクイックスタートガイドを参照してください。

複数形処理は、React における i18n でも特によくある課題の 1 つです。最初から正しく実装しておけば、後から大がかりなリファクタリングを避けられます。