export type WeightUnit = "g" | "oz" | "lb" | "kg"; const GRAMS_PER_OZ = 28.3495; const GRAMS_PER_LB = 453.592; const GRAMS_PER_KG = 1000; export function formatWeight( grams: number | null | undefined, unit: WeightUnit = "g", locale = "en", ): string { if (grams == null) return "--"; let value: number; let fractionDigits: number; switch (unit) { case "g": value = Math.round(grams); fractionDigits = 0; break; case "oz": value = grams / GRAMS_PER_OZ; fractionDigits = 1; break; case "lb": value = grams / GRAMS_PER_LB; fractionDigits = 2; break; case "kg": value = grams / GRAMS_PER_KG; fractionDigits = 2; break; } const formatted = new Intl.NumberFormat(locale, { minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits, }).format(value); return unit === "g" ? `${formatted}g` : `${formatted} ${unit}`; } export type Currency = "USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"; export function formatPrice( cents: number | null | undefined, currency: Currency = "USD", locale = "en", ): string { if (cents == null) return "--"; return new Intl.NumberFormat(locale, { style: "currency", currency, minimumFractionDigits: currency === "JPY" ? 0 : 2, maximumFractionDigits: currency === "JPY" ? 0 : 2, }).format(cents / 100); } export interface DualPriceOptions { sourceCents: number; sourceCurrency: Currency; targetCurrency: Currency; convertedCents: number; locale?: string; } /** * Format a price with dual display: source price + converted in parentheses. * Per D-14: "€2,000 (~£1,720)" — source prominent, converted approximate. */ export function formatDualPrice(options: DualPriceOptions): { source: string; converted: string; } { const locale = options.locale ?? "en"; const source = formatPrice( options.sourceCents, options.sourceCurrency, locale, ); const converted = `~${formatPrice(options.convertedCents, options.targetCurrency, locale)}`; return { source, converted }; }