fix: неоднозначные символы валют (CN¥, HK$) без огрубления остальных

currencyDisplay: narrowSymbol вместо code — чинит CNY/HKD, но не
превращает $/€/₽ в USD/EUR/RUB для всех остальных валют.
This commit is contained in:
ZProger
2026-09-09 00:32:22 +03:00
parent 8b87619e37
commit 526bfe104d

View File

@@ -5,16 +5,32 @@ export function getIntlLocale(language: Language = getLanguage()): string {
return language === "ru" ? "ru-RU" : "en-US"; return language === "ru" ? "ru-RU" : "en-US";
} }
/** Builds the currency formatter every money helper here shares, so amounts
* are labelled identically everywhere in the app.
*
* `currencyDisplay: "narrowSymbol"` avoids Intl's default "symbol" mode,
* which prefixes symbols it considers ambiguous with a region marker — CNY
* renders as "CN¥", HKD as "HK$" (both are in lib/currency.ts's supported
* list) — reading like a typo next to the amount. narrowSymbol drops that
* prefix (plain "¥", "$") while leaving every unambiguous currency (USD,
* EUR, RUB, ...) exactly as compact as plain "symbol" already renders them
* — unlike currencyDisplay: "code", which would fix CNY/HKD but turn every
* other currency's "$1,234"/"1 234 ₽" into "USD 1,234"/"1 234 RUB". */
function createCurrencyFormatter(currency: string, maximumFractionDigits: number): Intl.NumberFormat {
return new Intl.NumberFormat(getIntlLocale(), {
style: "currency",
currency,
currencyDisplay: "narrowSymbol",
maximumFractionDigits,
});
}
// `currency` defaults to the app's primary currency setting (Settings page) // `currency` defaults to the app's primary currency setting (Settings page)
// — call sites only need to pass it explicitly when formatting a value known // — call sites only need to pass it explicitly when formatting a value known
// to be in a *different* currency than that setting. // to be in a *different* currency than that setting.
export function formatCurrency(amount: number | string, currency: string = getCurrency()): string { export function formatCurrency(amount: number | string, currency: string = getCurrency()): string {
const value = typeof amount === "string" ? Number(amount) : amount; const value = typeof amount === "string" ? Number(amount) : amount;
return new Intl.NumberFormat(getIntlLocale(), { return createCurrencyFormatter(currency, 0).format(value);
style: "currency",
currency,
maximumFractionDigits: 0,
}).format(value);
} }
/** Same currency formatting as formatCurrency, but scales decimal precision /** Same currency formatting as formatCurrency, but scales decimal precision
@@ -37,7 +53,7 @@ export function formatCryptoAmount(amount: number | string, currency: string = g
// that — e.g. 0.000000006894 has 8 leading zeros, so this shows // that — e.g. 0.000000006894 has 8 leading zeros, so this shows
// 12 decimal places, landing exactly on "6894" and nothing more. // 12 decimal places, landing exactly on "6894" and nothing more.
Math.min(20, Math.max(0, -Math.floor(Math.log10(abs)) - 1) + 4); Math.min(20, Math.max(0, -Math.floor(Math.log10(abs)) - 1) + 4);
return new Intl.NumberFormat(getIntlLocale(), { style: "currency", currency, maximumFractionDigits }).format(value); return createCurrencyFormatter(currency, maximumFractionDigits).format(value);
} }
export function formatSignedCurrency(amount: number | string, currency: string = getCurrency()): string { export function formatSignedCurrency(amount: number | string, currency: string = getCurrency()): string {