Use Cases (Mis à jour: 02/06/2026)

Formatage monétaire avec Claude Code : guide Intl.NumberFormat

Implémentez les devises avec Intl.NumberFormat, minor units, arrondi, affichage comptable et tests JPY/USD/EUR.

Formatage monétaire avec Claude Code : guide Intl.NumberFormat

Le formatage monétaire fait partie de la facturation

Afficher $19.99, ¥1,980 ou R$ 1.234,56 semble être un détail d’interface. En réalité, dans un SaaS, une boutique ou une facture, cela touche les remboursements, les taxes, les exports et les rapports de revenus. Le JPY s’affiche généralement sans décimales, l’USD et l’EUR avec deux, l’anglais indien groupe les grands nombres comme12,34,567, et les montants négatifs peuvent exiger une notation comptable comme($10.00).

La règle robuste consiste à stocker un entier en minor units, à calculer sur ces entiers, puis à formater seulement au dernier moment avecIntl.NumberFormat. Une minor unit est l’unité minimale de calcul : centimes pour USD, yen entier pour JPY.

Ce guide donne une base que Claude Code peut modifier sans masquer les décisions de facturation : prix SaaS multi-devises, arrondi, affichage comptable, interdiction de stocker des chaînes formatées, tests JPY/USD/EUR/BRL/INR/IDR et prompt de revue. Les références officielles sont MDN pourIntl.NumberFormat, MDN pourformatToParts et la spécificationECMA-402 NumberFormat.

flowchart LR
  A[DB: minor unit integer] --> B[Domain math]
  B --> C[Tax, discount, refund]
  C --> D[Intl.NumberFormat]
  D --> E[UI, invoice, email]

Séparer la donnée stockée du texte affiché

Ne stockez pas"$19.99" en base. StockezamountMinor etcurrency : 1999 USD, 1980 JPY, 123456 IDR. Vous évitez ainsi le parsing de textes localisés pour trier, additionner, rembourser ou auditer.

ApprocheExempleAvantageRisque
Chaîne formatée"$19.99"Rapide pour une page statiqueCasse les rapports, remboursements et traductions
Nombre décimal19.99Simple au débutErreurs de virgule flottante et règles de devise dispersées
Entier minor unit1999Calculs et tests stablesConversion nécessaire aux frontières

Intl.NumberFormat formate. Il ne décide pas des taux de change, des taxes, des règles Stripe/Paddle ni du moment où une facture doit être arrondie.

Implémentation exécutable

Enregistrez ce fichier souscurrency-format-demo.mjs, puis lanceznode currency-format-demo.mjs.

// currency-format-demo.mjs
import assert from "node:assert/strict";

const minorUnitDigits = Object.freeze({
  JPY: 0,
  USD: 2,
  EUR: 2,
  BRL: 2,
  INR: 2,
  IDR: 0,
});

function assertCurrency(currency) {
  if (!(currency in minorUnitDigits)) {
    throw new Error(`Unsupported currency: ${currency}`);
  }
}

function roundHalfAwayFromZero(value) {
  return value < 0 ? -Math.round(Math.abs(value)) : Math.round(value);
}

export function moneyFromMajor(amount, currency) {
  assertCurrency(currency);
  if (!Number.isFinite(amount)) {
    throw new Error(`Invalid amount: ${amount}`);
  }
  const digits = minorUnitDigits[currency];
  return {
    minor: roundHalfAwayFromZero(amount * 10 ** digits),
    currency,
  };
}

export function toMajor(money) {
  assertCurrency(money.currency);
  return money.minor / 10 ** minorUnitDigits[money.currency];
}

export function addMoney(left, right) {
  if (left.currency !== right.currency) {
    throw new Error(`Currency mismatch: ${left.currency} vs ${right.currency}`);
  }
  return { minor: left.minor + right.minor, currency: left.currency };
}

export function multiplyMoney(money, factor) {
  if (!Number.isFinite(factor)) {
    throw new Error(`Invalid factor: ${factor}`);
  }
  return {
    minor: roundHalfAwayFromZero(money.minor * factor),
    currency: money.currency,
  };
}

export function formatMoney(
  money,
  {
    locale = "en-US",
    accounting = false,
    currencyDisplay = "symbol",
    roundingMode = "halfExpand",
  } = {},
) {
  assertCurrency(money.currency);
  return new Intl.NumberFormat(locale, {
    style: "currency",
    currency: money.currency,
    currencyDisplay,
    currencySign: accounting ? "accounting" : "standard",
    roundingMode,
  }).format(toMajor(money));
}

const samples = [
  ["ja-JP", { minor: 123456, currency: "JPY" }],
  ["en-US", { minor: 123456, currency: "USD" }],
  ["de-DE", { minor: 123456, currency: "EUR" }],
  ["pt-BR", { minor: 123456, currency: "BRL" }],
  ["en-IN", { minor: 123456789, currency: "INR" }],
  ["id-ID", { minor: 123456, currency: "IDR" }],
];

for (const [locale, money] of samples) {
  const formatter = new Intl.NumberFormat(locale, {
    style: "currency",
    currency: money.currency,
  });
  const options = formatter.resolvedOptions();
  const parts = formatter.formatToParts(toMajor(money));

  assert.equal(options.maximumFractionDigits, minorUnitDigits[money.currency]);
  assert.ok(parts.some((part) => part.type === "currency"));
  console.log(`${locale} ${money.currency}: ${formatMoney(money, { locale })}`);
}

assert.equal(
  addMoney(moneyFromMajor(19.99, "USD"), moneyFromMajor(5, "USD")).minor,
  2499,
);
assert.equal(multiplyMoney(moneyFromMajor(1980, "JPY"), 1.1).minor, 2178);
assert.match(
  formatMoney({ minor: -129900, currency: "USD" }, { locale: "en-US", accounting: true }),
  /^\(\$/,
);
assert.throws(
  () => addMoney(moneyFromMajor(10, "USD"), moneyFromMajor(10, "JPY")),
  /Currency mismatch/,
);

console.log("currency formatting checks passed");

Cas d’usage réels

Un tableau de prix SaaS multi-devises doit recevoir des données structurées, pas des libellés déjà traduits.

type CurrencyCode = "JPY" | "USD" | "EUR" | "BRL" | "INR" | "IDR";

type PlanPrice = {
  planId: "starter" | "pro" | "team";
  currency: CurrencyCode;
  amountMinor: number;
};

const prices: PlanPrice[] = [
  { planId: "pro", currency: "JPY", amountMinor: 1980 },
  { planId: "pro", currency: "USD", amountMinor: 1999 },
  { planId: "pro", currency: "EUR", amountMinor: 1899 },
];

Deuxième cas : factures et remboursements. currencySign: "accounting" peut produire une notation plus adaptée pour les montants négatifs, mais chaque locale garde ses règles. Pour un PDF de facture, ajoutez des tests de rendu.

Troisième cas : taxes, remises et prorata. 1999 * 10 / 31 ne tombe pas juste. Décidez si l’arrondi se fait par ligne ou après totalisation, puis rendez cette règle visible dans les noms de tests.

Quatrième cas : back-office et CSV. Exportezamount_minor, currency etamount_display. Les humains lisent le dernier champ, les outils de BI utilisent les deux premiers pour calculer MRR, LTV et remboursements.

Pièges fréquents

Ne stockez pas les chaînes monétaires formatées. Ne confondez pas locale et devise : une interface française peut afficher une facture USD. Ne supposez pas deux décimales pour toutes les devises. Ne prenez pasroundingMode pour une politique de facturation complète. Pour isoler le symbole ou les décimales, utilisezformatToParts plutôt qu’une expression régulière.

Prompt de revue pour Claude Code

Review this repository's money formatting and billing math.
Requirements:
- Check that DB/API models do not store formatted currency strings
- Make JPY/USD/EUR/BRL/INR/IDR minor units explicit
- Use Intl.NumberFormat while keeping locale and currency separate
- Decide whether refunds and discounts need currencySign: "accounting"
- Make rounding policy visible in test names
- Add Node-runnable tests for currency formatting behavior

À lire ensuite

Pour l’internationalisation générale, lisezimplémenter i18n avec Claude Code. Pour les dates et fuseaux horaires, voyezgestion date/heure. Pour la monétisation SaaS, combinez ce guide avecStripe subscription.

ClaudeCodeLab publie des checklists et prompts pour les zones que l’IA simplifie trop vite : facturation, auth, sécurité et release review. Avant de confier une page de prix à Claude Code, vérifiez que les données sont déjà séparées enamountMinor, currency etlocale.

J’ai vérifié le script avec Node.js 24 : chiffres fractionnaires et parties de devise pour JPY/USD/EUR/BRL/INR/IDR, affichage comptable USD, calculs entiers et erreur de devise mélangée. Les chaînes exactes peuvent varier selon les données ICU ; testez donc la politique métier, pas seulement une capture de texte.

#Claude Code #devise #formatage #Intl #internationalisation
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.