Next.js(Pages Router)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 propと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>Total Users</dt>
<dd>
<FormattedNumber
value={value}
minimumFractionDigits={0}
maximumFractionDigits={0}
/>
</dd>
<dt>Conversion Rate</dt>
<dd>
<FormattedNumber
value={value / 100}
style="percent"
minimumFractionDigits={2}
/>
</dd>
</dl>
);
}
これらのプロパティはIntl.NumberFormatOptionsに対応しており、ロケール固有の区切り文字を維持しながら、精度と表示を制御できます。
3. useIntlフックを使用して命令的に数値をフォーマットする
コンポーネントを関数コンポーネントとして表現できる場合、useIntlフックはintlオブジェクトへのアクセスを提供し、これにはformatNumberメソッドが含まれています。
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={`Chart showing ${formattedLabel} items`}
title={formattedLabel}
/>
</div>
);
}
命令的APIは、title、aria-label、altなどのテキスト属性を設定する際に不可欠です。これらの場所ではReactコンポーネントをレンダリングできません。