You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
v/src/glob/lib/curr_engine.ts

118 lines
5.3 KiB

import type { CurrInstance, CurrDefinition, CurrDisplay, CurrConfig, ConversionValue } from './curr_types.ts';
import type { LingInstance, LingNode } from '@/ling/lib/types.ts';
import type { LocaleResolver, GlobLocale } from './comn_types.ts';
import { resolveLocale } from './comn_engine.ts';
import { defaultISOLocale } from '@/ling/lib/consts.ts';
/**
* Crea un módulo de moneda con formateo y conversión.
*
* @param definitions - Mapa de monedas disponibles indexado por código ISO.
* @param ling - Instancia de ling, usada para la pluralización de nombres.
* @param opts - Configuración inicial (moneda activa, decimales, display).
* @param defaultLocale - Locale o resolver usado cuando `f()` no recibe locale explícito.
*/
export function createCurr<S extends LingNode>(
definitions : Record<string, CurrDefinition>,
ling : LingInstance<S>,
opts : CurrConfig = {},
defaultLocale: LocaleResolver = defaultISOLocale
): CurrInstance {
let selectedCurrency: string = opts.selectedCurrency ?? Object.keys(definitions)[0] ?? '';
const baseCurrency : string = opts.baseCurrency ?? selectedCurrency;
const defaults = {
display : opts.display ?? 'symbol' as CurrDisplay,
decimals : opts.decimals ?? 2,
};
// ── Currency ──────────────────────────────────────────────────────────────
function getCurrency(): string { return selectedCurrency; }
function setCurrency(currency: string): void { selectedCurrency = currency.toUpperCase(); }
/** Busca una definición por código ISO, insensible a mayúsculas. */
function get(currency: string): CurrDefinition | undefined {
return definitions[currency.toUpperCase()];
}
// ── Helpers ───────────────────────────────────────────────────────────────
function formatAmount(amount: number, decimals: number, locale: GlobLocale): string {
return new Intl.NumberFormat(locale, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
}).format(amount);
}
/** Resuelve el nombre plural de la moneda según `amount` y el idioma del locale. */
function resolveName(def: CurrDefinition, amount: number, locale: GlobLocale): string {
const record = def.name({ count: Math.abs(amount) });
const baseLocale = locale.split('-')[0] as keyof typeof record;
return record[baseLocale] ?? record['es'];
}
/** Normaliza `ConversionValue` a número o `null`. Evalúa la función si es dinámica. */
function resolveConversionValue(value: ConversionValue): number | null {
if (value === null || value === undefined) return null;
if (typeof value === 'number') return value;
return value();
}
// ── Format ────────────────────────────────────────────────────────────────
/**
* Formatea `amount` en la moneda activa.
* Si la moneda no está registrada, devuelve el importe seguido del código ISO.
*/
function f(
amount : number,
display : CurrDisplay = defaults.display,
locale : LocaleResolver = defaultLocale
): string {
const loc = resolveLocale(locale);
const formatted = formatAmount(amount, defaults.decimals, loc);
const def = get(selectedCurrency);
if (!def) return `${formatted} ${selectedCurrency}`;
switch (display) {
case 'symbol': return `${formatted} ${def.symbol}`;
case 'code': return `${formatted} ${def.code}`;
case 'name': return `${formatted} ${resolveName(def, amount, loc)}`;
}
}
// ── Conversión ────────────────────────────────────────────────────────────
/**
* Convierte `amount` de `from` a `to` usando sus tasas respecto a la moneda base.
* Fórmula: `(amount / fromRate) * toRate`
* Devuelve `null` si alguna moneda no tiene tasa de conversión.
*/
function convert(amount: number, from: string, to: string): number | null {
if (from.toUpperCase() === to.toUpperCase()) return amount;
const fromDef = get(from);
const toDef = get(to);
if (!fromDef || !toDef) return null;
const fromRate = resolveConversionValue(fromDef.conversionValue ?? null);
const toRate = resolveConversionValue(toDef.conversionValue ?? null);
if (fromRate === null || toRate === null) return null;
// Convertir a moneda base y luego a destino
const inBase = amount / fromRate;
return inBase * toRate;
}
/** Convierte desde la moneda activa (`selectedCurrency`) hacia `to`. */
function convertTo(amount: number, to: string): number | null {
return convert(amount, selectedCurrency, to);
}
return { f, convert, convertTo, setCurrency, getCurrency, get };
}

Powered by TurnKey Linux.