@jamx-framework/i18n
v1.0.0
Published
JAMX Framework — Internationalization
Maintainers
Readme
@jamx-framework/i18n
Descripción
Módulo de internacionalización (i18n) para JAMX Framework. Proporciona una API completa para gestionar múltiples idiomas en aplicaciones, incluyendo carga de traducciones, detección automática del idioma del usuario, interpolación de variables y formateo de mensajes. Soporta múltiples estrategias de detección de idioma (headers, cookies, query params) y es completamente type-safe.
Cómo funciona
El módulo implementa un sistema de traducción modular:
- I18n: Clase principal que gestiona el idioma actual, las traducciones cargadas y la interpolación
- Loaders: Cargadores de traducciones desde diferentes fuentes (JSON, API, etc.)
- Resolvers: Estrategias para detectar el idioma preferido del usuario (Accept-Language, cookie, query param)
- Interpolación: Reemplazo de variables en mensajes con formato y pluralización
Componentes principales
- src/i18n.ts: Clase
I18nque gestiona traducciones y cambio de idioma - src/interpolate.ts: Funciones de interpolación y formateo
- src/loaders/json-loader.ts: Cargador de traducciones desde archivos JSON
- src/resolvers/accept-language.ts: Detector por header Accept-Language
- src/resolvers/cookie.ts: Detector por cookie
- src/resolvers/header.ts: Detector por header personalizado
- src/resolvers/query.ts: Detector por query parameter
- src/types.ts: Tipos compartidos (Translation, Locale, etc.)
- src/index.ts: Punto de exportación
Uso básico
import { I18n, JsonLoader, AcceptLanguageResolver } from '@jamx-framework/i18n';
// 1. Crear instancia de I18n
const i18n = new I18n({
defaultLocale: 'en',
fallbackLocale: 'en',
loaders: [new JsonLoader('./locales')],
resolvers: [new AcceptLanguageResolver()],
});
// 2. Cargar traducciones
await i18n.load('en', { welcome: 'Welcome' });
await i18n.load('es', { welcome: 'Bienvenido' });
// 3. Establecer idioma
i18n.setLocale('es');
// 4. Traducir
console.log(i18n.t('welcome')); // 'Bienvenido'
// 5. Interpolar variables
console.log(i18n.t('greeting', { name: 'Juan' })); // 'Hello, Juan' (en) / 'Hola, Juan' (es)Ejemplos
Estructura de archivos de traducción
locales/
en.json
es.json
fr.jsonen.json:
{
"welcome": "Welcome, {{name}}!",
"items": "{{count}} item",
"items_plural": "{{count}} items"
}es.json:
{
"welcome": "¡Bienvenido, {{name}}!",
"items": "{{count}} artículo",
"items_plural": "{{count}} artículos"
}Uso con interpolación y pluralización
// Interpolación simple
i18n.t('welcome', { name: 'Ana' }); // '¡Bienvenido, Ana!'
// Pluralización
i18n.t('items', { count: 1 }); // '1 artículo' (es)
i18n.t('items', { count: 5 }); // '5 artículos' (es)
// Formato de fechas y números
i18n.formatDate(new Date(), { locale: 'es' }); // '15/03/2024'
i18n.formatNumber(1234.56, { style: 'currency', currency: 'EUR' }); // '1.234,56 €'Detección automática de idioma
// Con Accept-Language header
const resolver = new AcceptLanguageResolver();
const locale = await resolver.resolve(req.headers['accept-language']);
// locale = 'es-ES,es;q=0.9,en;q=0.8'
// Con cookie
const cookieResolver = new CookieResolver('locale');
const locale = await cookieResolver.resolve(req.headers.cookie);
// locale = 'es'
// Con query param
const queryResolver = new QueryResolver('lang');
const locale = await queryResolver.resolve(req.query);
// locale = 'en'Middleware para Express/JAMX Server
import { I18nMiddleware } from '@jamx-framework/i18n';
const i18nMiddleware = I18nMiddleware({
defaultLocale: 'en',
loaders: [new JsonLoader('./locales')],
resolvers: [new AcceptLanguageResolver(), new CookieResolver('locale')],
});
server.use(i18nMiddleware);
// En el handler:
server.get('/hello', (req, res) => {
const locale = req.locals.locale; // 'es', 'en', etc.
const t = req.locals.t; // función de traducción
res.json({ message: t('welcome', { name: 'User' }) });
});Carga perezosa (lazy) de traducciones
// Cargar solo cuando se necesite
await i18n.loadIfNeeded('fr'); // Descarga fr.json si no está cargado
// Verificar si un idioma está cargado
if (i18n.isLoaded('de')) {
console.log('Traducciones alemanas disponibles');
}Namespaces (módulos de traducción)
// Cargar traducciones por namespace
await i18n.load('en', {
auth: { login: 'Login', logout: 'Logout' },
dashboard: { welcome: 'Welcome to dashboard' },
});
// Usar namespace
i18n.t('auth.login'); // 'Login'
i18n.t('dashboard.welcome'); // 'Welcome to dashboard'Cambio de idioma dinámico
// Cambiar idioma en runtime
i18n.setLocale('fr');
// Escuchar cambios de idioma
i18n.onLocaleChange((newLocale) => {
console.log(`Idioma cambiado a: ${newLocale}`);
// Actualizar UI, recargar componentes, etc.
});Flujo interno
- Inicialización: Se crea
I18ncon loaders, resolvers y configuración - Detección: Los resolvers examinan la request (headers, cookies, query) para determinar el idioma
- Carga: Los loaders cargan traducciones desde archivos o APIs
- Cache: Las traducciones se cachean en memoria para acceso rápido
- Interpolación:
t()reemplaza variables y aplica pluralización - Formateo: Funciones auxiliares para fechas, números, monedas
- Fallback: Si una clave no existe, se busca en el idioma fallback
API Reference (Resumen)
I18n
constructor(config: I18nConfig)async load(locale: string, translations: Translation | Promise<Translation>): Promise<void>async loadIfNeeded(locale: string): Promise<boolean>setLocale(locale: string): voidgetLocale(): stringt(key: string, params?: Record<string, any>): stringhas(key: string): booleanisLoaded(locale: string): booleanformatDate(date: Date, options?: Intl.DateTimeFormatOptions): stringformatNumber(value: number, options?: Intl.NumberFormatOptions): stringonLocaleChange(callback: (locale: string) => void): () => void
Loaders
JsonLoader(basePath: string)ApiLoader(endpoint: string)FileLoader(path: string)
Resolvers
AcceptLanguageResolver()CookieResolver(cookieName: string)HeaderResolver(headerName: string)QueryResolver(paramName: string)
I18nMiddleware
I18nMiddleware(config): Middleware- Añade
req.locals.localeyreq.locals.t
Performance Considerations
- Lazy loading: Las traducciones se cargan solo cuando se necesitan
- Caching: Las traducciones se mantienen en memoria después de cargar
- Tree-shaking: Solo se incluyen los idiomas usados en el build
- Compression: Los archivos JSON de traducción pueden comprimirse
Configuration
const i18n = new I18n({
defaultLocale: 'en',
fallbackLocale: 'en',
loaders: [new JsonLoader('./locales')],
resolvers: [new AcceptLanguageResolver()],
interpolation: {
prefix: '{{',
suffix: '}}',
},
missingKeyHandler: (key, locale) => {
console.warn(`Missing translation: ${key} in ${locale}`);
return key; // devolver la clave como fallback
},
});Testing
Tests en packages/i18n/tests/unit/:
pnpm testCubre:
- Carga de traducciones desde JSON
- Detección de idioma por headers, cookies, query
- Interpolación de variables
- Pluralización
- Formateo de fechas y números
- Fallbacks y missing keys
Compatibility
- Compatible con Node.js 18+ y navegadores
- Funciona con ICU MessageFormat para pluralización
- Soporta todos los idiomas de Unicode
- No requiere dependencias nativas
CLI Integration
jamx i18n:extract: Extrae claves de traducción del código fuentejamx i18n:compile: Compila archivos de traducción a formato optimizadojamx i18n:add <locale>: Añade nuevo idiomajamx i18n:missing: Lista claves faltantes por idioma
Best Practices
- Usar keys descriptivas:
auth.login.titleen lugar delogin_title - Mantener estructura plana: Evitar anidación profunda en JSON
- Siempre tener fallback: Configurar
fallbackLocalepara claves faltantes - Extraer claves automáticamente: Usar
jamx i18n:extractpara encontrar claves no traducidas - Validar traducciones: Revisar que todas las claves existan en cada idioma
- Usar pluralización correcta: Definir formas singular y plural para cada clave
- No concatenar strings: Usar interpolación en su lugar
This i18n module provides a robust, type-safe internationalization solution for JAMX applications, enabling developers to reach global audiences with proper language support, formatting, and cultural adaptation.
