如何在 Next.js(Pages Router)v16 中为不同地区格式化数字
使用本地化分隔符显示数字
问题
世界各地的数字书写方式各不相同。在美国,10,000.5 表示一万零零点五,而在德国则写作 10.000,5——逗号和句点的作用完全对调。这不仅仅是风格或偏好的问题,而是可读性的问题。德国用户看到 10,000.5 可能会把它读作十,忽略分组符号。美国用户看到 10.000,5 可能会把它读作一万,忽略小数点。相同的数字,不同的含义。
如果应用程序在显示数字时没有考虑本地化格式,用户可能会感到困惑,甚至获得错误的信息。货币金额、百分比、计量单位和统计数据都依赖于用户从小习惯的地区规范。
解决方案
根据用户的地区格式化数字,遵循本地的千位分隔符和小数点规则。这样可以将数值转换为用户熟悉的格式化字符串,提升可读性和准确性。
react-intl 提供了两种方式:在 JSX 中声明式格式化的 <FormattedNumber> 组件,以及通过 useIntl hook 获取的 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 读取 locale,并应用相应的格式化规则。
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>
);
}
这些 props 对应 Intl.NumberFormatOptions,让你可以控制精度和展示方式,同时保持本地化分隔符。
3. 使用 useIntl hook 以命令式格式化数字
当组件可以用函数组件表达时,useIntl hook 可访问 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 组件。