如何在 React Router v7 中为不同的语言环境格式化数字

使用特定语言环境的分隔符显示数字

问题

世界各地的数字书写方式各不相同。在美国,10,000.5 表示一万点五,而在德国则写作 10.000,5——逗号和句号的作用完全对调。这不仅仅是偏好或风格的问题,而是可读性的问题。一位德国用户看到 10,000.5 可能会将其读作十,忽略分组分隔符。而一位美国用户看到 10.000,5 可能会将其读作一万,忽略小数点分隔符。

相同的数字根据读者的地区习惯可能传达完全相反的含义。当应用程序显示未经本地化格式化的原始数字值时,可能会让用户感到困惑,从而削弱对所呈现数据的信任。

解决方案

根据用户的地区格式化数字,使用区域规则来处理小数点和分组分隔符。这将数字值转换为符合用户所在地区格式规则的字符串。

React-intl 提供了利用浏览器内置的 Intl.NumberFormat API 的格式化方法,该 API 处理区域数字习惯的复杂性。通过将数字值传递给这些格式化器,应用程序可以生成符合用户期望的输出,而无需手动处理分隔符逻辑。

步骤

1. 创建一个使用 useIntl 格式化数字的组件

useIntl 钩子提供了访问格式化方法的能力,包括 formatNumber,它接受一个数字值并返回一个本地化格式的字符串。

import { useIntl } from "react-intl";

export default function ProductPrice() {
  const intl = useIntl();
  const price = 1234.56;

  return (
    <div>
      <p>价格: {intl.formatNumber(price)}</p>
    </div>
  );
}

formatNumber 方法会自动为当前地区应用正确的分隔符。一位使用 en-US 地区的用户会看到 "1,234.56",而一位使用 de-DE 地区的用户会看到 "1.234,56"。

2. 使用样式选项格式化货币值

formatNumber 方法接受符合 Intl.NumberFormatOptions 的选项,包括货币格式化。

import { useIntl } from "react-intl";

export default function ProductPrice() {
  const intl = useIntl();
  const price = 1234.56;

  return (
    <div>
      <p>
        {intl.formatNumber(price, {
          style: "currency",
          currency: "USD",
        })}
      </p>
    </div>
  );
}

对于 en-US,这将生成 "$1,234.56";对于 de-DE,则生成 "1.234,56 $",应用了分隔符和货币符号的约定。

3. 使用 FormattedNumber 进行声明式格式化

FormattedNumber 组件提供了一种声明式的替代方法,它接受与属性相同的选项。

import { FormattedNumber } from "react-intl";

export default function Statistics() {
  const totalUsers = 1500000;
  const growthRate = 0.23;

  return (
    <div>
      <p>
        总用户数:<FormattedNumber value={totalUsers} />
      </p>
      <p>
        增长率:<FormattedNumber value={growthRate} style="percent" />
      </p>
    </div>
  );
}

该组件直接将格式化后的数字渲染到 DOM 中。对于 en-US,显示为 "1,500,000" 和 "23%";对于 de-DE,显示为 "1.500.000" 和 "23 %"。

4. 从加载器数据中格式化数字

在 React Router 7 中,路由组件通过 useLoaderData 钩子访问加载器数据。结合数字格式化,可以显示服务器提供的值。

import { useLoaderData } from "react-router";
import { FormattedNumber } from "react-intl";

export async function loader() {
  return {
    revenue: 45678.9,
    units: 12500,
  };
}

export default function Dashboard() {
  const { revenue, units } = useLoaderData<typeof loader>();

  return (
    <div>
      <h1>仪表盘</h1>
      <p>
        收入:<FormattedNumber value={revenue} style="currency" currency="USD" />
      </p>
      <p>
        销售单位:<FormattedNumber value={units} />
      </p>
    </div>
  );
}

加载器提供原始的数字数据,组件根据用户的语言环境对其进行格式化。这种分离使数据获取与展示逻辑保持独立。