Claude Code से currency formatting: Intl.NumberFormat practical guide
Intl.NumberFormat से currency display, minor units, rounding, accounting format और JPY/USD/EUR tests बनाएं।
Currency formatting सिर्फ UI नहीं, billing boundary है
SaaS pricing, ecommerce checkout, invoice और admin reports में amount दिखाना छोटी UI task लग सकती है। लेकिन$19.99, ¥1,980, ₹12,34,567.89औरR$ 1.234,56के पीछे अलग-अलग locale rules होते हैं। JPY अक्सर decimal के बिना दिखता है, USD/EUR दो decimal दिखाते हैं, Indian English grouping अलग है, और refund line को accounting style में($10.00)जैसा दिखाना पड़ सकता है।
मजबूत तरीका यह है कि database में amount को integer minor units में store करें, domain math integer पर करें, और display के आखिरी step मेंIntl.NumberFormatलगाएं। Minor unit का मतलब है calculation की सबसे छोटी unit: USD के लिए cent, JPY के लिए 1 yen।
यह guide Claude Code से reviewable implementation बनवाने के लिए है: multi-currency SaaS billing, rounding, accounting display, formatted strings न store करने का rule, JPY/USD/EUR/BRL/INR/IDR tests और review prompt। Official reference के लिए MDN काIntl.NumberFormat, MDN काformatToParts, औरECMA-402 NumberFormat spec देखें।
flowchart LR
A[DB: minor unit integer] --> B[Domain math]
B --> C[Tax, discount, refund]
C --> D[Intl.NumberFormat]
D --> E[UI, invoice, email]
Stored value और display text अलग रखें
Database में"$19.99"या"₹1,999.00"को source of truth न बनाएं। इसके बजायamountMinorऔरcurrencystore करें: 1999 USD, 1980 JPY, 123456 IDR। इससे sorting, sum, refund, audit और translation साफ रहते हैं।
| Approach | Example | Benefit | Risk |
|---|---|---|---|
| Formatted string | "$19.99" | Static page में तेज | Reports, refunds और localization टूटते हैं |
| Decimal number | 19.99 | शुरुआत में आसान | Floating-point error और currency rules फैल जाते हैं |
| Minor-unit integer | 1999 | Calculation और tests stable | Input/output boundary पर conversion चाहिए |
Intl.NumberFormatसिर्फ format करता है। Exchange rate, tax, payment provider minor unit policy, और invoice rounding point application को तय करना होता है।
Copy-paste runnable implementation
इसेcurrency-format-demo.mjsके रूप में save करें औरnode 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");
Real use cases
पहला use case multi-currency SaaS pricing है। Pricing component को preformatted label नहीं, structured data दें।
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 },
];
दूसरा use case invoice और refunds है। currencySign: "accounting"negative amount को locale rules के अनुसार accounting style में दिखा सकता है, लेकिन हर locale parentheses नहीं दिखाता। Invoice PDF के लिए rendering या snapshot tests रखें।
तीसरा use case tax, discount और proration है। 1999 * 10 / 31exact नहीं होगा। Decide करें कि rounding invoice line पर होगी या final total पर, और इस policy को test name में लिखें।
चौथा use case admin screen और CSV export है। amount_minor, currency, औरamount_displayतीनों columns दें। Human display पढ़ेगा, BI tools numeric columns से MRR, LTV और refund rate निकालेंगे।
Common pitfalls
Formatted currency strings store न करें। Locale और currency को mix न करें: Hindi UI में USD price दिख सकता है। हर currency को two-decimal न मानें। roundingModeपूरी billing policy नहीं है; यह display rounding option है। Currency symbol निकालने के लिए regex न लिखें, जरूरत हो तोformatToPartsuse करें।
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
Related guides और CTA
Localization foundation के लिएClaude Code i18n implementation, dates और time zones के लिएdate/time handling, और paid subscriptions के लिएStripe subscription guide देखें।
ClaudeCodeLab billing, auth, security और release review जैसे areas के लिए practical checklists और prompts बनाता है। Pricing page बदलने से पहले यह verify करें कि product dataamountMinor + currency + localeमें अलग-अलग है।
मैंने demo script को local Node.js 24 पर चलाकर JPY/USD/EUR/BRL/INR/IDR fraction digits, currency parts, USD accounting display, integer arithmetic और currency mismatch error verify किया। ICU data के कारण exact string बदल सकती है, इसलिए production tests में सिर्फ snapshot नहीं, policy check करें।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code Obsidian to CLAUDE.md workflow: context बार-बार न समझाएं
Obsidian notes को CLAUDE.md operating notes में बदलकर Claude Code sessions को resume करना आसान बनाएं.
Claude Code Revenue CTA Routing: article से PDF, Gumroad और consultation तक
Reader intent के आधार पर free PDF, Gumroad products और consultation तक CTA route करने वाला workflow.
Claude Code टीम हैंडऑफ नियम: review proof, permissions, rollback और revenue path
Claude Code टीम काम के लिए evidence, permission rules, rollback, free PDF, Gumroad और consultation path वाला handoff.