如何在 Next.js(Pages Router)v16 中格式化货币金额
以货币符号和分隔符显示价格
问题
显示货币金额时,需要同时考虑两方面的本地化问题:货币本身以及用户所在地区的数字格式规范。例如,1200.50 这个价格,对于美国英语用户应显示为 "$1,200.50",而对于德国用户则应为 "1 200,50 €"。货币符号的位置、小数点分隔符、千位分隔符和空格等细节都会因地区而异。如果这些元素设置不当或被硬编码,用户就会看到不熟悉的格式,从而对金额的正确性产生怀疑,影响对价格信息的信任。
除了视觉一致性之外,错误的货币格式还会引发实际的困惑。习惯用逗号作为千位分隔符的用户,可能会把 "1.200" 理解为 1.2 而不是 1200。同样,货币符号位置不当也会让价格看起来不专业,甚至误导为其他货币。正确的货币格式应同时遵循货币代码和用户所在地区的数字规范,确保价格信息一目了然、值得信赖。
解决方案
通过将 ISO 4217 货币代码与用户当前的 locale 结合,格式化货币金额,从而生成符合地区规范的数字格式。这样可以将符号位置、分隔符选择和空格规则交由国际化库处理,自动应用每个地区和货币的正确规范。最终,价格显示能够完全符合用户预期,无需手动拼接字符串或在应用代码中编写特定于地区的逻辑。
步骤
1. 创建可复用的货币格式化组件
构建一个组件,接收数值和货币代码,并利用 react-intl 的 context 获取当前 locale,自动格式化价格。
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 读取 locale,并自动应用货币符号和特定于 locale 的数字格式化规则。
2. 在页面中使用该组件处理动态定价数据
通过将数值金额和货币代码传递给组件来渲染价格,使格式能够根据用户的 locale 自动适配。
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 组件,由该组件根据当前 locale 格式化金额。
3. 创建用于命令式货币格式化的辅助函数
在无法使用 React 组件的场景下(如设置属性或为非 JSX 上下文准备数据),可通过 useIntl hook 暴露一个格式化函数。
import { useIntl } from "react-intl";
export function useCurrencyFormatter() {
const intl = useIntl();
return (value: number, currency: string): string => {
return intl.formatNumber(value, {
style: "currency",
currency,
});
};
}
该 hook 返回一个将货币值格式化为字符串的函数,适用于 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 属性中,确保辅助技术以用户 locale 的正确格式播报价格。