如何在 TanStack Start v1 中显示货币信息

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

问题

应用程序通常需要显示货币信息,但不一定需要显示格式化的价格。例如,货币选择器可能列出可用货币,关于汇率的文章可能通过符号引用特定货币,或者金融仪表板可能显示货币代码。然而,每种表示方式都有其局限性。像 "USD" 这样的 ISO 代码虽然精确,但对非技术用户来说晦涩难懂。像 "US Dollar" 这样的全名清晰明了,但需要为国际用户进行翻译。像 "$" 这样的符号虽然熟悉,但却容易引起歧义——同一个符号可以代表美国、加拿大和澳大利亚的美元。在上下文中选择错误的格式会降低清晰度,并可能让用户感到困惑。

本地化使这一挑战更加复杂。在英语中适用的货币名称在其他语言中可能不存在或不正确。在一个地区标准的符号在另一个地区可能不为人所知。如果没有一致的、面向区域的解决方案,应用程序要么将货币表示硬编码为单一语言,要么维护庞大的翻译表,而这些表很快就会过时。

解决方案

使用浏览器内置的国际化 API,以符合用户区域设置和上下文的格式显示货币信息。Intl.DisplayNames API 提供本地化的完整货币名称,而通过 Intl.NumberFormatcurrencyDisplay 选项可以提取货币符号或代码。根据上下文选择合适的 API 和格式选项,可以确保货币引用既清晰又正确地本地化,而无需维护翻译数据。

创建小型、可重用的组件或辅助函数,这些函数接受货币代码和区域设置,然后返回适当的表示形式。这种方法将货币显示逻辑集中化,使得在应用程序中以不同格式显示相同货币变得简单易行。

步骤

1. 创建一个获取本地化货币名称的辅助函数

Intl.DisplayNames API 使用 type: "currency" 可以返回任何 ISO 4217 货币代码的本地化完整货币名称。

export function getCurrencyName(currencyCode: string, locale: string): string {
  const displayNames = new Intl.DisplayNames([locale], { type: "currency" });
  return displayNames.of(currencyCode) || currencyCode;
}

此辅助函数接受一个三字母的 ISO 货币代码,并以用户的语言返回其全名。可用于货币选择器或需要清晰说明的文本中,优先考虑清晰性而非简洁性。

2. 创建一个提取货币符号的辅助函数

Intl.NumberFormat 中的 currencyDisplay 选项控制货币的显示方式,其值可以是 "code"、"symbol"、"narrowSymbol" 和 "name"。

export function getCurrencySymbol(
  currencyCode: string,
  locale: string,
  narrow: boolean = false,
): string {
  const formatter = new Intl.NumberFormat(locale, {
    style: "currency",
    currency: currencyCode,
    currencyDisplay: narrow ? "narrowSymbol" : "symbol",
  });

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

此辅助函数格式化一个零值并仅提取货币符号。narrow 参数控制是否使用紧凑符号(如 "$")或区分符号(如 "US$")。在表格或图表等紧凑型 UI 元素中使用符号。

3. 创建一个显示货币信息的组件

构建一个灵活的组件,可以根据 display 属性以任何格式渲染货币信息。

interface CurrencyDisplayProps {
  code: string;
  locale: string;
  display: "name" | "symbol" | "narrowSymbol" | "code";
}

export function CurrencyDisplay({
  code,
  locale,
  display,
}: CurrencyDisplayProps) {
  let content: string;

  if (display === "name") {
    content = getCurrencyName(code, locale);
  } else if (display === "code") {
    content = code.toUpperCase();
  } else {
    content = getCurrencySymbol(code, locale, display === "narrowSymbol");
  }

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

此组件集中管理货币显示逻辑,并使格式切换变得简单。对于下拉菜单和说明性文本,使用 "name";对于紧凑型显示,使用 "symbol""narrowSymbol";当需要精确性时,使用 "code"

4. 在 TanStack Start 路由中使用组件

在任意路由组件中导入并使用该组件,从您的 i18n 上下文或路由加载器中传递用户的语言环境。

import { createFileRoute } from "@tanstack/react-router";
import { CurrencyDisplay } from "~/components/CurrencyDisplay";

export const Route = createFileRoute("/currencies")({
  component: CurrenciesPage,
});

function CurrenciesPage() {
  const locale = "en-US";

  return (
    <div>
      <h1>货币信息</h1>
      <p>
        全名:<CurrencyDisplay code="USD" locale={locale} display="name" />
      </p>
      <p>
        符号:<CurrencyDisplay code="USD" locale={locale} display="symbol" />
      </p>
      <p>
        代码:<CurrencyDisplay code="USD" locale={locale} display="code" />
      </p>
    </div>
  );
}

该组件以不同格式渲染相同的货币。在实际应用中,从您的 i18n 提供程序或路由上下文中检索语言环境,以确保所有货币显示符合用户的语言偏好。