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>
Revenue:{" "}
<FormattedNumber value={revenue} style="currency" currency="USD" />
</p>
<p>
Growth: <FormattedNumber value={growthRate} style="percent" />
</p>
<p>
Size:{" "}
<FormattedNumber
value={fileSize}
style="unit"
unit="megabyte"
unitDisplay="short"
/>
</p>
</div>
);
}
styleプロパティは、フォーマットモードを制御します。通貨フォーマットにはcurrencyコードが必要で、パーセンテージフォーマットは自動的に100を乗算し、単位フォーマットにはunit識別子が必要です。すべてのスタイルは、アクティブなロケールの規則に従います。
3. useIntlを使用して命令的に数値をフォーマットする
useIntlフックを使用して、動的な属性の生成や非React APIのデータ準備など、コンポーネントが適さないコンテキストで数値をフォーマットします。
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」のような省略形に変換し、ロケール固有の省略形を尊重します。