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);
+}