Use Cases (Aktualisiert: 2.6.2026)

Währungsformatierung mit Claude Code: Intl.NumberFormat in der Praxis

Mehrwährungs-Formatierung mit Intl.NumberFormat, Minor Units, Rundung, Accounting-Anzeige und JPY/USD/EUR-Tests.

Währungsformatierung mit Claude Code: Intl.NumberFormat in der Praxis

Währungsformatierung ist Teil der Abrechnung

$19.99, ¥1,980 oder R$ 1.234,56 wirken wie reine UI-Details. In einem SaaS, Shop oder Rechnungssystem beeinflussen sie aber Rückerstattungen, Steuern, Exporte und Umsatzberichte. JPY wird meist ohne Nachkommastellen angezeigt, USD und EUR mit zwei, indisches Englisch gruppiert Zahlen wie12,34,567, und negative Rechnungspositionen können eine Accounting-Darstellung wie($10.00) brauchen.

Die robuste Regel lautet: Speichere Geld als Integer in Minor Units, rechne mit Integern, und formatiere erst an der Ausgabestelle mitIntl.NumberFormat. Minor Unit bedeutet kleinste Recheneinheit einer Währung, also Cent bei USD und ganze Yen bei JPY.

Diese Anleitung zeigt eine Struktur, die Claude Code implementieren kann und die trotzdem prüfbar bleibt: SaaS-Preise in mehreren Währungen, Rundung, Accounting-Anzeige, keine formatierten Strings in der Datenbank, Tests für JPY/USD/EUR/BRL/INR/IDR und ein Review-Prompt. Nutze als Referenz MDN zuIntl.NumberFormat, MDN zuformatToParts und dieECMA-402 NumberFormat-Spezifikation.

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

Gespeicherte Werte und Anzeige trennen

Speichere nicht"$19.99" oder"1.980 €" als Quelle der Wahrheit. SpeichereamountMinor undcurrency: 1999 USD, 1980 JPY, 123456 IDR. So bleiben Sortierung, Summen, Rückerstattungen, Audits und Lokalisierung sauber.

AnsatzBeispielVorteilRisiko
Formatierter String"$19.99"Schnell für statische SeitenBricht Reporting, Refunds und Übersetzung
Dezimalzahl19.99Am Anfang einfachFloating-Point-Fehler und Währungsregeln verteilen sich
Minor-Unit-Integer1999Stabile Berechnung und TestsKonvertierung an Ein-/Ausgabegrenzen nötig

Intl.NumberFormat formatiert. Es entscheidet keine Wechselkurse, Steuern, Payment-Provider-Regeln oder Rechnungsrundung.

Direkt ausführbare Implementierung

Speichere den Code alscurrency-format-demo.mjs und führenode currency-format-demo.mjs aus.

// 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");

Praxisfälle

Eine SaaS-Preistabelle sollte strukturierte Daten erhalten, keine fertig übersetzten Preislabels.

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 },
];

Zweiter Fall: Rechnungen und Rückerstattungen. currencySign: "accounting" kann negative Beträge passend darstellen, aber nicht jede Locale nutzt Klammern. Für PDFs brauchst du Rendering- oder Snapshot-Tests.

Dritter Fall: Steuern, Rabatte und anteilige Abrechnung. 1999 * 10 / 31 geht nicht glatt auf. Entscheide, ob pro Rechnungszeile oder erst nach der Summe gerundet wird, und schreibe diese Regel in den Testnamen.

Vierter Fall: Admin-UI und CSV. Exportiereamount_minor, currency undamount_display. Menschen lesen die Anzeige, BI-Tools rechnen mit den ersten beiden Spalten.

Häufige Fehler

Speichere keine formatierten Währungsstrings. Verwechsle Locale nicht mit Währung: Eine deutsche Oberfläche kann USD anzeigen. Gehe nicht von zwei Dezimalstellen für jede Währung aus. BehandleroundingMode nicht als vollständige Abrechnungsregel. Zerlege Beträge mitformatToParts, nicht mit Regex.

Claude-Code-Review-Prompt

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

Weiterführende Artikel und CTA

Für Lokalisierung liesi18n mit Claude Code. Für Datum und Zeitzonen sieheDate and Time Handling. Für bezahlte Pläne kombiniere dies mitStripe Subscription.

ClaudeCodeLab veröffentlicht Checklisten und Prompts für Grenzen, die KI gern zu stark vereinfacht: Billing, Auth, Security und Release Review. Prüfe vor einer Preis-Seiten-Überarbeitung, ob deine Daten bereitsamountMinor, currency undlocale trennen.

Ich habe das Skript lokal mit Node.js 24 geprüft: Nachkommastellen und Currency-Parts für JPY/USD/EUR/BRL/INR/IDR, USD-Accounting-Darstellung, Integer-Arithmetik und Fehler bei gemischten Währungen. Exakte Strings können durch ICU-Daten variieren; teste daher die Policy, nicht nur einen Snapshot.

#Claude Code #Währung #Formatierung #Intl #Internationalisierung
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.