如何在 TanStack Start v1 中为不同地区格式化数字

使用符合地区习惯的分隔符显示数字

问题

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

如果应用程序在显示数字时没有考虑地区格式规范,就会造成混淆,甚至影响用户信任。用户期望数字遵循他们习惯的模式,偏离这些模式会让他们停下来重新理解,甚至怀疑数据是否正确。

解决方案

根据用户的地区设置格式化数字,使用本地的千位分隔符和小数点规则。这样可以将数值转换为用户熟悉的格式字符串,提升可读性和信任感。

React-intl 提供了组件和 hook,利用浏览器内置的 Intl.NumberFormat API 实现地区化格式化。只需传入数值和当前 locale,这些工具就会自动选择正确的分隔符、分组方式和其他地区规范。最终生成的字符串无需手动处理或编写地区相关逻辑,就能符合用户的预期。

步骤

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 组件会自动应用当前 locale 的小数点和分组分隔符。它会从组件树中最近的 IntlProvider 读取 locale,并据此格式化数字。

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>
        Revenue:{" "}
        <FormattedNumber value={revenue} style="currency" currency="USD" />
      </p>
      <p>
        Growth: <FormattedNumber value={growthRate} style="percent" />
      </p>
      <p>
        Size:{" "}
        <FormattedNumber
          value={fileSize}
          style="unit"
          unit="megabyte"
          unitDisplay="short"
        />
      </p>
    </div>
  );
}

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

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

在组件不适用的场景(如生成动态属性或为非 React API 准备数据)下,可以使用 useIntl hook 进行数字格式化。

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 传递参数,可以控制精度、分组和记数法,无论是用于 FormattedNumber 还是 formatNumber

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” 的缩写形式,并遵循 locale 的缩写规则。