Как форматировать числа для разных локалей в 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>Price: {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 предоставляет декларативную альтернативу, принимая те же опции в виде props.

import { FormattedNumber } from "react-intl";

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

  return (
    <div>
      <p>
        Total users: <FormattedNumber value={totalUsers} />
      </p>
      <p>
        Growth rate: <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>Dashboard</h1>
      <p>
        Revenue:{" "}
        <FormattedNumber value={revenue} style="currency" currency="USD" />
      </p>
      <p>
        Units sold: <FormattedNumber value={units} />
      </p>
    </div>
  );
}

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