Comment formater les montants en devises dans React Router v7

Afficher les prix avec les symboles monétaires et les séparateurs

Problème

L'affichage des prix dans les applications web nécessite de gérer deux aspects d'internationalisation interdépendants : la devise utilisée et les conventions régionales de présentation des valeurs monétaires. Un prix de 1200,50 dollars américains apparaît comme « $1,200.50 » pour les utilisateurs aux États-Unis mais comme « 1 200,50 $US » en France. La position du symbole monétaire, le séparateur décimal, le regroupement des milliers et l'espacement varient tous selon la locale. Lorsque ces conventions ne sont pas respectées, les utilisateurs peuvent mal interpréter les montants ou se demander si le prix est correct, ce qui nuit à la confiance dans l'application.

Le défi est amplifié lorsqu'une application dessert plusieurs régions ou permet aux utilisateurs de visualiser les prix dans différentes devises. Le codage en dur de chaînes de format ou le placement manuel de symboles crée un code fragile qui se brise lorsque de nouvelles locales ou devises sont ajoutées. Sans approche systématique, maintenir des affichages de prix cohérents et corrects dans toute l'application devient source d'erreurs.

Solution

Formatez les valeurs monétaires en utilisant à la fois le code de devise cible et la locale active de l'utilisateur. Cette approche délègue les règles complexes de présentation des devises aux API d'internationalisation du navigateur, qui encodent déjà les conventions de formatage pour des centaines de combinaisons locale-devise.

En transmettant un montant numérique, un code de devise et la locale actuelle à une fonction de formatage, le système applique automatiquement le symbole, les séparateurs et la mise en page corrects. Cela garantit que les prix apparaissent toujours dans un format familier à l'utilisateur, quelle que soit la devise affichée ou la région dans laquelle se trouve l'utilisateur.

Étapes

1. Créer un composant de formatage de devise réutilisable

Créez un composant qui utilise FormattedNumber de react-intl avec les props style et currency pour formater les valeurs monétaires.

import { FormattedNumber } from "react-intl";

interface PriceProps {
  amount: number;
  currency: string;
}

export function Price({ amount, currency }: PriceProps) {
  return (
    <FormattedNumber value={amount} style="currency" currency={currency} />
  );
}

Le composant FormattedNumber utilise l'API formatNumber et accepte Intl.NumberFormatOptions. L'option style="currency" indique au formateur d'inclure les symboles monétaires, et la prop currency spécifie la devise à afficher.

2. Utilisez le composant Price dans les composants de route

Affichez les prix dans n'importe quel composant de route React Router en passant la valeur numérique et le code de devise.

import { Price } from "~/components/Price";

export default function ProductPage() {
  const product = {
    name: "Wireless Headphones",
    price: 129.99,
    currency: "USD",
  };

  return (
    <div>
      <h1>{product.name}</h1>
      <p>
        <Price amount={product.price} currency={product.currency} />
      </p>
    </div>
  );
}

Le composant Price formate automatiquement le montant selon la locale configurée dans IntlProvider. Pour une locale américaine, cela affiche "$129.99" ; pour une locale allemande avec la même devise USD, cela affiche "129,99 $".

3. Formatez la devise de manière impérative si nécessaire

Pour les scénarios où vous avez besoin de la chaîne formatée directement, utilisez le hook useIntl pour accéder à la méthode formatNumber.

import { useIntl } from "react-intl";

export default function CheckoutSummary() {
  const intl = useIntl();
  const subtotal = 89.99;
  const tax = 7.2;
  const total = subtotal + tax;

  const formattedTotal = intl.formatNumber(total, {
    style: "currency",
    currency: "EUR",
  });

  return (
    <div>
      <h2>Order Summary</h2>
      <dl>
        <dt>Subtotal</dt>
        <dd>
          {intl.formatNumber(subtotal, {
            style: "currency",
            currency: "EUR",
          })}
        </dd>
        <dt>Tax</dt>
        <dd>
          {intl.formatNumber(tax, {
            style: "currency",
            currency: "EUR",
          })}
        </dd>
        <dt>Total</dt>
        <dd aria-label={`Total: ${formattedTotal}`}>
          <strong>{formattedTotal}</strong>
        </dd>
      </dl>
    </div>
  );
}

La méthode formatNumber accepte une valeur et un objet d'options avec FormatNumberOptions. Cela est utile lorsque vous avez besoin de la chaîne formatée pour des attributs, de la journalisation ou des contextes non-JSX.

4. Contrôlez la précision décimale pour les devises en nombres entiers

Certaines devises n'utilisent pas d'unités fractionnaires. Ajustez le nombre de décimales pour correspondre aux conventions monétaires.

import { FormattedNumber } from "react-intl";

interface PriceProps {
  amount: number;
  currency: string;
}

export function Price({ amount, currency }: PriceProps) {
  const isWholeNumberCurrency = currency === "JPY" || currency === "KRW";

  return (
    <FormattedNumber
      value={amount}
      style="currency"
      currency={currency}
      minimumFractionDigits={isWholeNumberCurrency ? 0 : 2}
      maximumFractionDigits={isWholeNumberCurrency ? 0 : 2}
    />
  );
}

Le yen japonais n'utilise pas d'unité mineure, les montants sont donc affichés sans décimales. Les options minimumFractionDigits et maximumFractionDigits remplacent le comportement décimal par défaut si nécessaire.