JSPM

@jamx-framework/i18n

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q66404F
  • License MIT

JAMX Framework — Internationalization

Package Exports

  • @jamx-framework/i18n

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:

  1. I18n: Clase principal que gestiona el idioma actual, las traducciones cargadas y la interpolación
  2. Loaders: Cargadores de traducciones desde diferentes fuentes (JSON, API, etc.)
  3. Resolvers: Estrategias para detectar el idioma preferido del usuario (Accept-Language, cookie, query param)
  4. Interpolación: Reemplazo de variables en mensajes con formato y pluralización

Componentes principales

  • src/i18n.ts: Clase I18n que 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.json

en.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

  1. Inicialización: Se crea I18n con loaders, resolvers y configuración
  2. Detección: Los resolvers examinan la request (headers, cookies, query) para determinar el idioma
  3. Carga: Los loaders cargan traducciones desde archivos o APIs
  4. Cache: Las traducciones se cachean en memoria para acceso rápido
  5. Interpolación: t() reemplaza variables y aplica pluralización
  6. Formateo: Funciones auxiliares para fechas, números, monedas
  7. 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): void
  • getLocale(): string
  • t(key: string, params?: Record<string, any>): string
  • has(key: string): boolean
  • isLoaded(locale: string): boolean
  • formatDate(date: Date, options?: Intl.DateTimeFormatOptions): string
  • formatNumber(value: number, options?: Intl.NumberFormatOptions): string
  • onLocaleChange(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.locale y req.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 test

Cubre:

  • 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 fuente
  • jamx i18n:compile: Compila archivos de traducción a formato optimizado
  • jamx i18n:add <locale>: Añade nuevo idioma
  • jamx i18n:missing: Lista claves faltantes por idioma

Best Practices

  1. Usar keys descriptivas: auth.login.title en lugar de login_title
  2. Mantener estructura plana: Evitar anidación profunda en JSON
  3. Siempre tener fallback: Configurar fallbackLocale para claves faltantes
  4. Extraer claves automáticamente: Usar jamx i18n:extract para encontrar claves no traducidas
  5. Validar traducciones: Revisar que todas las claves existan en cada idioma
  6. Usar pluralización correcta: Definir formas singular y plural para cada clave
  7. 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.