Как форматировать числа для разных локалей в React Router v7

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

Проблема

Числа записываются по-разному в разных странах. То, что в Соединенных Штатах выглядит как 10,000.5, в Германии становится 10.000,5 — запятые и точки полностью меняются местами. Это не вопрос предпочтений или стиля, а вопрос читаемости. Немецкий пользователь, видя 10,000.5, может прочитать это как десять, игнорируя разделители групп. Американский пользователь, видя 10.000,5, может прочитать это как десять тысяч, игнорируя десятичный разделитель.

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

Решение

Форматируйте числа в соответствии с локалью пользователя, используя региональные правила для десятичных и групповых разделителей. Это преобразует числовые значения в строки, которые соответствуют правилам форматирования, знакомым пользователям в их регионе.

React-intl предоставляет методы форматирования, которые используют встроенный API браузера Intl.NumberFormat, обрабатывающий сложность региональных числовых стандартов. Пропуская числовые значения через эти форматтеры, приложение создает вывод, соответствующий ожиданиям пользователей, без необходимости ручной логики разделителей.

Шаги

1. Создайте компонент, который форматирует числа с помощью useIntl

Хук useIntl предоставляет доступ к методам форматирования, включая formatNumber, который принимает числовое значение и возвращает строку, отформатированную в соответствии с локалью.

import { useIntl } from "react-intl";

export default function ProductPrice() {
  const intl = useIntl();
  const price = 1234.56;

  return (
    <div>
      <p>Цена: {intl.formatNumber(price)}</p>
    </div>
  );
}

Метод formatNumber автоматически применяет правильные разделители для текущей локали. Пользователь с локалью en-US увидит "1,234.56", а пользователь с локалью de-DE увидит "1.234,56".

2. Форматирование денежных значений с использованием параметров стиля

Метод formatNumber принимает параметры, соответствующие Intl.NumberFormatOptions, включая форматирование валюты.

import { useIntl } from "react-intl";

export default function ProductPrice() {
  const intl = useIntl();
  const price = 1234.56;

  return (
    <div>
      <p>
        {intl.formatNumber(price, {
          style: "currency",
          currency: "USD",
        })}
      </p>
    </div>
  );
}

Это создаёт "$1,234.56" для en-US и "1.234,56 $" для de-DE, применяя как разделители, так и символы валюты.

3. Использование FormattedNumber для декларативного форматирования

Компонент FormattedNumber предоставляет декларативную альтернативу, принимая те же параметры в качестве свойств.

import { FormattedNumber } from "react-intl";

export default function Statistics() {
  const totalUsers = 1500000;
  const growthRate = 0.23;

  return (
    <div>
      <p>
        Всего пользователей: <FormattedNumber value={totalUsers} />
      </p>
      <p>
        Темп роста: <FormattedNumber value={growthRate} style="percent" />
      </p>
    </div>
  );
}

Компонент напрямую отображает отформатированное число в DOM. Для en-US это отображает "1,500,000" и "23%". Для de-DE это отображает "1.500.000" и "23 %".

4. Форматирование чисел из данных загрузчика

В React Router 7 компоненты маршрутов получают доступ к данным загрузчика через хук useLoaderData. Совместите это с форматированием чисел для отображения значений, предоставленных сервером.

import { useLoaderData } from "react-router";
import { FormattedNumber } from "react-intl";

export async function loader() {
  return {
    revenue: 45678.9,
    units: 12500,
  };
}

export default function Dashboard() {
  const { revenue, units } = useLoaderData<typeof loader>();

  return (
    <div>
      <h1>Панель управления</h1>
      <p>
        Доход:{" "}
        <FormattedNumber value={revenue} style="currency" currency="USD" />
      </p>
      <p>
        Продано единиц: <FormattedNumber value={units} />
      </p>
    </div>
  );
}

Загрузчик предоставляет необработанные числовые данные, а компонент форматирует их в соответствии с локалью пользователя. Такое разделение позволяет отделить получение данных от вопросов их представления.