如何在 TanStack Start v1 中格式化货币金额

使用货币符号和分隔符显示价格

问题

价格的本地化涉及两个挑战:货币表示和数字格式化。同样的金额在美国显示为 $1,200.50,在德国则为 1 200,50 €。货币符号的位置、千位分隔符和小数点符号都会变化,甚至空格的用法也不同。

这些规范不是个人偏好,而是用户的预期。如果价格格式不符合本地习惯,用户会怀疑金额是否正确。例如,德国用户看到“1200.50 EUR”,或美国用户看到“1.200,50$”,都会产生困惑,降低信任感。

解决方案

应根据货币类型和用户的语言环境格式化金额。这样可以结合货币符号的位置和本地数字格式规则,确保价格显示符合各地区用户对货币的认知习惯。

使用 react-intl 的 formatNumber 方法,并结合 style: 'currency' 选项,可以同时应用货币代码和用户本地的格式化规则。浏览器的 Intl.NumberFormat API 会自动处理符号位置、分隔符和空格。

步骤

1. 创建货币格式化组件

构建一个可复用组件,接收数值和货币代码,并根据用户的语言环境格式化价格。

import { useIntl } from "react-intl";

interface PriceProps {
  value: number;
  currency: string;
}

export function Price({ value, currency }: PriceProps) {
  const intl = useIntl();

  const formattedPrice = intl.formatNumber(value, {
    style: "currency",
    currency: currency,
  });

  return <span>{formattedPrice}</span>;
}

useIntl hook 提供了格式化 API 的访问能力。结合 formatNumber 方法和 style: 'currency',可以同时应用货币符号和本地数字格式。

2. 在不同货币下使用该组件

在应用中传入数值和货币代码,即可在各处显示价格。

export default function ProductCard() {
  return (
    <div>
      <h2>Premium Plan</h2>
      <Price value={1200.5} currency="USD" />
    </div>
  );
}

该组件会根据用户的本地化设置自动格式化价格。美国用户会看到 "$1,200.50",而德国用户会看到 "1.200,50 $"。

3. 使用自定义小数精度格式化价格

通过添加 minimumFractionDigitsmaximumFractionDigits 选项,可以控制显示的小数位数。

export function PriceWithPrecision({ value, currency }: PriceProps) {
  const intl = useIntl();

  const formattedPrice = intl.formatNumber(value, {
    style: "currency",
    currency: currency,
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  });

  return <span>{formattedPrice}</span>;
}

这样可以确保所有价格的小数显示保持一致,即使数值为 100 这样的整数也不例外。

4. 创建内联格式化辅助函数

在不需要组件包装器的情况下,可以创建一个格式化辅助函数。

import { useIntl } from "react-intl";

export function useFormatCurrency() {
  const intl = useIntl();

  return (value: number, currency: string) => {
    return intl.formatNumber(value, {
      style: "currency",
      currency: currency,
    });
  };
}

当你需要在属性、计算值或其他非 JSX 场景下获取格式化价格时,可以使用这个 hook。

export function CheckoutSummary({ total }: { total: number }) {
  const formatCurrency = useFormatCurrency();

  return (
    <button aria-label={`Pay ${formatCurrency(total, "USD")}`}>Checkout</button>
  );
}

该辅助函数返回一个普通字符串,适用于无法使用 JSX 组件的任何场景。