Как форматировать суммы в Next.js (Pages Router) v16

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

Проблема

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

Помимо визуальной согласованности, неправильное форматирование валюты может реально запутать пользователя. Тот, кто привык к запятой как к разделителю тысяч, может прочитать "1.200" как один и две десятых, а не как тысячу двести. Аналогично, неверное расположение символа валюты делает цену неаккуратной или даже указывает на другую валюту. Корректное форматирование учитывает и код валюты, и числовые правила локали пользователя, чтобы цены были сразу понятны и вызывали доверие.

Решение

Форматируйте суммы, комбинируя код валюты по ISO 4217 с активной локалью пользователя — так вы получите правильное отображение чисел для нужного региона. Такой подход передаёт расстановку символов, выбор разделителей и пробелов на обработку библиотеке интернационализации, которая применяет нужные правила для каждой пары локаль-валюта. В результате цена отображается так, как ожидает пользователь, без ручной обработки строк или сложной логики в коде приложения.

Шаги

1. Создайте переиспользуемый компонент для форматирования валюты

Сделайте компонент, который принимает числовое значение и код валюты, а затем форматирует цену, используя текущую локаль из контекста react-intl.

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 и автоматически применяет символ валюты и правила форматирования чисел для этой локали.

2. Используйте компонент на странице с динамическими ценами

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

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, который форматирует сумму в соответствии с активной локалью.

3. Создайте помощник для императивного форматирования валюты

В ситуациях, когда нельзя использовать React-компонент, например, при установке атрибутов или подготовке данных для не-JSX-контекста, используйте функцию форматирования через хук useIntl.

import { useIntl } from "react-intl";

export function useCurrencyFormatter() {
  const intl = useIntl();

  return (value: number, currency: string): string => {
    return intl.formatNumber(value, {
      style: "currency",
      currency,
    });
  };
}

Этот хук возвращает функцию, которая форматирует значения валюты в строку — удобно для aria-меток, meta-тегов или других текстовых контекстов, где компоненты не подходят.

4. Применяйте форматтер в атрибутах

Используйте императивный форматтер для заполнения HTML-атрибутов, которым нужен только текст, а не React-элементы.

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, чтобы вспомогательные технологии озвучивали цены в правильном формате для локали пользователя.