Как форматировать суммы в валюте в 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>Премиум-план</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={`Оплатить ${formatCurrency(total, "USD")}`}>Оформить заказ</button>
  );
}

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