Next.js(ページルーター)v16で異なるロケールの数値をフォーマットする方法
ロケール固有の区切り記号で数値を表示する
問題
数字は世界中で異なる方法で表記されています。アメリカ合衆国では10,000.5と表示される数字が、ドイツでは10.000,5となり、カンマとピリオドが完全に入れ替わります。これは好みやスタイルの問題ではなく、可読性の問題です。ドイツのユーザーが10,000.5を見ると、桁区切り記号を無視して「10」と読む可能性があります。アメリカのユーザーが10.000,5を見ると、小数点を無視して「1万」と読む可能性があります。同じ数字でも、正反対の意味になるのです。
アプリケーションがロケールを考慮せずに数字を表示すると、ユーザーを混乱させたり、誤った情報を伝えたりするリスクがあります。通貨金額、パーセンテージ、測定値、統計データはすべて、ユーザーが幼少期から学んできた地域の慣習に依存しています。
解決策
ユーザーのロケールに基づいて数字をフォーマットし、小数点や桁区切り記号に地域のルールを使用します。これにより、数値がユーザーの地域で馴染みのあるフォーマットルールに従った文字列に変換されます。
react-intlは2つのアプローチを提供しています:JSXで宣言的にフォーマットするための<FormattedNumber>コンポーネントと、命令的にフォーマットするためのuseIntlフックからのformatNumberメソッドです。どちらもブラウザのIntl.NumberFormat APIに依存し、IntlProviderで設定されたロケールを自動的に適用します。フォーマットされた出力は、千の位区切り記号、小数点、数字のグループ化に関する地域の慣習を尊重します。
ステップ
1. 宣言的に数字をフォーマットするコンポーネントを作成する
<FormattedNumber>コンポーネントはformatNumberおよびIntl.NumberFormat APIを使用し、valueプロップとIntl.NumberFormatOptionsに対応するオプションを受け入れます。
import { FormattedNumber } from "react-intl";
export default function ProductPrice({ price }: { price: number }) {
return (
<div>
<FormattedNumber value={price} />
</div>
);
}
デフォルトでは、<FormattedNumber>はフォーマットされた数字をReact.Fragmentにレンダリングします。このコンポーネントは最も近いIntlProviderからロケールを読み取り、適切なフォーマットルールを適用します。
2. 特定のオプションで数値をフォーマットする
フォーマットオプションを渡して、数値の表示方法を制御します。一般的なオプションには minimumFractionDigits、maximumFractionDigits、style などがあります。
import { FormattedNumber } from "react-intl";
export default function Statistics({ value }: { value: number }) {
return (
<dl>
<dt>総ユーザー数</dt>
<dd>
<FormattedNumber
value={value}
minimumFractionDigits={0}
maximumFractionDigits={0}
/>
</dd>
<dt>コンバージョン率</dt>
<dd>
<FormattedNumber
value={value / 100}
style="percent"
minimumFractionDigits={2}
/>
</dd>
</dl>
);
}
これらのプロパティは Intl.NumberFormatOptions に対応しており、ロケール固有の区切り文字を維持しながら、精度と表示方法を制御できます。
3. useIntl フックを使用して命令的に数値をフォーマットする
コンポーネントが関数コンポーネントとして表現できる場合、useIntl フックは formatNumber メソッドを含む intl オブジェクトへのアクセスを提供します。
import { useIntl } from "react-intl";
export default function DataTable({ rows }: { rows: number[] }) {
const intl = useIntl();
return (
<table>
<tbody>
{rows.map((value, index) => (
<tr key={index}>
<td>{intl.formatNumber(value)}</td>
</tr>
))}
</tbody>
</table>
);
}
formatNumber 関数はフォーマットされた数値文字列を返し、数値として解析できる値と NumberFormatOptions に準拠するオプションを受け取ります。
4. JSX以外のコンテキストで数値をフォーマットする
属性、変数、またはコンポーネントを使用できないその他のコンテキストでフォーマットされた数値が必要な場合は、formatNumber を使用します。
import { useIntl } from "react-intl";
export default function Chart({ dataPoints }: { dataPoints: number[] }) {
const intl = useIntl();
const formattedLabel = intl.formatNumber(dataPoints[0], {
notation: "compact",
maximumFractionDigits: 1,
});
return (
<div>
<img
src="/chart.png"
alt={`${formattedLabel}項目を表示するチャート`}
title={formattedLabel}
/>
</div>
);
}
命令的APIは、Reactコンポーネントをレンダリングできない title、aria-label、alt などのテキスト属性を設定する際に不可欠です。