Как форматировать числа для разных локалей в Next.js (Pages Router) v16
Отображение чисел с разделителями, специфичными для локали
Проблема
Числа записываются по-разному в разных странах. То, что в Соединенных Штатах выглядит как 10,000.5, в Германии становится 10.000,5 — запятые и точки полностью меняются местами. Это не вопрос предпочтений или стиля, а вопрос читаемости. Немецкий пользователь, видя 10,000.5, может прочитать это как десять, игнорируя разделители групп. Американский пользователь, видя 10.000,5, может прочитать это как десять тысяч, игнорируя десятичный разделитель. Одни и те же цифры, противоположные значения.
Когда приложения отображают числа без учета локальных форматов, они рискуют запутать пользователей или передать неверную информацию. Суммы в валюте, проценты, измерения и статистика зависят от региональных стандартов, которые пользователи усваивают с детства.
Решение
Форматируйте числа в соответствии с локалью пользователя, используя региональные правила для десятичных и разделителей групп. Это преобразует числовые значения в строки, которые соответствуют правилам форматирования, знакомым пользователям в их регионе.
react-intl предоставляет два подхода: компонент <FormattedNumber> для декларативного форматирования в JSX и метод formatNumber из хука useIntl для императивного форматирования. Оба используют API браузера Intl.NumberFormat и автоматически применяют локаль, настроенную в вашем IntlProvider. Отформатированный вывод учитывает региональные стандарты для разделителей тысяч, десятичных точек и группировки цифр.
Шаги
1. Создайте компонент для декларативного форматирования чисел
Компонент <FormattedNumber> использует API formatNumber и Intl.NumberFormat и принимает проп value вместе с опциями, соответствующими Intl.NumberFormatOptions.
import { FormattedNumber } from "react-intl";
export default function ProductPrice({ price }: { price: number }) {
return (
<div>
<FormattedNumber value={price} />
</div>
);
}
По умолчанию <FormattedNumber> рендерит отформатированное число в React.Fragment. Компонент считывает локаль из ближайшего IntlProvider и применяет соответствующие правила форматирования.
2. Форматирование чисел с использованием определённых параметров
Передавайте параметры форматирования, чтобы контролировать, как отображаются числа. Общие параметры включают minimumFractionDigits, maximumFractionDigits и style.
import { FormattedNumber } from "react-intl";
export default function Statistics({ value }: { value: number }) {
return (
<dl>
<dt>Всего пользователей</dt>
<dd>
<FormattedNumber
value={value}
minimumFractionDigits={0}
maximumFractionDigits={0}
/>
</dd>
<dt>Коэффициент конверсии</dt>
<dd>
<FormattedNumber
value={value / 100}
style="percent"
minimumFractionDigits={2}
/>
</dd>
</dl>
);
}
Эти свойства соответствуют Intl.NumberFormatOptions, предоставляя вам контроль над точностью и представлением, сохраняя при этом разделители, специфичные для локали.
3. Императивное форматирование чисел с использованием хука useIntl
Когда компонент может быть представлен в виде функционального компонента, хук useIntl предоставляет доступ к объекту intl, который включает метод formatNumber.
import { useIntl } from "react-intl";
export default function DataTable({ rows }: { rows: number[] }) {
const intl = useIntl();
return (
<table>
<tbody>
{rows.map((value, index) => (
<tr key={index}>
<td>{intl.formatNumber(value)}</td>
</tr>
))}
</tbody>
</table>
);
}
Функция formatNumber возвращает строку с отформатированным числом и принимает значение, которое может быть преобразовано в число, а также параметры, соответствующие NumberFormatOptions.
4. Форматирование чисел вне контекста JSX
Используйте formatNumber, когда вам нужно отформатировать числа в атрибутах, переменных или других контекстах, где нельзя использовать компоненты.
import { useIntl } from "react-intl";
export default function Chart({ dataPoints }: { dataPoints: number[] }) {
const intl = useIntl();
const formattedLabel = intl.formatNumber(dataPoints[0], {
notation: "compact",
maximumFractionDigits: 1,
});
return (
<div>
<img
src="/chart.png"
alt={`График, показывающий ${formattedLabel} элементов`}
title={formattedLabel}
/>
</div>
);
}
Императивный API необходим для установки текстовых атрибутов, таких как title, aria-label или alt, где нельзя использовать React-компоненты.