如何在 React Router v7 中显示货币信息

显示货币代码、名称和符号

问题

应用程序经常需要显示货币信息,但不一定要展示格式化的价格。例如,货币选择器可能会显示 "USD" 或 "US Dollar",而金融仪表盘可能只显示 "$" 符号。每种格式都有不同的用途,但它们都面临一些共同的挑战。ISO 代码 "USD" 精确,但对非技术用户来说不够直观;全称 "US Dollar" 清晰明了,但需要为国际用户进行翻译;符号 "$" 简洁,但容易产生歧义——它可能代表美元、加元或澳元,具体取决于上下文。选择错误的格式会让用户困惑,降低金融界面的可信度。

在构建多语言应用时,这一挑战会更加突出。英文下适用的货币名称,直接翻译到其他语言可能并不合适,而且不同地区对符号的使用习惯也不一样。如果将这些值硬编码,会增加维护成本,并限制应用的国际化能力。

解决方案

使用 react-intl 的 formatDisplayName 方法来显示本地化的货币名称和代码,并利用浏览器的 Intl.NumberFormat API 提取货币符号。formatDisplayName 方法接收货币代码,根据用户的语言环境自动返回合适的本地化名称,实现自动翻译。对于符号,可以用该货币格式化一个示例数字,然后从结果中提取符号部分。

为每种展示格式(全称、符号或代码)创建专用的辅助函数或组件,这样可以根据不同场景选择合适的展示方式。这种做法有助于将货币展示逻辑集中管理,确保应用内的一致性,同时尊重用户的本地化偏好。

步骤

1. 创建一个辅助函数以显示本地化货币名称

使用 formatDisplayName 方法结合 type: 'currency',将 ISO 货币代码转换为本地化的全称。

import { useIntl } from "react-intl";

export function CurrencyName({ code }: { code: string }) {
  const intl = useIntl();
  const name = intl.formatDisplayName(code, { type: "currency" });
  return <span>{name}</span>;
}

当 locale 为 "en" 且代码为 "CNY" 时,将显示 "Chinese Yuan"。该方法会根据 IntlProvider 提供的当前 locale 自动翻译名称。

2. 创建一个辅助函数以提取和显示货币符号

使用 Intl.NumberFormat 结合 formatToParts,通过筛选 type === "currency" 的部分来提取货币符号。

function getCurrencySymbol(locale: string, currency: string): string {
  const parts = new Intl.NumberFormat(locale, {
    style: "currency",
    currency,
    currencyDisplay: "narrowSymbol",
  }).formatToParts(0);

  const currencyPart = parts.find((part) => part.type === "currency");
  return currencyPart?.value || currency;
}

export function CurrencySymbol({ code }: { code: string }) {
  const intl = useIntl();
  const symbol = getCurrencySymbol(intl.locale, code);
  return <span>{symbol}</span>;
}

currencyDisplay 选项用于控制货币的显示形式,其中 "narrowSymbol" 提供最紧凑的表示方式。此方法会自动处理符号在不同 locale 下的位置和格式。

3. 创建一个支持多种显示选项的组件

构建一个灵活的组件,接受 display mode 属性,在代码、符号和名称格式之间切换。

import { useIntl } from "react-intl";

type CurrencyDisplayMode = "code" | "symbol" | "name";

interface CurrencyInfoProps {
  code: string;
  display?: CurrencyDisplayMode;
}

export function CurrencyInfo({ code, display = "symbol" }: CurrencyInfoProps) {
  const intl = useIntl();

  if (display === "name") {
    const name = intl.formatDisplayName(code, { type: "currency" });
    return <span>{name}</span>;
  }

  if (display === "symbol") {
    const parts = new Intl.NumberFormat(intl.locale, {
      style: "currency",
      currency: code,
      currencyDisplay: "narrowSymbol",
    }).formatToParts(0);
    const symbol =
      parts.find((part) => part.type === "currency")?.value || code;
    return <span>{symbol}</span>;
  }

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

该组件集中管理货币显示逻辑,便于根据 UI 场景切换显示格式。对于需要清晰度的货币选择器,使用 display="name";对于表头等紧凑显示,使用 display="symbol";对于技术或财务报告,使用 display="code"

4. 在不同场景中使用该组件

根据货币信息在界面中的位置,选择合适的显示模式。

export default function CurrencyExample() {
  return (
    <div>
      <label htmlFor="currency-select">
        Select currency: <CurrencyInfo code="EUR" display="name" />
      </label>

      <table>
        <thead>
          <tr>
            <th>
              Amount (<CurrencyInfo code="USD" display="symbol" />)
            </th>
          </tr>
        </thead>
      </table>

      <p>
        Transaction currency: <CurrencyInfo code="GBP" display="code" />
      </p>
    </div>
  );
}

每个场景都采用最适合其用途的格式:选择界面用全称,紧凑显示用符号,技术引用用代码。该组件会根据 IntlProvider 提供的用户 locale 自动处理本地化。