/** * Vietnamese currency formatting utilities. * * Centralised formatter for all price displays across the platform. * Converts raw VND numbers into human-readable Vietnamese format: * 3,500,000,000 -> "3.5 ty" * 150,000,000 -> "150 trieu" * 800,000 -> "800.000" */ // --------------------------------------------------------------------------- // Core formatter // --------------------------------------------------------------------------- /** * Format a VND amount into compact Vietnamese notation. * * @example * formatPrice(3_500_000_000) // "3.5 ty" * formatPrice(150_000_000) // "150 trieu" * formatPrice(1_500_000) // "1.5 trieu" * formatPrice(800_000) // "800.000" * formatPrice("3500000000") // "3.5 ty" (string input accepted) */ export function formatPrice(amount: string | number): string { const num = typeof amount === 'string' ? Number(amount) : amount; if (!Number.isFinite(num) || num < 0) return '0'; if (num >= 1_000_000_000) { const billions = num / 1_000_000_000; return `${stripTrailingZero(billions.toFixed(1))} tỷ`; } if (num >= 1_000_000) { const millions = num / 1_000_000; return `${stripTrailingZero(millions.toFixed(1))} triệu`; } return num.toLocaleString('vi-VN'); } // --------------------------------------------------------------------------- // Variant: with currency suffix // --------------------------------------------------------------------------- /** * Format a VND amount with a " đ" currency suffix. * Returns "Miễn phí" for zero amounts. * * @example * formatVND(4_990_000) // "4.99 trieu d" * formatVND(0) // "Mien phi" */ export function formatVND(amount: string | number): string { const num = typeof amount === 'string' ? Number(amount) : amount; if (!Number.isFinite(num) || num < 0) return '0 đ'; if (num === 0) return 'Miễn phí'; if (num >= 1_000_000_000) { const billions = num / 1_000_000_000; return `${stripTrailingZero(billions.toFixed(1))} tỷ đ`; } if (num >= 1_000_000) { const millions = num / 1_000_000; return `${stripTrailingZero(millions.toFixed(1))} triệu đ`; } return num.toLocaleString('vi-VN') + ' đ'; } // --------------------------------------------------------------------------- // Variant: price per square metre // --------------------------------------------------------------------------- /** * Format a VND/m² value. * * @example * formatPricePerM2(50_500_000) // "50.5 tr/m²" * formatPricePerM2(500_000) // "500k/m²" */ export function formatPricePerM2(price: string | number): string { const num = typeof price === 'string' ? Number(price) : price; if (!Number.isFinite(num) || num < 0) return '0 đ/m²'; if (num >= 1_000_000_000) { const billions = num / 1_000_000_000; return `${stripTrailingZero(billions.toFixed(1))} tỷ/m²`; } if (num >= 1_000_000) { const millions = num / 1_000_000; return `${stripTrailingZero(millions.toFixed(1))} tr/m²`; } if (num >= 1_000) { return `${Math.round(num / 1_000)}k/m²`; } return `${num.toLocaleString('vi-VN')} đ/m²`; } // --------------------------------------------------------------------------- // Variant: full locale format (no compact notation) // --------------------------------------------------------------------------- /** * Format a VND amount as a full locale number with " đ" suffix. * Unlike formatVND, this never uses compact notation. * * @example * formatVNDFull(4_990_000) // "4.990.000 đ" * formatVNDFull(0) // "0 đ" */ export function formatVNDFull(amount: string | number): string { const num = typeof amount === 'string' ? Number(amount) : amount; if (!Number.isFinite(num) || num < 0) return '0 đ'; return new Intl.NumberFormat('vi-VN').format(num) + ' đ'; } // --------------------------------------------------------------------------- // Parser (reverse direction) // --------------------------------------------------------------------------- /** * Parse a formatted Vietnamese price string back into a number. * Returns null if the input cannot be parsed. */ export function parseVND(formatted: string): number | null { const cleaned = formatted.replace(/[^\d]/g, ''); if (cleaned === '') return null; return Number(cleaned); } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- /** Remove a trailing ".0" so "3.0 ty" becomes "3 ty". */ function stripTrailingZero(str: string): string { return str.replace(/\.0$/, ''); }