如何在 Next.js(Pages Router)v16 中格式化货币金额

显示带有货币符号和分隔符的价格

问题

显示货币值需要协调两个不同的本地化问题:货币本身和用户所在地区的数字格式约定。对于美国英语用户,1200.50 的价格必须显示为 "$1,200.50",而对于德国用户,则应显示为 "1 200,50 €"。货币符号的位置、小数点分隔符、分组分隔符和间距因地区而异。当这些元素未对齐或被硬编码时,用户会看到不熟悉的格式,从而对显示的金额是否正确产生怀疑,削弱了对价格信息的信任。

除了视觉一致性之外,不正确的货币格式还可能引起实际的混淆。习惯于将逗号作为千位分隔符的用户可能会将 "1.200" 误读为一小数点二,而不是一千二百。同样,错误放置的货币符号可能会使价格看起来不专业,或者完全暗示错误的货币。正确的货币格式既尊重货币代码,也尊重用户特定地区的数字约定,确保价格清晰可信。

解决方案

通过结合 ISO 4217 货币代码和用户的活动地区来格式化货币值,以生成符合地区规范的数字格式。此方法将符号位置、分隔符选择和间距规则委托给国际化库,由其为每个地区-货币组合应用正确的约定。结果是价格显示符合用户预期,无需手动字符串操作或应用代码中的特定地区逻辑。

步骤

1. 创建一个可复用的货币格式化组件

构建一个组件,该组件接受一个数值和货币代码,然后使用 react-intl 的上下文中的当前地区格式化价格。

import { FormattedNumber } from "react-intl";

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

export default function Price({ value, currency }: PriceProps) {
  return <FormattedNumber value={value} style="currency" currency={currency} />;
}

FormattedNumber 组件从最近的 IntlProvider 中读取地区,并自动应用货币符号和地区特定的数字格式规则。

2. 在页面中使用带有动态定价数据的组件

通过将数值金额和货币代码传递给组件来渲染价格,使格式能够适应用户的语言环境。

import { GetServerSideProps } from "next";
import Price from "../components/Price";

interface Product {
  id: string;
  name: string;
  price: number;
  currency: string;
}

interface ProductPageProps {
  product: Product;
}

export default function ProductPage({ product }: ProductPageProps) {
  return (
    <div>
      <h1>{product.name}</h1>
      <p>
        <Price value={product.price} currency={product.currency} />
      </p>
    </div>
  );
}

export const getServerSideProps: GetServerSideProps = async () => {
  const product = {
    id: "1",
    name: "Wireless Headphones",
    price: 1299.99,
    currency: "USD",
  };

  return {
    props: {
      product,
    },
  };
};

该页面获取包括货币代码在内的产品数据,并将其传递给 Price 组件,该组件根据活动语言环境格式化金额。

3. 创建一个用于命令式货币格式化的辅助工具

对于无法使用 React 组件的场景,例如设置属性或为非 JSX 上下文准备数据,可以使用 useIntl 钩子公开一个格式化函数。

import { useIntl } from "react-intl";

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

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

此钩子返回一个函数,用于将货币值格式化为字符串,适用于 aria 标签、meta 标签或其他仅限文本的上下文中,无法使用组件的场景。

4. 在属性上下文中应用格式化器

使用命令式格式化器填充需要纯文本而非 React 元素的 HTML 属性。

import { useCurrencyFormatter } from "../hooks/useCurrencyFormatter";

interface ProductCardProps {
  name: string;
  price: number;
  currency: string;
  imageUrl: string;
}

export default function ProductCard({
  name,
  price,
  currency,
  imageUrl,
}: ProductCardProps) {
  const formatCurrency = useCurrencyFormatter();
  const priceLabel = formatCurrency(price, currency);

  return (
    <article aria-label={`${name}, ${priceLabel}`}>
      <img src={imageUrl} alt={name} />
      <h2>{name}</h2>
      <p>{priceLabel}</p>
    </article>
  );
}

格式化器生成一个本地化的货币字符串,可以嵌入到 aria-label 属性中,确保辅助技术以用户语言环境的正确格式播报价格。