TanStack Start v1で異なるロケールの数値をフォーマットする方法
ロケール固有の区切り記号で数値を表示する
問題
数字は世界各地で異なる方法で表記されています。アメリカ合衆国では10,000.5と表示される数字が、ドイツでは10.000,5となり、カンマとピリオドが完全に入れ替わります。これは好みやスタイルの問題ではなく、可読性の問題です。ドイツのユーザーが10,000.5を見ると、桁区切り記号を無視して「10」と読む可能性があります。アメリカのユーザーが10.000,5を見ると、小数点を無視して「1万」と読む可能性があります。同じ数字でも、正反対の意味になるのです。
アプリケーションが地域の書式設定規則を考慮せずに数字を表示すると、混乱を招き、信頼を損ないます。ユーザーは数字が自分が学んだパターンに従うことを期待しており、そのパターンからの逸脱は、一時停止して再解釈し、データが正しいかどうかを疑問視させることになります。
解決策
ユーザーのロケールに基づいて数値をフォーマットし、小数点と桁区切り記号に地域のルールを使用します。これにより、数値が各地域のユーザーに馴染みのあるフォーマットルールに従った文字列に変換されます。
React-intlは、ブラウザの組み込みIntl.NumberFormat APIを活用するコンポーネントとフックを提供します。数値と現在のロケールを渡すことで、これらのツールは自動的に正しい区切り記号、数字のグループ化、その他の地域の慣習を選択します。結果として、コンポーネント内で手動の文字列操作やロケール固有のロジックを使わずに、ユーザーの期待に合ったフォーマットされた文字列が得られます。
ステップ
1. FormattedNumberを使用して数値表示コンポーネントを作成する
数値を受け取り、react-intlのFormattedNumberコンポーネントを使用してロケールに適したフォーマットでレンダリングするコンポーネントを構築します。
import { FormattedNumber } from "react-intl";
interface PriceDisplayProps {
value: number;
}
export function PriceDisplay({ value }: PriceDisplayProps) {
return (
<div>
<FormattedNumber value={value} />
</div>
);
}
FormattedNumberコンポーネントは、ロケールの小数点と桁区切り記号を自動的に適用します。コンポーネントツリー内の最も近いIntlProviderからロケールを読み取り、それに応じて数値をフォーマットします。
2. 特定のスタイルで数値をフォーマットする
通貨、パーセンテージ、単位のスタイルオプションをFormattedNumberに渡して、数値フォーマットをカスタマイズします。
import { FormattedNumber } from "react-intl";
interface MetricsProps {
revenue: number;
growthRate: number;
fileSize: number;
}
export function Metrics({ revenue, growthRate, fileSize }: MetricsProps) {
return (
<div>
<p>
収益:{" "}
<FormattedNumber value={revenue} style="currency" currency="USD" />
</p>
<p>
成長率: <FormattedNumber value={growthRate} style="percent" />
</p>
<p>
サイズ:{" "}
<FormattedNumber
value={fileSize}
style="unit"
unit="megabyte"
unitDisplay="short"
/>
</p>
</div>
);
}
styleプロパティはフォーマットモードを制御します。通貨フォーマットにはcurrencyコードが必要で、パーセンテージフォーマットは自動的に100を掛け、単位フォーマットにはunit識別子が必要です。すべてのスタイルはアクティブなロケールの規則に従います。
3. useIntlを使って命令的に数値をフォーマットする
コンポーネントが適さない状況、例えば動的な属性の生成やReact以外のAPIのデータ準備などでは、useIntlフックを使用して数値をフォーマットします。
import { useIntl } from "react-intl";
interface ChartTooltipProps {
dataPoint: number;
}
export function ChartTooltip({ dataPoint }: ChartTooltipProps) {
const intl = useIntl();
const formattedValue = intl.formatNumber(dataPoint, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
return <div title={formattedValue}>{formattedValue}</div>;
}
formatNumberメソッドはフォーマットされた文字列をすぐに返すため、属性、ariaラベル、またはReactエレメントではなく文字列としてフォーマットされた値が必要な任意のコンテキストに適しています。
4. カスタムフォーマットオプションを適用する
FormattedNumberまたはformatNumberにIntl.NumberFormatOptionsを渡すことで、精度、グループ化、表記法を制御できます。
import { FormattedNumber } from "react-intl";
interface StatisticProps {
value: number;
compact?: boolean;
}
export function Statistic({ value, compact }: StatisticProps) {
return (
<div>
<FormattedNumber
value={value}
notation={compact ? "compact" : "standard"}
minimumFractionDigits={0}
maximumFractionDigits={2}
/>
</div>
);
}
notation、minimumFractionDigits、maximumFractionDigitsなどのオプションは、数値の表示方法を制御します。コンパクト表記は大きな数値を「1.2M」のような省略形に変換し、ロケール固有の省略形を尊重します。