Как форматировать суммы в валюте в TanStack Start v1

Отображение цен с валютными символами и разделителями

Проблема

Цены — это сразу две задачи локализации: отображение валюты и форматирование чисел. Одна и та же сумма выглядит как $1,200.50 в США и 1 200,50 € в Германии. Местоположение символа валюты, разделители целых и дробных частей, а также пробелы — всё меняется.

Эти правила — не просто предпочтения, а ожидания пользователей. Если цена показана в непривычном формате, человек начинает сомневаться, правильно ли он понял сумму. Например, «1200.50 EUR» для пользователя из Германии или «1.200,50$» для пользователя из США вызывает недоверие и мешает восприятию.

Решение

Форматируйте денежные значения с учётом и валюты, и локали пользователя. Это сочетает правильное расположение символа валюты с локальными правилами форматирования чисел, чтобы цена выглядела так, как ожидают в конкретном регионе.

Используйте метод formatNumber из react-intl с опцией style: 'currency', чтобы применить и код валюты, и форматирование по локали пользователя. Браузерный API Intl.NumberFormat автоматически расставляет символы, выбирает разделители и пробелы.

Шаги

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 даёт доступ к 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. Форматируйте цены с нужным количеством знаков после запятой

Управляйте количеством отображаемых знаков после запятой с помощью опций minimumFractionDigits и maximumFractionDigits.

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 контекстах.

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

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

Хелпер возвращает обычную строку, подходящую для любого случая, где нельзя использовать JSX-компоненты.