如何在 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> 组件使用了 formatNumberIntl.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. 使用特定选项格式化数字

通过传递格式化选项来控制数字的显示方式。常见的选项包括 minimumFractionDigitsmaximumFractionDigitsstyle

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 对于设置 titlearia-labelalt 等文本属性至关重要,这些属性无法渲染 React 组件。