La internacionalización (i18n) es esencial para crear aplicaciones Angular globales. Esta guía te muestra cómo implementar i18next en Angular con una carga de recursos, inicialización y patrones de uso adecuados.
i18next es uno de los frameworks de i18n más populares, ofreciendo características potentes y flexibilidad:
Instala i18next usando npm o yarn:
npm install i18next¡Eso es todo! i18next es una biblioteca independiente sin dependencias específicas del framework. No necesitas angular-i18next ni ningún otro envoltorio; simplemente usa i18next directamente en tu aplicación Angular.
Existen dos enfoques principales para cargar recursos de traducción en Angular con i18next:
El uso de importaciones dinámicas aprovecha la división de código de Webpack para cargar las 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>> = {};
// Load all namespaces for the given language in parallel
await Promise.all(
NAMESPACES.map(async (namespace) => {
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 de backend HTTP carga las traducciones a través de solicitudes HTTP desde un servidor o CDN. Esto es útil cuando deseas actualizar las 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 una 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 renderice.
La función provideAppInitializer de Angular (introducida en Angular 18) asegura que i18next esté completamente inicializado antes de que la aplicación comience a renderizarse. 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 evita que aparezcan claves de traducción 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é es importante esto?
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 las 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 pure: 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í misma.
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 dar formato y combinar múltiples valores en una sola cadena de traducción.
En el código del 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 aplicación: 'auth:login.title' en lugar de 'loginTitle' o texto sin procesar.
Divide las traducciones en espacios de nombres lógicos (p. ej., common.json, auth.json, dashboard.json) para una mejor mantenibilidad.
Usa el soporte de pluralización integrado de i18next para manejar las formas singular/plural correctamente en todos los idiomas.
Asegúrate siempre de que i18next esté inicializado antes de renderizar tu aplicación usando provideAppInitializer() para ejecutar la inicialización durante el arranque.
Usa TypeScript para extraer claves de traducción directamente de tus archivos JSON para una seguridad de tipos completa. En lugar de mantener manualmente las definiciones de tipos, aprovecha typeof de TypeScript y las importaciones dinámicas para generar tipos automáticamente a partir de tus archivos de traducción reales.
Define un tipo que extraiga todas las claves posibles 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 tipos en las 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 con IntelliSense y errores en tiempo de compilación para claves no válidas tanto en el código TypeScript como en las 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 las claves de traducción con seguridad de tipos:
Implementa una 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 aplicación se inicia comprobando 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 comprueba primero localStorage (preferencia del usuario), luego recurre al idioma del navegador (navigator.language) y finalmente utiliza el idioma predeterminado de la aplicación.
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');
await Promise.all(
NAMESPACES.map(async (namespace) => {
const currentResources = i18next.default.getResourceBundle(
lang,
namespace,
);
// Check if any namespace is missing for this language
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() comprueba si los recursos ya están cargados usando getResourceBundle(). Si faltan, los importa dinámicamente y los añade usando addResourceBundle(). Esto evita solicitudes de red redundantes mientras asegura que todas las traducciones necesarias estén disponibles.
Detalles clave de implementación:
Gestionar las traducciones manualmente en varios idiomas consume mucho tiempo y es propenso a errores. Ahí es donde entra la localización potenciada por IA.
l10n.dev es un servicio de traducción potenciado 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 se encargue de la traducción con conciencia del contexto y el formato adecuado
Descubre por qué la traducción impulsada por IA es mejor para archivos i18n que los métodos tradicionales
Integra la localización impulsada por IA directamente en tu pipeline de CI/CD
Lleva la localización con IA a tu flujo de trabajo con nuestras extensiones y plugins
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 potenciados por IA como l10n.dev, puedes crear aplicaciones verdaderamente globales más rápido y con menos errores.
¡Empieza a crear aplicaciones Angular multilingües hoy mismo!