diff --git a/src/app/solve/page.tsx b/src/app/solve/page.tsx index c4d9a31..1369de5 100644 --- a/src/app/solve/page.tsx +++ b/src/app/solve/page.tsx @@ -8,10 +8,9 @@ import { useAcceptIntent } from "@/hooks/useAcceptIntent"; import { useSolverRegistration } from "@/hooks/useSolverRegistration"; import { timeRemaining } from "@/lib/time"; import { isValidStellarPublicKey } from "@/lib/stellarAddress"; +import { formatCurrency } from "@/lib/format"; -const usdCompact = new Intl.NumberFormat("en-US", { - style: "currency", - currency: "USD", +const usdCompact = (value: number) => formatCurrency(value, undefined, { notation: "compact", maximumFractionDigits: 1, }); @@ -163,6 +162,8 @@ export default function SolvePage() {
Fills
+
{usdCompact(s.volumeUsd)}
+
Volume
{usdCompact.format(s.volumeUsd)}
Volume
diff --git a/src/components/SwapCard.tsx b/src/components/SwapCard.tsx index 5592b95..eb36bbe 100644 --- a/src/components/SwapCard.tsx +++ b/src/components/SwapCard.tsx @@ -5,6 +5,7 @@ import { useQuote } from "@/hooks/useQuote"; import { useDebouncedValue } from "@/hooks/useDebouncedValue"; import { useSwapSubmission } from "@/hooks/useSwapSubmission"; import { CHAINS, SRC_TOKENS, DST_TOKENS } from "@/lib/marketData"; +import { formatCurrency, formatTokenAmount } from "@/lib/format"; import { useTranslation } from "@/lib/i18n/I18nProvider"; import type { MessageKey } from "@/lib/i18n"; @@ -150,6 +151,8 @@ export function SwapCard() { className={`w-full flex items-center justify-between px-3 py-2 rounded-lg text-sm transition-colors ${token.symbol === srcToken.symbol ? "bg-vx-lav-bg text-vx-lav" : "hover:bg-vx-surface text-vx-muted hover:text-vx-text"}`} > + {t.symbol} + {formatCurrency(t.priceUSD)} {token.symbol} ${token.priceUSD.toLocaleString()} @@ -159,6 +162,7 @@ export function SwapCard() { {srcValueUSD > 0 && (
+ ≈ {formatCurrency(srcValueUSD, undefined, { maximumFractionDigits: 2 })} {/* Number formatting stays locale-hardcoded here; issue #63 owns making it locale-aware. */} {t("swap.from.approxValue", { value: srcValueUSD.toLocaleString("en-US", { maximumFractionDigits: 2 }), @@ -197,7 +201,11 @@ export function SwapCard() {
) : (
- {dstAmount > 0 ? dstAmount.toFixed(dstToken.symbol === "XLM" ? 2 : 4) : "0"} + {dstAmount > 0 + ? formatTokenAmount(dstAmount, undefined, { + maximumFractionDigits: dstToken.symbol === "XLM" ? 2 : 4, + }) + : "0"}
)} diff --git a/src/lib/format.test.ts b/src/lib/format.test.ts new file mode 100644 index 0000000..5333cc2 --- /dev/null +++ b/src/lib/format.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; +import { formatCurrency, formatTokenAmount } from "./format"; + +describe("formatCurrency", () => { + it("formats USD values for en-US locale", () => { + expect(formatCurrency(1234.5, "en-US")).toBe("$1,234.50"); + }); + + it("formats USD values for de-DE locale", () => { + expect(formatCurrency(1234.5, "de-DE")).toBe("1.234,50 $"); + }); +}); + +describe("formatTokenAmount", () => { + it("formats token amounts for en-US locale", () => { + expect(formatTokenAmount(1234.5678, "en-US", { maximumFractionDigits: 4 })).toBe("1,234.5678"); + }); + + it("formats token amounts for de-DE locale", () => { + expect(formatTokenAmount(1234.5678, "de-DE", { maximumFractionDigits: 4 })).toBe("1.234,5678"); + }); +}); diff --git a/src/lib/format.ts b/src/lib/format.ts new file mode 100644 index 0000000..61d576e --- /dev/null +++ b/src/lib/format.ts @@ -0,0 +1,23 @@ +const DEFAULT_LOCALE = "en-US"; + +function resolveLocale(locale?: string): string { + if (locale) return locale; + + if (typeof navigator !== "undefined" && navigator.language) { + return navigator.language; + } + + return DEFAULT_LOCALE; +} + +export function formatCurrency(value: number, locale?: string, options: Intl.NumberFormatOptions = {}) { + return new Intl.NumberFormat(resolveLocale(locale), { + style: "currency", + currency: "USD", + ...options, + }).format(value); +} + +export function formatTokenAmount(value: number, locale?: string, options: Intl.NumberFormatOptions = {}) { + return new Intl.NumberFormat(resolveLocale(locale), options).format(value); +}