如何在 TanStack Start v1 中为不同的语言环境格式化数字

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

问题

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

当应用程序在显示数字时未考虑区域格式化规则时,会引发混淆并削弱用户的信任。用户期望数字遵循他们所学的模式,偏离这些模式会迫使他们停下来重新解释,并质疑数据的正确性。

解决方案

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

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>
        收入:{" "}
        <FormattedNumber value={revenue} style="currency" currency="USD" />
      </p>
      <p>
        增长率:<FormattedNumber value={growthRate} style="percent" />
      </p>
      <p>
        大小:{" "}
        <FormattedNumber
          value={fileSize}
          style="unit"
          unit="megabyte"
          unitDisplay="short"
        />
      </p>
    </div>
  );
}

style 属性控制格式化模式。货币格式化需要提供 currency 代码,百分比格式化会自动乘以 100,而单位格式化需要提供 unit 标识符。所有样式都会遵循当前活动语言环境的约定。

3. 使用 useIntl 命令式格式化数字

在组件不适用的上下文中,例如生成动态属性或为非 React API 准备数据,可以使用 useIntl 钩子来格式化数字。

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. 应用自定义格式选项

通过将 Intl.NumberFormatOptions 传递给 FormattedNumberformatNumber,可以控制精度、分组和表示法。

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>
  );
}

选项如 notationminimumFractionDigitsmaximumFractionDigits 控制数字的显示方式。紧凑表示法会将大数字转换为缩写形式,例如 "1.2M",同时遵循语言环境特定的缩写规则。