Use Cases (Actualizado: 2/6/2026)

Formato de moneda con Claude Code: guía práctica de Intl.NumberFormat

Implementa monedas con Intl.NumberFormat, minor units, redondeo, formato contable y pruebas para JPY/USD/EUR.

Formato de moneda con Claude Code: guía práctica de Intl.NumberFormat

El formato de moneda también es lógica de cobro

Mostrar $19.99, ¥1,980 o R$ 1.234,56 parece una tarea visual, pero en un SaaS, una tienda o una factura afecta soporte, devoluciones, impuestos y reportes. JPY normalmente se muestra sin decimales, USD y EUR usan dos, India agrupa cifras como12,34,567, y Brasil cambia el uso de coma y punto frente al inglés de Estados Unidos. Además, una devolución puede necesitar formato contable como($10.00).

La regla práctica es simple: guarda importes como enteros en minor units, calcula con enteros y formatea solo al final conIntl.NumberFormat. Minor unit significa la unidad mínima usada para calcular: centavos para USD, yenes enteros para JPY.

Esta guía muestra una implementación que Claude Code puede generar y que un humano puede revisar: precios SaaS multi-moneda, redondeo, display contable, no guardar strings formateados, pruebas para JPY/USD/EUR/BRL/INR/IDR y prompts de revisión. Usa como base la documentación oficial de MDN paraIntl.NumberFormat, MDN paraformatToParts y la especificaciónECMA-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]

Separa el valor guardado del texto mostrado

No guardes"$19.99" en la base de datos. GuardaamountMinor ycurrency: 1999 USD, 1980 JPY, 123456 IDR. Así puedes ordenar, sumar, auditar, devolver y traducir sin parsear textos localizados.

EnfoqueEjemploVentajaRiesgo
String formateado"$19.99"Rápido para una página estáticaRompe reportes, devoluciones y localización
Número decimal19.99Fácil al principioFiltra errores de coma flotante
Entero minor unit1999Cálculo y pruebas establesRequiere conversión en entradas y salidas

Intl.NumberFormat formatea. No decide tipo de cambio, impuestos, política de Stripe/Paddle ni en qué punto redondear una factura. Pídele a Claude Code que mantenga esas responsabilidades separadas.

Implementación ejecutable

Guarda esto comocurrency-format-demo.mjs y ejecutanode 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");

Casos de uso reales

Primero, una tabla de precios SaaS multi-moneda debe recibir datos estructurados, no etiquetas traducidas.

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

Segundo, facturas y reembolsos pueden requerircurrencySign: "accounting". No todas las locales usan paréntesis, así que una factura PDF con reglas fijas necesita pruebas visuales o snapshots.

Tercero, impuestos, descuentos y prorrateos necesitan una política de redondeo. 1999 * 10 / 31 no es exacto; decide si redondeas cada línea o el total. Ese detalle debe aparecer en el nombre de la prueba.

Cuarto, para paneles y CSV exportaamount_minor, currency yamount_display. Así el equipo puede leer la tabla y también calcular MRR, LTV, reembolsos y ROAS sin limpiar strings.

Errores frecuentes

No guardes strings formateados. No confundas locale con currency: una interfaz en español puede cobrar en USD o EUR. No asumas dos decimales para todas las monedas. No tratesroundingMode como política completa de cobro; solo afecta el formato de salida. Si necesitas separar símbolo, enteros y decimales, usaformatToParts en vez de regex.

Prompt para revisar con 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

Lecturas relacionadas y CTA

Para internacionalización completa, leeimplementación i18n con Claude Code. Para fechas y zonas horarias, vemanejo de fecha y hora. Para suscripciones, combínalo conStripe subscriptions.

ClaudeCodeLab publica checklists y prompts para límites de implementación que la IA suele simplificar demasiado: cobros, auth, seguridad y releases. Antes de pedirle a Claude Code que cambie una página de precios, verifica que tu producto separaamountMinor, currency ylocale.

Probé el script localmente con Node.js 24. Verifica dígitos fraccionarios y partes de moneda para JPY/USD/EUR/BRL/INR/IDR, display contable en USD, aritmética entera y error por mezclar monedas. Las cadenas exactas pueden variar por datos ICU, así que en producción conviene probar la política, no solo un snapshot.

#Claude Code #moneda #formato #Intl #internacionalización
Gratis

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.