diff --git a/src/constants/i18n.test.ts b/src/constants/i18n.test.ts index f87318ee..f4a6bac5 100644 --- a/src/constants/i18n.test.ts +++ b/src/constants/i18n.test.ts @@ -35,6 +35,28 @@ describe("i18n utility functions", () => { expect(formatCurrency(1200, "USD", "en-US")).toContain("$"); }); + it("uses the requested currency even when the locale has a different default", () => { + const usdInGerman = formatCurrency(100, "USD", "de-DE"); + expect(usdInGerman).toContain("$"); + expect(usdInGerman).not.toContain("€"); + expect(formatCurrency(100, "EUR", "en-US")).toContain("€"); + expect(formatCurrency(100, "GBP", "ja-JP")).toBe( + new Intl.NumberFormat("ja-JP", { style: "currency", currency: "GBP" }).format(100), + ); + }); + + it("falls back to the locale default currency when none is requested", () => { + expect(formatCurrency(100, undefined, "de-DE")).toContain("€"); + expect(formatCurrency(100, undefined, "ja-JP")).toBe( + new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY" }).format(100), + ); + }); + + it("only drops decimals when the requested currency is JPY", () => { + expect(formatCurrency(1234.5, "JPY", "en-US")).not.toMatch(/\.\d/); + expect(formatCurrency(1234.5, "USD", "ja-JP")).toContain(".50"); + }); + it("formats dates using locale settings", () => { expect(formatDate("2026-05-10", "en-US")).toContain("2026"); expect(formatDate("2026-05-10", "de-DE")).toContain("2026"); diff --git a/src/constants/i18n.ts b/src/constants/i18n.ts index ac2ed41b..a28b72e5 100644 --- a/src/constants/i18n.ts +++ b/src/constants/i18n.ts @@ -62,7 +62,7 @@ export const LOCALE_FORMAT_SETTINGS: Record = { defaultCurrency: "USD", date: { year: "numeric", month: "short", day: "numeric" }, number: { maximumFractionDigits: 2 }, - currency: { style: "currency", currency: "USD", currencyDisplay: "symbol", maximumFractionDigits: 2 }, + currency: { style: "currency", currencyDisplay: "symbol" }, }, "de-DE": { code: "de-DE", @@ -71,7 +71,7 @@ export const LOCALE_FORMAT_SETTINGS: Record = { defaultCurrency: "EUR", date: { year: "numeric", month: "numeric", day: "numeric" }, number: { maximumFractionDigits: 2 }, - currency: { style: "currency", currency: "EUR", currencyDisplay: "symbol", maximumFractionDigits: 2 }, + currency: { style: "currency", currencyDisplay: "symbol" }, }, "ja-JP": { code: "ja-JP", @@ -80,7 +80,7 @@ export const LOCALE_FORMAT_SETTINGS: Record = { defaultCurrency: "JPY", date: { year: "numeric", month: "numeric", day: "numeric" }, number: { maximumFractionDigits: 0, useGrouping: true }, - currency: { style: "currency", currency: "JPY", currencyDisplay: "symbol", maximumFractionDigits: 0 }, + currency: { style: "currency", currencyDisplay: "symbol" }, }, "ar-SA": { code: "ar-SA", @@ -89,7 +89,7 @@ export const LOCALE_FORMAT_SETTINGS: Record = { defaultCurrency: "SAR", date: { year: "numeric", month: "numeric", day: "numeric" }, number: { maximumFractionDigits: 2, useGrouping: true }, - currency: { style: "currency", currency: "SAR", currencyDisplay: "symbol", maximumFractionDigits: 2 }, + currency: { style: "currency", currencyDisplay: "symbol" }, }, "fr-FR": { code: "fr-FR", @@ -98,7 +98,7 @@ export const LOCALE_FORMAT_SETTINGS: Record = { defaultCurrency: "EUR", date: { year: "numeric", month: "numeric", day: "numeric" }, number: { maximumFractionDigits: 2 }, - currency: { style: "currency", currency: "EUR", currencyDisplay: "symbol", maximumFractionDigits: 2 }, + currency: { style: "currency", currencyDisplay: "symbol" }, }, "es-ES": { code: "es-ES", @@ -107,7 +107,7 @@ export const LOCALE_FORMAT_SETTINGS: Record = { defaultCurrency: "EUR", date: { year: "numeric", month: "numeric", day: "numeric" }, number: { maximumFractionDigits: 2 }, - currency: { style: "currency", currency: "EUR", currencyDisplay: "symbol", maximumFractionDigits: 2 }, + currency: { style: "currency", currencyDisplay: "symbol" }, }, "zh-CN": { code: "zh-CN", @@ -116,7 +116,7 @@ export const LOCALE_FORMAT_SETTINGS: Record = { defaultCurrency: "CNY", date: { year: "numeric", month: "numeric", day: "numeric" }, number: { maximumFractionDigits: 2, useGrouping: true }, - currency: { style: "currency", currency: "CNY", currencyDisplay: "symbol", maximumFractionDigits: 2 }, + currency: { style: "currency", currencyDisplay: "symbol" }, }, "en-GB": { code: "en-GB", @@ -125,7 +125,7 @@ export const LOCALE_FORMAT_SETTINGS: Record = { defaultCurrency: "GBP", date: { year: "numeric", month: "short", day: "numeric" }, number: { maximumFractionDigits: 2 }, - currency: { style: "currency", currency: "GBP", currencyDisplay: "symbol", maximumFractionDigits: 2 }, + currency: { style: "currency", currencyDisplay: "symbol" }, }, }; @@ -165,13 +165,13 @@ export function formatCurrency( locale: string = "en-US", options: Intl.NumberFormatOptions = {}, ): string { - const defaultCurr = currency ?? LOCALE_FORMAT_SETTINGS[locale]?.defaultCurrency ?? "USD"; + const resolvedCurrency = currency ?? LOCALE_FORMAT_SETTINGS[locale]?.defaultCurrency ?? "USD"; const setting = LOCALE_FORMAT_SETTINGS[locale]?.currency ?? {}; return new Intl.NumberFormat(locale, { style: "currency", - currency: defaultCurr, currencyDisplay: "symbol", ...setting, + currency: resolvedCurrency, ...options, }).format(value); }