Impove Intl formatting (#5372)
* Improve Intl formatting * Additional fixes * Fixed formatters were not updated on locale change * Fixed formatNumber was not updated on locale change * Additional formatting and fixes after merge * Run prepr:frontend * Remove `'` in icon map * Run `pnpm install` * fix: lint + import * Additional fixes --------- Co-authored-by: Calum H. <calum@modrinth.com> Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
This commit is contained in:
74
packages/ui/src/composables/format-number.ts
Normal file
74
packages/ui/src/composables/format-number.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { LRUCache } from 'lru-cache'
|
||||
|
||||
import { injectI18n } from '../providers/i18n'
|
||||
|
||||
const formatterCache = new LRUCache<string, Intl.NumberFormat>({ max: 15 })
|
||||
|
||||
// `formatNumber(1234567)` → `1,234,567`
|
||||
export function useFormatNumber() {
|
||||
const { locale } = injectI18n()
|
||||
|
||||
function format(value: number | bigint): string {
|
||||
const formatter = getStandardFormatter(locale.value)
|
||||
return formatter!.format(value)
|
||||
}
|
||||
|
||||
return format
|
||||
}
|
||||
|
||||
// `formatCompactNumber(1234567)` → `1.23M`
|
||||
//
|
||||
// Use `formatCompactNumberPlural` over `{(here!), plural, one {...} other {...}}`
|
||||
export function useCompactNumber() {
|
||||
const { locale } = injectI18n()
|
||||
|
||||
function formatCompactNumber(value: number | bigint): string {
|
||||
if (value < 10_000) {
|
||||
const standardFormatter = getStandardFormatter(locale.value)
|
||||
return standardFormatter.format(value)
|
||||
}
|
||||
if (value < 1_000_000) {
|
||||
const oneDigitCompactFormatter = getCompactFormatter(locale.value, 1)
|
||||
return oneDigitCompactFormatter.format(value)
|
||||
}
|
||||
const twoDigitsCompactFormatter = getCompactFormatter(locale.value, 2)
|
||||
return twoDigitsCompactFormatter.format(value)
|
||||
}
|
||||
|
||||
function formatCompactNumberPlural(value: number | bigint): string {
|
||||
if (value < 10_000) {
|
||||
return value.toString()
|
||||
}
|
||||
if (value < 1_000_000) {
|
||||
const oneDigitCompactFormatter = getCompactFormatter(locale.value, 1)
|
||||
return oneDigitCompactFormatter.format(value)
|
||||
}
|
||||
const twoDigitsCompactFormatter = getCompactFormatter(locale.value, 2)
|
||||
return twoDigitsCompactFormatter.format(value)
|
||||
}
|
||||
|
||||
return { formatCompactNumber, formatCompactNumberPlural }
|
||||
}
|
||||
|
||||
function getStandardFormatter(locale: string): Intl.NumberFormat {
|
||||
const cacheKey = `${locale}:standard`
|
||||
let formatter = formatterCache.get(cacheKey)
|
||||
if (!formatter) {
|
||||
formatter = new Intl.NumberFormat(locale)
|
||||
formatterCache.set(cacheKey, formatter)
|
||||
}
|
||||
return formatter
|
||||
}
|
||||
|
||||
function getCompactFormatter(locale: string, maximumFractionDigits: number): Intl.NumberFormat {
|
||||
const cacheKey = `${locale}:compact:${maximumFractionDigits}`
|
||||
let formatter = formatterCache.get(cacheKey)
|
||||
if (!formatter) {
|
||||
formatter = new Intl.NumberFormat(locale, {
|
||||
notation: 'compact',
|
||||
maximumFractionDigits,
|
||||
})
|
||||
formatterCache.set(cacheKey, formatter)
|
||||
}
|
||||
return formatter
|
||||
}
|
||||
Reference in New Issue
Block a user