Use Cases (Diperbarui: 2/6/2026)

Format Mata Uang dengan Claude Code: Panduan Intl.NumberFormat

Bangun format multi-mata uang dengan Intl.NumberFormat, minor units, rounding, accounting display, dan tes JPY/USD/EUR.

Format Mata Uang dengan Claude Code: Panduan Intl.NumberFormat

Format mata uang adalah bagian dari billing

Menampilkan $19.99, ¥1,980, atau Rp 123.456 terlihat seperti urusan UI. Dalam SaaS, ecommerce, invoice, dan dashboard pendapatan, format uang memengaruhi refund, pajak, ekspor CSV, dan laporan. JPY biasanya tanpa desimal, USD dan EUR memakai dua desimal, India mengelompokkan angka seperti12,34,567, dan nilai negatif bisa perlu accounting display seperti($10.00).

Pola yang aman adalah menyimpan uang sebagai integer minor units, melakukan perhitungan dengan integer, lalu memformat hanya di batas tampilan menggunakanIntl.NumberFormat. Minor unit adalah unit terkecil untuk perhitungan, misalnya cent untuk USD dan yen utuh untuk JPY.

Panduan ini memberi implementasi yang bisa dibuat Claude Code dan tetap mudah direview: pricing SaaS multi-currency, rounding, accounting display, larangan menyimpan string terformat, tes JPY/USD/EUR/BRL/INR/IDR, dan prompt review. Referensi resminya adalah MDN untukIntl.NumberFormat, MDN untukformatToParts, serta spesifikasiECMA-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]

Pisahkan nilai tersimpan dan teks tampilan

Jangan jadikan"$19.99"atau"Rp 123.456"sebagai sumber kebenaran di database. SimpanamountMinordancurrency: 1999 USD, 1980 JPY, 123456 IDR. Dengan begitu sorting, penjumlahan, refund, audit, dan lokalisasi tidak perlu parsing string.

PendekatanContohKelebihanRisiko
String terformat"$19.99"Cepat untuk halaman statisMerusak laporan, refund, dan lokalisasi
Angka desimal19.99Mudah di awalFloating-point error dan aturan mata uang menyebar
Integer minor unit1999Perhitungan dan tes stabilPerlu konversi di input/output

Intl.NumberFormatbertugas memformat. Ia tidak menentukan kurs, pajak, aturan payment provider, atau kapan invoice harus dibulatkan.

Implementasi yang bisa langsung dijalankan

Simpan sebagaicurrency-format-demo.mjs, lalu jalankannode 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");

Use case nyata

Pertama, tabel harga SaaS multi-currency harus menerima data terstruktur, bukan label harga yang sudah diformat.

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

Kedua, invoice dan refund bisa membutuhkancurrencySign: "accounting". Namun setiap locale punya aturan sendiri, jadi jangan menganggap semua output memakai tanda kurung. Untuk invoice PDF, tambahkan tes rendering atau snapshot.

Ketiga, pajak, diskon, dan prorata membutuhkan kebijakan rounding. 1999 * 10 / 31tidak bulat. Putuskan apakah rounding terjadi per baris invoice atau setelah total, lalu tulis kebijakan itu di nama tes.

Keempat, admin screen dan CSV sebaiknya mengeksporamount_minor, currency, danamount_display. Manusia membaca display; BI menghitung MRR, LTV, refund rate, dan ROAS dari kolom numerik.

Kesalahan umum

Jangan simpan string mata uang terformat. Jangan campur locale dan currency: UI bahasa Indonesia bisa saja menampilkan USD. Jangan menganggap semua mata uang punya dua desimal. roundingModebukan kebijakan billing lengkap, hanya opsi display. Jika perlu memisahkan simbol dan angka, gunakanformatToParts, bukan regex.

Prompt review untuk 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

Bacaan terkait dan CTA

Untuk fondasi lokalisasi, bacaimplementasi i18n dengan Claude Code. Untuk tanggal dan zona waktu, lihatdate/time handling. Untuk monetisasi berlangganan, gabungkan denganStripe subscription.

ClaudeCodeLab menerbitkan checklist dan prompt untuk batas implementasi yang sering disederhanakan AI: billing, auth, security, dan release review. Sebelum meminta Claude Code mengubah halaman pricing, pastikan data produk sudah memisahkanamountMinor, currency, danlocale.

Saya menjalankan demo script di Node.js 24 lokal dan memverifikasi fraction digits serta currency parts untuk JPY/USD/EUR/BRL/INR/IDR, accounting display USD, arithmetic integer, dan error saat mata uang dicampur. String persis bisa berubah karena data ICU, jadi tes produksi sebaiknya memvalidasi kebijakan, bukan hanya satu snapshot.

#Claude Code #mata uang #format #Intl #internasionalisasi
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.