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.
dev 6b5533c840
Add module prefix to all remaining ad-hoc constants
5 months ago
..
curr Replace LOGGER_CATEGORY with <MOD>_MODULE single source of truth 5 months ago
dates Replace LOGGER_CATEGORY with <MOD>_MODULE single source of truth 5 months ago
nums Add module prefix to all remaining ad-hoc constants 5 months ago
test Add module prefix to all remaining ad-hoc constants 5 months ago
unts Replace LOGGER_CATEGORY with <MOD>_MODULE single source of truth 5 months ago
README.md Expand formats documentation 5 months ago
active-formats.svelte.ts Consolidate framework diagnostics and refactors 5 months ago
active-runtime.svelte.ts Consolidate docs and format runtime cleanup 5 months ago
auto-state.ts Consolidate framework diagnostics and refactors 5 months ago
consts.ts Add module prefix to all remaining ad-hoc constants 5 months ago
engine-formats.ts Consolidate framework diagnostics and refactors 5 months ago
errors.ts Replace LOGGER_CATEGORY with <MOD>_MODULE single source of truth 5 months ago
helpers.ts Add module prefix to all remaining ad-hoc constants 5 months ago
index.ts Add module prefix to all remaining ad-hoc constants 5 months ago
locale-state.ts Add module prefix to all remaining ad-hoc constants 5 months ago
types.ts Build runtime infrastructure layer: aapp + 5 new artifacts 6 months ago

README.md

Formats

Formats es la API publica del artefacto fmts. Su responsabilidad es centralizar todos los formatos que dependen de locale: numeros, moneda, unidades y fechas. No traduce textos, no decide idioma y no depende de Lang; solo consume una fuente de locale cuando se quiere reactividad.

El objetivo es que una app tenga una sola verdad:

App.setLocale('es-AR');

App.Formats.numbers.format(1234.5);
App.Formats.currency.getCurrency(); // ARS
App.Formats.units.getSystem(); // metric
App.Formats.dates.getDateOrder();

Mapa del modulo

Pieza Factory Responsabilidad
formats createEngineFormats, createActiveFormats Agrega numbers, currency, units y dates bajo un mismo locale.
numbers createEngineNumbers, createActiveNumbers Formato y parseo numerico, separadores, porcentajes, compact, currency/unit via Intl.NumberFormat.
currency createEngineCurrency, createActiveCurrency Moneda por region, formato de moneda, conversion y cache de rates.
units createEngineUnits, createActiveUnits Sistema metrico/imperial, unidades por defecto y conversiones.
dates createEngineDates, createActiveDates Orden de fecha, ciclo horario y formato fecha/hora.

Todos los submodulos siguen el mismo patron:

  • Engine* es puro y no depende de Svelte.
  • Active* envuelve el engine con reactividad Svelte 5.
  • locale puede ser string o funcion en engines.
  • localeSource es la entrada reactiva en active wrappers.
  • dispose() existe aunque hoy casi todo sea puro, para mantener el contrato comun.

Uso rapido

import { createEngineFormats } from '$fmts';

const formats = createEngineFormats({ locale: 'es-ES' });

formats.numbers.format(1234.5); // "1234,5" segun Intl del runtime
formats.currency.format(12.5); // "12,50 EUR"
formats.units.formatDefault(20, 'distance'); // "20 km"
formats.dates.formatDateTime(new Date());

En una app Svelte:

import { createActiveFormats } from '$fmts';

const Formats = createActiveFormats({
	locale: 'en-US'
});

Formats.setLocale('fr-FR');
Formats.currency.format(99.5);

Bajo createActiveApp(...) normalmente no se crea a mano: App.Formats recibe el localeSource de la app y se mueve con App.setLocale(...).

LocaleSource

Formats consume el alias compartido LocaleSource de $locale:

import type { LocaleSource } from '$locale';

interface LocaleSource<L extends string = string> {
	getLocale: () => L;
	onLocaleChange?: (fn: (locale: L) => void) => () => void;
}

createActiveFormats({ localeSource }) suscribe esa fuente y propaga los cambios a todos los submotores. Si el source no tiene onLocaleChange, el engine puede leer getLocale() cuando se formatea, pero no recibe notificaciones activas.

Locale por defecto

Cuando no se pasa locale ni localeSource, se usa DEFAULT_LOCALE = 'en-US'. Esto evita depender del locale implicito del runtime, que puede variar entre navegador, servidor, tests y CI.

Si quieres que el default de proyecto sea otro, pasalo en el composition root:

const App = createActiveApp({
	formats: { locale: 'es-ES' }
});

Regla Auto / Manual

Todos los valores derivados siguen la misma dinamica:

setX('auto'); // vuelve a derivar desde locale/Intl/framework
clearX(); // equivalente semantico de volver a auto
isXAuto(); // true si el usuario no fijo un valor manual

Un valor explicito siempre gana sobre el locale. Si el usuario fija currency, system, hourCycle, dateOrder o separadores numericos, cambiar el locale no debe mover ese valor hasta que vuelva a auto.

Modulo Valor derivado Deriva de Fijar Volver a auto
currency moneda region del locale setCurrency('EUR') setCurrency('auto') / clearCurrency()
units sistema metrico/imperial region del locale setSystem('metric') setSystem('auto') / clearSystem()
dates dateOrder $libs/days + locale setDateOrder('YMD') setDateOrder('auto') / clearDateOrder()
dates hourCycle $libs/days + locale setHourCycle(12) setHourCycle('auto') / clearHourCycle()
numbers separadores y grouping Intl.NumberFormat setDecimalSeparator(',') setDecimalSeparator('auto') / clearDecimalSeparator()

Ejemplo:

formats.currency.setCurrency('EUR');
formats.setLocale('en-US');
formats.currency.getCurrency(); // EUR: valor fijado por usuario

formats.currency.clearCurrency();
formats.currency.getCurrency(); // USD: vuelve a derivar del locale

Numbers

numbers es la base para el resto cuando se inyecta en currency o units.

formats.numbers.format(1234567.89);
formats.numbers.formatPercent(0.42);
formats.numbers.formatCompact(1200000);
formats.numbers.formatCurrency(99.5, 'EUR');
formats.numbers.formatUnit(20, 'kilometer');

formats.numbers.parse('1.234,50');
formats.numbers.getDecimalSeparator();
formats.numbers.getGroupSeparator();

Los separadores pueden quedar en auto o ser preferencias de usuario:

formats.numbers.setDecimalSeparator(',');
formats.numbers.clearDecimalSeparator();

Currency

currency resuelve moneda desde la region explicita del locale. No hace fallback por idioma porque eso produce errores como tratar es-AR como es-ES.

formats.setLocale('es-AR');
formats.currency.getCurrency(); // ARS

formats.setLocale('es');
formats.currency.getCurrency(); // defaultCurrency, porque no hay region

Para conversiones se inyecta un provider de rates:

import { createRates } from '$fmts/curr';

const rates = createRates({
	initial: {
		base: 'EUR',
		rates: { USD: 1.08, GBP: 0.86 }
	}
});

const formats = createEngineFormats({
	locale: 'es-ES',
	currency: { rates }
});

await formats.currency.convert(10, 'USD');

Si falta un provider o un rate, currency puede emitir diagnosticos por el logger inyectado. Las categorias y mensajes viven en constantes del submodulo.

Units

units resuelve sistema por region y marca unidades por defecto en unts/unit-definitions.ts.

formats.units.getSystem(); // metric | imperial
formats.units.getDefaultUnit('distance'); // kilometer | mile
formats.units.isDefaultUnit('mile', 'distance', 'imperial');
formats.units.formatDefault(20, 'distance');
formats.units.convert(1, 'mile', 'kilometer');

El sistema tambien respeta auto/manual:

formats.units.setSystem('imperial');
formats.setLocale('es-ES');
formats.units.getSystem(); // imperial

formats.units.clearSystem();
formats.units.getSystem(); // metric

Dates

dates usa $libs/days como base pura y fmts/dates como capa de engine/configuracion.

formats.dates.getDateOrder();
formats.dates.getHourCycle();
formats.dates.formatDate(new Date());
formats.dates.formatTime(new Date());
formats.dates.formatDateTime(new Date());

getHourCycle() deriva del locale a traves de las utilidades de days; si el usuario fija setHourCycle(12) o setHourCycle(24), esa preferencia queda bloqueada hasta clearHourCycle().

Active listeners

Los wrappers activos exponen listeners para que una UI o un modulo superior pueda reaccionar sin inspeccionar internals:

const offLocale = Formats.numbers.onLocaleChange((locale) => {
	console.log('locale changed', locale);
});

const offPrefs = Formats.dates.onPreferenceChange(() => {
	console.log('date preferences changed');
});

const offCurrency = Formats.currency.onCurrencyChange((currency) => {
	console.log('currency changed', currency);
});

dispose() limpia estas suscripciones.

Integracion recomendada

En aapp, Formats debe compartir locale con Lang y Frontend:

const App = createActiveApp({
	lang: { locale: 'es-ES', schema },
	formats: {
		currency: { defaultCurrency: 'EUR' }
	}
});

App.setLocale('ar');
App.Formats.dates.formatDate(new Date());

Si un modulo necesita solo formatos sin toda la app, usa el engine aislado:

const formats = createEngineFormats({
	locale: () => requestLocale
});

Testing

Tests utiles:

  • npx vitest run src/arts/fmts
  • /test/fmts para validar locale compartido, auto/manual y UI.
  • /test/ecosystem para validar Lang + Formats + Frontend + Dom juntos.

Powered by TurnKey Linux.