/** * Locale-aware formatting helpers (Intl-based) + pluralization. Pair with the * request language (`ctx.lang`) so numbers, dates, and currencies render right * for each user. */ /** Format a number for a locale (e.g. 1234.5 → "1,234.5"). */ export function formatNumber( value: number, lang: string, options?: Intl.NumberFormatOptions, ): string { return new Intl.NumberFormat(lang || undefined, options).format(value); } /** Format a currency amount (e.g. 9.99, "USD" → "$9.99"). */ export function formatCurrency(value: number, currency: string, lang: string): string { return new Intl.NumberFormat(lang || undefined, { style: "currency", currency }).format(value); } /** Format a date/timestamp for a locale. */ export function formatDate( value: Date | number | string, lang: string, options: Intl.DateTimeFormatOptions = { dateStyle: "medium" }, ): string { const date = value instanceof Date ? value : new Date(value); return new Intl.DateTimeFormat(lang || undefined, options).format(date); } /** Relative time, e.g. -3 days → "3 days ago" (localized). */ export function formatRelativeTime( value: number, unit: Intl.RelativeTimeFormatUnit, lang: string, ): string { return new Intl.RelativeTimeFormat(lang || undefined, { numeric: "auto" }).format(value, unit); } /** * Pick a plural form for `count` in `lang` using CLDR rules, e.g. * `plural(n, { one: "1 item", other: "# items" }, lang)` — "#" is replaced by n. */ export function plural( count: number, forms: Partial>, lang: string, ): string { const rule = new Intl.PluralRules(lang || undefined).select(count); const template = forms[rule] ?? forms.other ?? ""; return template.replace(/#/g, String(count)); }