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