Traducir tu app Angular es esencial para llegar a una audiencia global. Angular proporciona soporte integrado de internacionalización (i18n) a través de @angular/localize, y también está la popular biblioteca ngx-translate. Esta guía te guiará a través de ambos enfoques para traducir una app Angular.
Entendiendo la Localización de Angular
Angular soporta internacionalización a través de:
- @angular/localize - Solución i18n integrada (recomendada para producción)
- ngx-translate - Biblioteca de terceros popular (configuración más fácil, traducciones en tiempo de ejecución)
Cubriremos ambos enfoques en esta guía.
Método 1: Usando @angular/localize (i18n Integrado)
El i18n integrado de Angular es la solución oficial y funciona en tiempo de compilación.
Paso 1: Agregar Soporte Angular i18n
ng add @angular/localize
Este comando agrega las dependencias y configuración necesarias.
Paso 2: Marcar Texto para Traducción
Usa el atributo i18n en tus plantillas:
Antes:
<h1>Welcome to our app!</h1>
Después:
<h1 i18n="@@welcome">Welcome to our app!</h1>
O con descripción:
<h1 i18n="Welcome message|@@welcome">Welcome to our app!</h1>
Paso 3: Extraer Mensajes de Traducción
Extrae cadenas traducibles a un archivo de mensajes:
ng extract-i18n --output-path src/locale
Esto crea el archivo messages.xlf con todas las cadenas traducibles.
Paso 4: Crear Archivos de Traducción
Crea archivos de traducción para cada idioma:
src/locale/messages.es.xlf (Español):
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="welcome" datatype="html">
<source>Welcome to our app!</source>
<target>¡Bienvenido a nuestra aplicación!</target>
</trans-unit>
</body>
</file>
</xliff>
src/locale/messages.fr.xlf (Francés):
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="welcome" datatype="html">
<source>Welcome to our app!</source>
<target>Bienvenue dans notre application!</target>
</trans-unit>
</body>
</file>
</xliff>
Paso 5: Configurar angular.json
Actualiza angular.json para incluir configuraciones de locale:
{
"projects": {
"my-app": {
"i18n": {
"sourceLocale": "en",
"locales": {
"es": {
"translation": "src/locale/messages.es.xlf"
},
"fr": {
"translation": "src/locale/messages.fr.xlf"
}
}
},
"architect": {
"build": {
"configurations": {
"es": {
"localize": ["es"]
},
"fr": {
"localize": ["fr"]
}
}
},
"serve": {
"configurations": {
"es": {
"browserTarget": "my-app:build:es"
},
"fr": {
"browserTarget": "my-app:build:fr"
}
}
}
}
}
}
}
Paso 6: Compilar para Diferentes Locales
Compila la app para cada locale:
ng build --configuration=es
ng build --configuration=fr
Paso 7: Manejar Variables en Traducciones
Usa interpolación:
<p i18n="User greeting|@@userGreeting">Hello, {{ userName }}!</p>
En el archivo XLF:
<trans-unit id="userGreeting" datatype="html">
<source>Hello, <x id="INTERPOLATION"/>!</source>
<target>¡Hola, <x id="INTERPOLATION"/>!</target>
</trans-unit>
Paso 8: Pluralización
Usa formato de mensaje ICU:
<span i18n="Item count|@@itemsCount">
{count, plural, =0 {No items} one {1 item} other {{{count}} items}}
</span>
Método 2: Usando ngx-translate (Traducciones en Tiempo de Ejecución)
ngx-translate es más fácil de configurar y permite cambio de idioma en tiempo de ejecución.
Paso 1: Instalar ngx-translate
npm install @ngx-translate/core @ngx-translate/http-loader
O con yarn:
yarn add @ngx-translate/core @ngx-translate/http-loader
Paso 2: Crear Archivos de Traducción
Crea archivos JSON para cada idioma:
src/assets/i18n/en.json (Inglés - por defecto):
{
"app": {
"welcome": "Welcome to our app!",
"title": "My Angular App"
},
"button": {
"submit": "Submit",
"cancel": "Cancel",
"delete": "Delete"
},
"error": {
"network": "Network error. Please try again.",
"notFound": "Page not found"
},
"items": {
"count": "{count, plural, =0 {No items} one {# item} other {# items}}"
},
"user": {
"greeting": "Hello, {{name}}! You have {{count}} messages.",
"greeting_zero": "Hello, {{name}}! You have no messages.",
"greeting_one": "Hello, {{name}}! You have {{count}} message.",
"greeting_other": "Hello, {{name}}! You have {{count}} messages."
}
}
src/assets/i18n/es.json (Español):
{
"app": {
"welcome": "¡Bienvenido a nuestra aplicación!",
"title": "Mi Aplicación Angular"
},
"button": {
"submit": "Enviar",
"cancel": "Cancelar",
"delete": "Eliminar"
},
"error": {
"network": "Error de red. Por favor, inténtalo de nuevo.",
"notFound": "Página no encontrada"
},
"items": {
"count": "{count, plural, =0 {No hay elementos} one {# elemento} other {# elementos}}"
},
"user": {
"greeting": "¡Hola, {{name}}! Tienes {{count}} mensajes.",
"greeting_zero": "¡Hola, {{name}}! No tienes mensajes.",
"greeting_one": "¡Hola, {{name}}! Tienes {{count}} mensaje.",
"greeting_other": "¡Hola, {{name}}! Tienes {{count}} mensajes."
}
}
src/assets/i18n/fr.json (Francés):
{
"app": {
"welcome": "Bienvenue dans notre application!",
"title": "Mon Application Angular"
},
"button": {
"submit": "Soumettre",
"cancel": "Annuler",
"delete": "Supprimer"
},
"error": {
"network": "Erreur réseau. Veuillez réessayer.",
"notFound": "Page non trouvée"
},
"items": {
"count": "{count, plural, =0 {Aucun élément} one {# élément} other {# éléments}}"
},
"user": {
"greeting": "Bonjour, {{name}}! Vous avez {{count}} messages.",
"greeting_zero": "Bonjour, {{name}}! Vous n'avez aucun message.",
"greeting_one": "Bonjour, {{name}}! Vous avez {{count}} message.",
"greeting_other": "Bonjour, {{name}}! Vous avez {{count}} messages."
}
}
Paso 3: Configurar App Module
src/app/app.module.ts:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { AppComponent } from './app.component';
// Función factory para TranslateHttpLoader
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
},
defaultLanguage: 'en'
})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Paso 4: Usar Traducciones en Plantillas
Usa el pipe translate:
Antes:
<h1>Welcome to our app!</h1>
Después:
<h1>{{ 'app.welcome' | translate }}</h1>
Paso 5: Usar Traducciones en Componentes
Usa el TranslateService:
src/app/app.component.ts:
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-app';
constructor(private translate: TranslateService) {
// Establecer idioma por defecto
translate.setDefaultLang('en');
// Usar idioma del navegador si está disponible
const browserLang = translate.getBrowserLang();
translate.use(browserLang?.match(/en|es|fr/) ? browserLang : 'en');
}
}
Paso 6: Interpolación de Cadenas con Variables
Pasa variables a las traducciones:
Plantilla:
<p>{{ 'user.greeting' | translate: { name: userName, count: messageCount } }}</p>
Componente:
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-user-greeting',
template: `
<p>{{ greeting }}</p>
`
})
export class UserGreetingComponent {
userName = 'John';
messageCount = 5;
greeting = '';
constructor(private translate: TranslateService) {
this.translate.get('user.greeting', {
name: this.userName,
count: this.messageCount
}).subscribe((res: string) => {
this.greeting = res;
});
}
}
Paso 7: Pluralización
Usa formato de mensaje ICU:
translation.json:
{
"items": {
"count": "{count, plural, =0 {No items} one {# item} other {# items}}"
}
}
Plantilla:
<p>{{ 'items.count' | translate: { count: itemCount } }}</p>
Componente:
itemCount = 5;
Paso 8: Formatear Fechas
Usa el DatePipe de Angular con locale:
app.module.ts:
import { LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localeEs from '@angular/common/locales/es';
import localeFr from '@angular/common/locales/fr';
registerLocaleData(localeEs);
registerLocaleData(localeFr);
Plantilla:
<p>{{ date | date:'fullDate' }}</p>
Componente:
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-date-display',
template: `
<p>{{ date | date:'fullDate' }}</p>
`
})
export class DateDisplayComponent {
date = new Date();
constructor(private translate: TranslateService) {
// Establecer locale basándose en idioma actual
this.translate.onLangChange.subscribe((event) => {
// Actualizar locale para formateo de fechas
// Esto requiere configuración adicional con LOCALE_ID
});
}
}
Paso 9: Formatear Números
Usa el DecimalPipe y CurrencyPipe de Angular:
Plantilla:
<p>Número: {{ number | number }}</p>
<p>Moneda: {{ amount | currency:'USD' }}</p>
Paso 10: Cambiar Idioma Programáticamente
Crea un selector de idioma:
src/app/components/language-switcher/language-switcher.component.ts:
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-language-switcher',
template: `
<select (change)="changeLanguage($event)">
<option value="en" [selected]="currentLang === 'en'">English</option>
<option value="es" [selected]="currentLang === 'es'">Español</option>
<option value="fr" [selected]="currentLang === 'fr'">Français</option>
</select>
`
})
export class LanguageSwitcherComponent {
currentLang: string;
constructor(private translate: TranslateService) {
this.currentLang = translate.currentLang || translate.defaultLang;
}
changeLanguage(event: any) {
const lang = event.target.value;
this.translate.use(lang);
this.currentLang = lang;
localStorage.setItem('language', lang);
}
}
Paso 11: Detección de Idioma y Persistencia
Carga el idioma guardado al iniciar la app:
app.component.ts:
import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
constructor(private translate: TranslateService) {
translate.setDefaultLang('en');
}
ngOnInit() {
// Intentar obtener idioma guardado
const savedLang = localStorage.getItem('language');
if (savedLang) {
this.translate.use(savedLang);
} else {
// Usar idioma del navegador
const browserLang = this.translate.getBrowserLang();
this.translate.use(browserLang?.match(/en|es|fr/) ? browserLang : 'en');
}
}
}
Mejores Prácticas
1. Usar Claves de Traducción Descriptivas
Malo:
{
"msg1": "Submit"
}
Bueno:
{
"button": {
"submit": "Submit"
}
}
2. Organizar por Característica
{
"auth": {
"login": "Login",
"logout": "Logout"
},
"dashboard": {
"title": "Dashboard"
}
}
3. Usar Carga Perezosa para Módulos de Características
feature.module.ts:
import { TranslateModule } from '@ngx-translate/core';
@NgModule({
imports: [
TranslateModule.forChild() // Usar forChild en módulos de características
]
})
export class FeatureModule { }
4. Manejar Traducciones Faltantes
this.translate.get('some.key', { defaultValue: 'Texto por defecto' })
.subscribe((text: string) => {
// Usar texto
});
5. Usar Servicio de Traducción en Servicios
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Injectable({
providedIn: 'root'
})
export class NotificationService {
constructor(private translate: TranslateService) {}
showError() {
this.translate.get('error.network').subscribe((message: string) => {
// Mostrar notificación de error
});
}
}
6. Probar Longitudes de Cadenas
Algunos idiomas son más largos que otros. Diseña tu UI para acomodar:
- Alemán y finlandés: 30-50% más largo que inglés
- Idiomas asiáticos: Pueden necesitar más espacio vertical
Errores Comunes
1. Olvidar Importar TranslateModule
Siempre importa TranslateModule en módulos que usan traducciones:
imports: [TranslateModule]
2. No Proporcionar HttpClient
Para TranslateHttpLoader, necesitas HttpClientModule:
imports: [HttpClientModule]
3. Codificar Cadenas de Formato Directamente
Malo:
const price = `$${amount.toFixed(2)}`;
Bueno:
{{ amount | currency:'USD' }}
4. No Manejar Traducciones Asíncronas
Las traducciones se cargan de forma asíncrona. Usa observables:
this.translate.get('key').subscribe((text: string) => {
// Usar texto
});
Avanzado: Cargador de Traducción Personalizado
Crea un cargador personalizado para diferentes fuentes de datos:
import { Observable } from 'rxjs';
import { TranslateLoader } from '@ngx-translate/core';
export class CustomTranslateLoader implements TranslateLoader {
getTranslation(lang: string): Observable<any> {
// Cargar traducciones desde fuente personalizada
return this.http.get(`/api/translations/${lang}`);
}
}
Avanzado: Caché de Traducciones
Almacena traducciones en caché para reducir solicitudes HTTP:
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Injectable()
export class TranslationCacheService {
private cache = new Map<string, any>();
constructor(private translate: TranslateService) {
this.translate.onLangChange.subscribe(() => {
this.cache.clear();
});
}
getTranslation(lang: string): Observable<any> {
if (this.cache.has(lang)) {
return of(this.cache.get(lang));
}
return this.http.get(`/assets/i18n/${lang}.json`).pipe(
tap(translations => this.cache.set(lang, translations))
);
}
}
Avanzado: Soporte RTL
Maneja idiomas de derecha a izquierda:
import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-root',
template: '<div [dir]="textDirection"><router-outlet></router-outlet></div>'
})
export class AppComponent implements OnInit {
textDirection = 'ltr';
constructor(private translate: TranslateService) {}
ngOnInit() {
this.translate.onLangChange.subscribe((event) => {
const isRTL = ['ar', 'he', 'fa'].includes(event.lang);
this.textDirection = isRTL ? 'rtl' : 'ltr';
document.documentElement.dir = this.textDirection;
document.documentElement.lang = event.lang;
});
}
}
Avanzado: Soporte TypeScript
Crea definiciones de tipo para traducciones:
src/types/translations.d.ts:
export interface Translations {
app: {
welcome: string;
title: string;
};
button: {
submit: string;
cancel: string;
delete: string;
};
// ... otros tipos de traducción
}
Usar con Angular Universal (SSR)
Para renderizado del lado del servidor:
import { TranslateLoader } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { makeStateKey, TransferState } from '@angular/platform-browser';
export class TranslateBrowserLoader implements TranslateLoader {
constructor(
private http: HttpClient,
private transferState: TransferState
) {}
getTranslation(lang: string): Observable<any> {
const key = makeStateKey<number>(`transfer-translate-${lang}`);
const data = this.transferState.get(key, null);
if (data) {
return of(data);
} else {
return this.http.get(`/assets/i18n/${lang}.json`).pipe(
tap(data => this.transferState.set(key, data))
);
}
}
}
Conclusión
Localizar tu app Angular se puede hacer a través de dos enfoques principales:
- @angular/localize (Integrado) - Listo para producción, traducciones en tiempo de compilación
- ngx-translate - Configuración más fácil, traducciones en tiempo de ejecución, más flexible
Elige el método que mejor se ajuste a tus requisitos. Siguiendo estas prácticas, crearás una app que proporciona una experiencia nativa para usuarios en todo el mundo, expandiendo significativamente tu base de usuarios potencial.
Optimiza tu Flujo de Trabajo de Localización Angular
Gestionar traducciones para múltiples idiomas puede volverse complejo a medida que tu app crece. Considera usar una plataforma de gestión de traducciones para:
- Colaborar con traductores
- Mantener traducciones sincronizadas con tu código base
- Automatizar el flujo de trabajo de traducción
- Mantener consistencia en todos los idiomas
- Generar archivos de traducción automáticamente
¿Listo para llevar tu app Angular a nivel global? Explora la plataforma de localización de AZbox y optimiza tu flujo de trabajo de traducción: