Как форматировать числа для разных локалей в TanStack Start v1
Отображение чисел с учётом локальных разделителей
Проблема
Числа записываются по-разному в разных странах. То, что в США выглядит как 10,000.5, в Германии превращается в 10.000,5 — запятая и точка меняются местами. Это не вопрос стиля или предпочтений, а вопрос читаемости. Немец, увидев 10,000.5, может прочитать это как десять, игнорируя разделители разрядов. Американец, увидев 10.000,5, может воспринять это как десять тысяч, не заметив десятичный разделитель. Одни и те же цифры — противоположные значения.
Если приложение отображает числа без учёта региональных стандартов форматирования, это вызывает путаницу и снижает доверие. Пользователи ожидают видеть привычные им форматы, и любые отклонения заставляют их задуматься, перепроверять и сомневаться в корректности данных.
Решение
Форматируйте числа в соответствии с локалью пользователя, используя региональные правила для десятичных и разрядных разделителей. Это превращает числовые значения в строки, которые выглядят привычно для пользователей из разных регионов.
React-intl предоставляет компоненты и хуки, которые используют встроенный API браузера Intl.NumberFormat для применения локального форматирования. Передавая числовое значение и текущую локаль, эти инструменты автоматически выбирают нужные разделители, группировку цифр и другие региональные особенности. В результате получается строка, полностью соответствующая ожиданиям пользователя, без ручной обработки строк или написания локализованной логики в компонентах.
Шаги
1. Создайте компонент для отображения числа с помощью FormattedNumber
Создайте компонент, который принимает числовое значение и отображает его с учётом локали, используя компонент FormattedNumber из react-intl.
import { FormattedNumber } from "react-intl";
interface PriceDisplayProps {
value: number;
}
export function PriceDisplay({ value }: PriceDisplayProps) {
return (
<div>
<FormattedNumber value={value} />
</div>
);
}
Компонент FormattedNumber автоматически применяет десятичные и разделительные знаки, соответствующие выбранной локали. Он получает локаль из ближайшего IntlProvider в дереве компонентов и форматирует число соответственно.
2. Форматируйте числа с определёнными стилями
Настраивайте форматирование чисел, передавая параметры стиля в FormattedNumber для валюты, процентов или единиц.
import { FormattedNumber } from "react-intl";
interface MetricsProps {
revenue: number;
growthRate: number;
fileSize: number;
}
export function Metrics({ revenue, growthRate, fileSize }: MetricsProps) {
return (
<div>
<p>
Revenue:{" "}
<FormattedNumber value={revenue} style="currency" currency="USD" />
</p>
<p>
Growth: <FormattedNumber value={growthRate} style="percent" />
</p>
<p>
Size:{" "}
<FormattedNumber
value={fileSize}
style="unit"
unit="megabyte"
unitDisplay="short"
/>
</p>
</div>
);
}
Свойство style управляет режимом форматирования. Для форматирования валюты требуется код currency, для процентов значение автоматически умножается на 100, а для единиц требуется идентификатор unit. Все стили учитывают правила выбранной локали.
3. Императивное форматирование чисел с помощью useIntl
Используйте хук useIntl для форматирования чисел там, где компоненты не подходят, например, при генерации динамических атрибутов или подготовке данных для не-React API.
import { useIntl } from "react-intl";
interface ChartTooltipProps {
dataPoint: number;
}
export function ChartTooltip({ dataPoint }: ChartTooltipProps) {
const intl = useIntl();
const formattedValue = intl.formatNumber(dataPoint, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
return <div title={formattedValue}>{formattedValue}</div>;
}
Метод formatNumber сразу возвращает отформатированную строку, что удобно для атрибутов, aria-меток или любых случаев, когда нужно получить значение в виде строки, а не React-элемента.
4. Используйте кастомные параметры форматирования
Управляйте точностью, разделением и стилем записи, передавая Intl.NumberFormatOptions как в FormattedNumber, так и в formatNumber.
import { FormattedNumber } from "react-intl";
interface StatisticProps {
value: number;
compact?: boolean;
}
export function Statistic({ value, compact }: StatisticProps) {
return (
<div>
<FormattedNumber
value={value}
notation={compact ? "compact" : "standard"}
minimumFractionDigits={0}
maximumFractionDigits={2}
/>
</div>
);
}
Параметры вроде notation, minimumFractionDigits и maximumFractionDigits определяют, как будет отображаться число. Компактная запись сокращает большие числа, например, до «1,2M», с учётом локальных сокращений.