my website at ewancroft.uk
at main 58 lines 2.1 kB view raw
1/** 2 * Number formatting utilities 3 */ 4 5/** 6 * Determines the effective locale, preferring system locale with fallback to 'en-GB'. 7 */ 8function getLocale(locale?: string): string { 9 return locale || (typeof navigator !== 'undefined' && navigator.language) || 'en-GB'; 10} 11 12/** 13 * Formats large numbers into a compact, human-readable format. 14 * Automatically adapts to the given or system locale. 15 * Numbers are rounded DOWN to ensure stats don't appear inflated. 16 * @param num - The number to format 17 * @param locale - Optional locale string (defaults to system or 'en-GB') 18 * @returns Formatted string (e.g., "1.2K", "3.4M") 19 */ 20export function formatCompactNumber(num?: number, locale?: string): string { 21 if (num === undefined || num === null) return '0'; 22 const effectiveLocale = getLocale(locale); 23 24 // For numbers >= 1000, round down to one decimal place for the compact format 25 if (num >= 1000) { 26 // Determine the divisor (1000 for K, 1000000 for M, etc.) 27 const divisor = num >= 1000000000 ? 1000000000 : num >= 1000000 ? 1000000 : 1000; 28 // Floor to one decimal place: floor(num / divisor * 10) / 10 29 const roundedDown = Math.floor((num / divisor) * 10) / 10; 30 // Re-multiply to get the actual number to format 31 const adjustedNum = roundedDown * divisor; 32 33 return new Intl.NumberFormat(effectiveLocale, { 34 notation: 'compact', 35 compactDisplay: 'short', 36 maximumFractionDigits: 1 37 }).format(adjustedNum); 38 } 39 40 // For numbers < 1000, just return as-is 41 return new Intl.NumberFormat(effectiveLocale, { 42 notation: 'compact', 43 compactDisplay: 'short', 44 maximumFractionDigits: 1 45 }).format(num); 46} 47 48/** 49 * Formats a number with thousand separators. 50 * Automatically adapts to the given or system locale. 51 * @param num - The number to format 52 * @param locale - Optional locale string (defaults to system or 'en-GB') 53 * @returns Formatted string (e.g., "1,234,567") 54 */ 55export function formatNumber(num: number, locale?: string): string { 56 const effectiveLocale = getLocale(locale); 57 return new Intl.NumberFormat(effectiveLocale).format(num); 58}