Skip to content

Commit 3254530

Browse files
authored
fix(frontend): non usd formatting broken (#7517)
fix: non usd formatting broke
1 parent 83912f6 commit 3254530

1 file changed

Lines changed: 27 additions & 22 deletions

File tree

‎packages/ui/src/composables/format-money.ts‎

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,27 @@ import { LRUCache } from 'lru-cache'
33
import { injectI18n } from '../providers/i18n'
44

55
const formatterCache = new LRUCache<string, Intl.NumberFormat>({ max: 10 })
6-
const maxDigitsCache = new LRUCache<string, number>({ max: 10 })
6+
// Billing amounts use Stripe's minor units, which can differ from Intl's display defaults
7+
// (for example, COP displays without decimals in some runtimes but stores centavos).
8+
// ISK and UGX also use two-decimal amounts for Stripe compatibility.
9+
// https://docs.stripe.com/currencies#zero-decimal
10+
const zeroDecimalCurrencies = new Set([
11+
'BIF',
12+
'CLP',
13+
'DJF',
14+
'GNF',
15+
'JPY',
16+
'KMF',
17+
'KRW',
18+
'MGA',
19+
'PYG',
20+
'RWF',
21+
'VND',
22+
'VUV',
23+
'XAF',
24+
'XOF',
25+
'XPF',
26+
])
727

828
// `formatMoney(1234.56, 'USD')` → `$1,234.56`
929
export function useFormatMoney() {
@@ -26,13 +46,13 @@ export function useFormatPrice() {
2646
const { locale } = injectI18n()
2747

2848
function format(price: number, currency: string, trimZeros = false): string {
29-
const maxDigits = getMaxDigits(currency)
49+
const maxDigits = zeroDecimalCurrencies.has(currency.toUpperCase()) ? 0 : 2
3050
const convertedPrice = price / Math.pow(10, maxDigits)
3151

32-
const minimumFractionDigits = trimZeros && Number.isInteger(convertedPrice) ? 0 : undefined
52+
const minimumFractionDigits = trimZeros && Number.isInteger(convertedPrice) ? 0 : maxDigits
3353

3454
try {
35-
const formatter = getFormatter(locale.value, currency, minimumFractionDigits)
55+
const formatter = getFormatter(locale.value, currency, minimumFractionDigits, maxDigits)
3656
return formatter.format(convertedPrice)
3757
} catch {
3858
return `${currency} ${convertedPrice}`
@@ -46,33 +66,18 @@ function getFormatter(
4666
locale: string,
4767
currency: string,
4868
minimumFractionDigits?: number,
69+
maximumFractionDigits?: number,
4970
): Intl.NumberFormat {
50-
const cacheKey = `${locale}:${currency}:${minimumFractionDigits}`
71+
const cacheKey = `${locale}:${currency}:${minimumFractionDigits}:${maximumFractionDigits}`
5172
let formatter = formatterCache.get(cacheKey)
5273
if (!formatter) {
5374
formatter = new Intl.NumberFormat(locale, {
5475
style: 'currency',
5576
currency,
5677
minimumFractionDigits,
78+
maximumFractionDigits,
5779
})
5880
formatterCache.set(cacheKey, formatter)
5981
}
6082
return formatter
6183
}
62-
63-
function getMaxDigits(currency: string): number {
64-
let maxDigits = maxDigitsCache.get(currency)
65-
if (!maxDigits) {
66-
try {
67-
const formatter = new Intl.NumberFormat(undefined, {
68-
style: 'currency',
69-
currency,
70-
})
71-
maxDigits = formatter.resolvedOptions().maximumFractionDigits ?? 2
72-
} catch {
73-
maxDigits = 2
74-
}
75-
maxDigitsCache.set(currency, maxDigits)
76-
}
77-
return maxDigits
78-
}

0 commit comments

Comments
 (0)