Hola a todos, hoy os voy a explicar como podemos crear una aplicación multiidioma con Angular.
En Angular puede ser que necesitemos manejar varios idiomas en nuestra aplicación, lo podemos hacer de una manera sencilla.
Creación proyecto Angular multiidioma
Lo primero es crear un proyecto Angular, sino sabes como se hace, te dejo un tutorial aquí:
Ficheros de traducción
Para hacer la magia de la traducción, lo que vamos a necesitar son ficheros JSON con las traducciones ya montadas, uno por cada idioma, por ejemplo, es.json o en.json. Si lo necesitas, también se puede añadir el código del país, por ejemplo, es-CO.json o en-US.json.
Si quieres saber más sobre la internacionalización, visita este articulo de la Wikipedia.
En este tutorial, vamos a tener español (es) e ingles (en).
Estos ficheros los vamos a tener en la carpeta i18n dentro de assets:
Este es el contenido:
— en.json
[json]{«hello.world»: «Hello world!»,
«hello.custom»: «Hello {value}!»,
«spanish»: «Spanish»,
«english»: «English»
}[/json]
— es.json
[json]{«hello.world»: «¡Hola mundo!»,
«hello.custom»: «¡Hola {value}!»,
«spanish»: «Español»,
«english»: «Inglés»
}[/json]
{value} lo utilizaremos para personalizar traducciones.
IMPORTANTE: en todos los ficheros, siempre debe tener la misma clave (lo que esta entre paréntesis) para que la traducción sea efectiva.
IMPORTANTE: si vas a usar códigos de países, el código del país debe estar en español.
Creación de modulo de traducción
Vamos a crear todo lo necesario para nuestro modulo de traducción.
Primero el módulo, abre la terminal y posiciónate en la carpeta app dentro de src y ejecuta el siguiente comando:
A continuación, un servicio donde obtendremos los datos de los ficheros json que tenemos en la carpeta assets.
Por último, el pipe que utilizaremos para traducir en el HTML, donde más nos hará falta.
En el modulo translate, le declararemos y exportaremos el pipe y en providers, el servicio. También necesitaremos el módulo HttpClientModule para obtener los ficheros JSON a nuestra aplicación.
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; import { TranslatePipe } from './translate.pipe'; import { TranslateService } from './translate.service'; @NgModule({ imports: [ CommonModule, HttpClientModule ], declarations: [ TranslatePipe ], exports: [ TranslatePipe ], providers: [ TranslateService ] }) export class TranslateModule { }
Servicio de traducción
Este servicio, es el más importante, ya que es el que trae los datos de los ficheros y será el encargado de traducir según el lenguaje elegido.
Haremos lo siguiente:
- Tener un atributo data para almacenar los datos
- Inyectaremos HttpClient en nuestro constructor.
- Realizaremos el método getData, que dándole una ruta y un idioma (este ultimo opcional), nos traerá el fichero JSON correspondiente. Sino le pasamos un idioma, usamos navigator.language que nos da el navegador. En caso de que no exista el fichero de traducción, nos descargaremos uno por defecto, en mi caso el fichero de español.
- Realizaremos el método getTranslate, que dándole una clave, nos devolverá la traducción asociada.
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; @Injectable() export class TranslateService { // Objeto donde guardar las traducciones private data: any; constructor(private http: HttpClient) { } public getData(path: string, language?: string) { return new Promise((resolve, reject) => { // Si no hay idioma, cogemos el del navegador if (!language) { language = navigator.language; } // Obtengo los datos del fichero this.http.get(path + language + '.json').subscribe({ next: (data) => { this.data = data; resolve(true); }, error: (error) => { console.error(error); // Obtengo los datos del fichero es.json this.http.get(path + 'es.json').subscribe({ next: (data) => { this.data = data; resolve(true); }, error: (error) => { reject(true); } }) } }) }) } public getTranslate(key: string): string { return this.data ? this.data[key] : key; } }
Pipe para la traducción
Nuestro pipe nos ayudará a traducir las partes del HTML que necesitemos traducir, que es donde más se suele usar.
Solo debemos inyectar nuestro servicio y hacerlo impuro.
¿Por qué hacerlo impuro? Como es algo que será dinámico, tiene que estar pendiente de que cambie de idioma, si es puro, no podríamos cambiar de idioma una vez seteado.
Si le pasamos un objeto con parámetros, recorro sus claves y remplazo los {clave} por su valor.
import { Pipe, PipeTransform } from '@angular/core'; import { TranslateService } from './translate.service'; @Pipe({ name: 'translate', pure: false }) export class TranslatePipe implements PipeTransform { constructor(private translateService: TranslateService) { } transform(value: string, params?: any): string { // Si no tiene parametros, devuelvo la traducción if (!params) { return this.translateService.getTranslate(value); } // Obtengo la traducción let translate = this.translateService.getTranslate(value); // Recorro el objeto y remplazo los valores for (const keyParam of Object.keys(params)) { translate = translate.replaceAll('{' + keyParam + '}', params[keyParam]) } return translate; } }
Nota, si la traducción no existe, lo mostraremos tal cual.
Iniciando nuestro módulo multiidioma
Antes de entrar en acción, debemos ir a nuestro app.module e importar nuestro nuevo módulo:
imports: [ BrowserModule, AppRoutingModule, TranslateModule ],
Es necesario que añadamos la siguiente función antes de @NgModule:
export function translateFactory(provider: TranslateService) { return () => provider.getData('/assets/i18n/'); }
Y en providers, le añadimos lo siguiente:
{ provide: APP_INITIALIZER, useFactory: translateFactory, deps: [TranslateService], multi: true, }
Quedando nuestro app.module así:
import { APP_INITIALIZER, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { TranslateModule } from './translate/translate.module'; import { TranslateService } from './translate/translate.service'; export function translateFactory(provider: TranslateService) { return () => provider.getData('/assets/i18n/'); } @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, TranslateModule ], providers: [ { provide: APP_INITIALIZER, useFactory: translateFactory, deps: [TranslateService], multi: true, }, ], bootstrap: [AppComponent] }) export class AppModule { }
Probando nuestro proyecto multiidioma
Para testear nuestro proyecto, nos vamos a app.component y pegamos el siguiente código:
import { Component } from '@angular/core'; import { TranslateService } from './translate/translate.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { constructor(private translateService: TranslateService) { console.log('Traduciendo desde el servicio: ' + this.translateService.getTranslate('hello.world')); } async changeLanguage(language: string) { await this.translateService.getData('/assets/i18n/', language); console.log('Traduciendo desde el servicio: ' + this.translateService.getTranslate('hello.world')); } }
Y en app.component.html, esté código:
<!-- Cambio a Español --> <button>{{'spanish' | translate}}</button> <!-- Cambio a Ingles --> <button>{{'english' | translate}}</button> <!-- Estandar --> {{'hello.world' | translate}} <!-- Parametros --> {{'hello.custom' | translate: {value: 'DDR'} }}
Ahora, solo tenemos que arrancar nuestro proyecto con el siguiente comando:
Esto es lo que veremos:
Si abrimos la consola, veremos los que ha escrito desde el typescript.
Cambiar de idioma inicial desde Chrome
Si quieres que cuando inicie coja un idioma concreto, debes configurar tu navegador con ese idioma. En Chrome se hace de la siguiente manera:
- Escribe en la barra de búsqueda lo siguiente: chrome://settings/languages
- Añade el idioma que quieras.
- Muévelo al principio
Ahora, si vuelves a la aplicación, puedes ver (en mi caso) que carga el ingles por defecto.
Os dejo un video donde os lo explico con detalle.
Aquí tienes el repositorio de github.
Espero que os sea de ayuda. Si tenéis dudas, preguntad. Estamos para ayudarte.
Deja una respuesta