如何在 Next.js(Pages Router)v16 中为不同的语言环境格式化数字
使用特定语言环境的分隔符显示数字
问题
世界各地的数字书写方式各不相同。在美国,10,000.5 表示一万点五,而在德国则写作 10.000,5——逗号和句号的作用完全对调。这不仅仅是偏好或风格的问题,而是可读性的问题。一位德国用户看到 10,000.5 可能会将其读作十,忽略分组分隔符。而一位美国用户看到 10.000,5 可能会将其读作一万,忽略小数点分隔符。同样的数字,却有相反的含义。
当应用程序在显示数字时未使用与区域相关的格式化时,可能会让用户感到困惑或传递错误的信息。货币金额、百分比、测量值和统计数据都依赖于用户从小就习惯的区域性约定。
解决方案
根据用户的区域设置格式化数字,使用区域规则来处理小数点和分组分隔符。这将数字值转换为符合用户所在地区熟悉的格式规则的字符串。
react-intl 提供了两种方法:在 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 钩子提供对 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={`图表显示 ${formattedLabel} 项目`}
title={formattedLabel}
/>
</div>
);
}
命令式 API 对于设置 title、aria-label 或 alt 等文本属性至关重要,这些属性无法渲染 React 组件。