La internacionalización (i18n) es esencial para construir aplicaciones Angular globales. Esta guía te muestra cómo implementar i18next en Angular con carga de recursos adecuada, inicialización y patrones de uso.
i18next es uno de los frameworks de i18n más populares, ofreciendo potentes características y flexibilidad:
Instala i18next usando npm o yarn:
npm install i18next¡Eso es todo! i18next es una biblioteca independiente sin dependencias específicas de framework. No necesitas angular-i18next ni ningún otro wrapper - solo usa i18next directamente en tu aplicación Angular.
Hay dos enfoques principales para cargar recursos de traducción en Angular con i18next:
Usar importaciones dinámicas aprovecha la división de código de Webpack para cargar traducciones bajo demanda. Este enfoque es ideal para aplicaciones con muchos idiomas o archivos de traducción grandes.
Ventajas:
Ejemplo: Carga con Importaciones Dinámicas
private async loadLanguageResources(
lang: string,
): Promise<Record<string, Record<string, unknown>>> {
const languageResources: Record<string, Record<string, unknown>> = {};
for (const namespace of NAMESPACES) {
const translationModule = await this.loadJson(lang, namespace);
languageResources[namespace] = translationModule.default;
}
return languageResources;
}
private async loadJson(
language: string,
namespace: string,
): Promise<{ default: Record<string, unknown> }> {
// Webpack automatically code-splits this into separate chunks
return import(`../../i18n/${language}/${namespace}.json`);
}Este enfoque importa archivos de traducción como módulos ES, permitiendo que Webpack los divida automáticamente en fragmentos separados que se cargan bajo demanda.
El plugin backend HTTP carga traducciones a través de solicitudes HTTP desde un servidor o CDN. Esto es útil cuando quieres actualizar traducciones sin reconstruir tu aplicación.
Ventajas:
Desventajas:
Ejemplo: Carga con Backend HTTP
import HttpBackend from 'i18next-http-backend';
await init({
lng: initialLanguage,
fallbackLng: DEFAULT_LANGUAGE,
backend: {
loadPath: '/assets/i18n/{{lng}}/{{ns}}.json',
},
use: [HttpBackend],
ns: NAMESPACES,
defaultNS: DEFAULT_NAMESPACE,
});Este enfoque obtiene archivos de traducción de un servidor en tiempo de ejecución. Ten en cuenta que esto requiere configuración adicional para SSR.
Inicializar i18next correctamente es crucial para evitar errores en tiempo de ejecución y asegurar que las traducciones estén disponibles cuando tu aplicación se renderiza.
La función provideAppInitializer de Angular (introducida en Angular 18) asegura que i18next esté completamente inicializado antes de que la aplicación comience a renderizar. La función proporcionada se ejecuta durante el arranque de la aplicación, y la inicialización no se completa hasta que la Promesa se resuelve. Esto previene que las claves de traducción aparezcan en lugar del texto traducido.
Ejemplo: Configuración del Inicializador de la Aplicación
export const appConfig: ApplicationConfig = {
providers: [
// ... other providers
provideAppInitializer(initializeI18n),
// ... more providers
],
};Por qué esto es importante:
La función de inicialización maneja la carga de recursos y la configuración de los ajustes de i18next.
Ejemplo: Inicialización de i18next
public async initializeI18n(): Promise<void> {
const initialLanguage = this.getInitialLanguage();
const resources: Record<string, Record<string, Record<string, unknown>>> = {};
if (isPlatformBrowser(this.platformId)) {
// Browser: Only load the specific language needed
const languageResources = await this.loadLanguageResources(initialLanguage);
resources[initialLanguage] = languageResources;
} else {
// SSR: Load all languages for prerendering
for (const lang of SUPPORTED_LANGUAGES) {
const languageResources = await this.loadLanguageResources(lang);
resources[lang] = languageResources;
}
}
await init({
lng: initialLanguage,
fallbackLng: DEFAULT_LANGUAGE,
resources,
ns: NAMESPACES,
defaultNS: DEFAULT_NAMESPACE,
interpolation: {
escapeValue: false,
},
});
}Puntos Clave:
El pipe de traducción de Angular proporciona una forma limpia de usar traducciones en plantillas.
Ejemplo: Pipe de Traducción
import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';
@Pipe({
name: 't',
standalone: true,
pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
transform(key: string, options?: Record<string, unknown>): string {
return t(key, options);
}
}El pipe está marcado como puro: false para asegurar que se actualice cuando cambie el idioma. Esto es importante porque los cambios de idioma no modifican la clave de traducción en sí.
En Plantillas:
<!-- With namespace -->
<p>{{ "common:welcomeMessage" | t }}</p>
<!-- With nested keys -->
<p>{{ "dashboard.title" | t }}</p>
<!-- Simple interpolation -->
<p>{{ "greeting" | t: { name: userName } }}</p>
<!-- Result: "Hello, John!" from greeting: "Hello, {{name}}!" -->
<!-- With Angular pipes -->
<p>{{ "price" | t: { amount: 29.99 | currency } }}</p>
<!-- Result: "Price: $29.99" from: "Price: {{amount}}" -->
<!-- Multiple variables -->
<p>{{ "updated" | t: { date: lastModified | date, user: currentUser } }}</p>
<!-- Result: "Updated on Jan 15, 2024 by Alice" from: "Updated on {{date}} by {{user}}." -->
La interpolación te permite insertar valores dinámicos en tus traducciones. Puedes pasar variables, usar pipes de Angular para formatear, y combinar múltiples valores en una sola cadena de traducción.
En Código de Componente:
import { Component } from '@angular/core';
import { t } from 'i18next';
@Component({
selector: 'app-dashboard',
template: `
<h1>{{ title }}</h1>
<p>{{ welcomeMsg }}</p>
`,
})
export class DashboardComponent {
title = t('dashboard.title');
welcomeMsg = t('greeting', { name: 'John' });
}Para el uso en plantillas, la sintaxis del pipe es limpia y legible. Para el acceso programático, llama a t() directamente. Esto es útil para mostrar notificaciones, establecer títulos dinámicos, o manejar traducciones en la lógica del componente.
Usa notación de puntos que refleje la estructura de tu app: 'auth:login.title' en lugar de 'loginTitle' o texto sin procesar.
Divide las traducciones en espacios de nombres lógicos (por ejemplo, common.json, auth.json, dashboard.json) para una mejor mantenibilidad.
Usa el soporte de pluralización incorporado de i18next para manejar correctamente las formas singular/plural en todos los idiomas.
Asegúrate siempre de que i18next esté inicializado antes de renderizar tu app usando provideAppInitializer() para ejecutar la inicialización durante el arranque de la app.
Usa TypeScript para extraer claves de traducción directamente de tus archivos JSON para una seguridad de tipo completa. En lugar de mantener manualmente definiciones de tipo, aprovecha typeof de TypeScript e importaciones dinámicas para generar automáticamente tipos a partir de tus archivos de traducción reales.
Define un tipo que extraiga todas las posibles claves de tus archivos de traducción JSON usando un tipo auxiliar NestedKeys recursivo.
import common from './en/common.json'; // defaultNS: DEFAULT_NAMESPACE,
import auth from './en/auth.json'; // namespaces
type NestedKeys<T, Prefix extends string = ''> = T extends object
? {
[K in keyof T & (string | number)]: T[K] extends object
?
| NestedKeys<T[K], `${Prefix}${K & (string | number)}.`>
| `${Prefix}${K & (string | number)}`
: `${Prefix}${K & (string | number)}`;
}[keyof T & (string | number)]
: never;
export type TranslationKey =
| `common:${NestedKeys<typeof common>}`
| `auth:${NestedKeys<typeof auth>}`;Cambia el método transform del pipe para aceptar TranslationKey en lugar de string. Esto proporciona seguridad de tipo en plantillas y asegura que solo se puedan usar claves de traducción válidas.
import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';
import { TranslationKey } from '../../i18n/translation-key';
@Pipe({
name: 't',
standalone: true,
pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
transform(key: TranslationKey, options?: Record<string, unknown>): string {
return t(key, options);
}
}
Ahora obtienes autocompletado completo de IntelliSense y errores en tiempo de compilación para claves inválidas tanto en código TypeScript como en plantillas.
t('common:greeting'); // ✅ Autocomplete
t('auth:login.title'); // ✅ Valid
t('invalid.key'); // ❌ IDE shows error
{{ "common:greeting" | t }} // ✅ Valid in templates
{{ "invalid.key" | t }} // ❌ IDE shows errorBeneficios de Claves de Traducción Seguras en Tipo:
Implementa detección de idioma inteligente y permite a los usuarios cambiar de idioma en tiempo de ejecución con carga automática de recursos.
Determina el idioma del usuario a usar cuando tu app se inicia verificando múltiples fuentes en orden de prioridad.
private getInitialLanguage(): string {
if (isPlatformBrowser(this.platformId)) {
// Try to get language from localStorage (user preference)
const savedLang = localStorage.getItem('language');
if (savedLang) {
return savedLang;
}
// Fall back to browser language
return navigator.language ?? DEFAULT_LANGUAGE;
}
// Default language for SSR
return DEFAULT_LANGUAGE;
}Este enfoque verifica localStorage primero (preferencia del usuario), luego retrocede al idioma del navegador (navigator.language), y finalmente se establece en el idioma predeterminado de la app.
Cambia de idioma en tiempo de ejecución mientras cargas automáticamente los recursos de traducción bajo demanda.
public async switchLanguage(
language: string
): Promise<void> {
// In browser, check if we need to load the translation first
if (isPlatformBrowser(this.platformId)) {
const i18next = await import('i18next');
// Check if any namespace is missing for this language
for (const namespace of NAMESPACES) {
const currentResources = i18next.default.getResourceBundle(lang, namespace);
if (!currentResources) {
// Translation not loaded yet, load it dynamically
const translationModule = await this.loadJson(lang, namespace);
i18next.default.addResourceBundle(
lang,
namespace,
translationModule.default
);
}
}
}
// Switch to the new language
await changeLanguage(lang);
// Save preference to localStorage
if (isPlatformBrowser(this.platformId)) {
localStorage.setItem('language', lang);
}
}Al usar importaciones dinámicas, las traducciones se cargan bajo demanda. El método switchLanguage() verifica si los recursos ya están cargados usando getResourceBundle(). Si faltan, los importa dinámicamente y los agrega usando addResourceBundle(). Esto previene solicitudes de red redundantes mientras asegura que todas las traducciones necesarias estén disponibles.
Detalles Clave de Implementación:
Gestionar traducciones manualmente en múltiples idiomas es una tarea que consume tiempo y propensa a errores. Ahí es donde entra la localización impulsada por IA.
l10n.dev es un servicio de traducción impulsado por IA diseñado específicamente para archivos JSON de i18next:
Ahorra horas de trabajo de traducción manual y evita errores comunes como marcadores de posición rotos o formas plurales incorrectas.
¿Listo para optimizar tu flujo de trabajo de i18n en Angular?
Sube tus archivos i18n y deja que la IA maneje la traducción con conciencia del contexto y formato adecuado.
Descubre por qué la traducción potenciada por IA es mejor para archivos de i18n que los métodos tradicionales.
Integra la localización potenciada por IA directamente en tu pipeline de CI/CD.
Incorpora la localización de IA en tu flujo de trabajo con nuestras extensiones y complementos.
Implementar i18next en Angular con estrategias adecuadas de inicialización y carga de recursos asegura una experiencia de internacionalización fluida.
Combinado con servicios de traducción impulsados por IA como l10n.dev, puedes construir aplicaciones verdaderamente globales más rápido y con menos errores.
¡Comienza a construir aplicaciones multilingües en Angular hoy!