Cómo hacer una Web App multiidioma en Angular: componentes y módulos de terceros (II)

Juan Martín21-Jul, 2020

Continuamos con nuestra serie de artículos sobre cómo traducir una web app en Angular a múltiples idiomas. En la Parte I vimos cómo traducir los textos principales de la web utilizando ngx-translate así como las utilidades de Angular (como las pipes) indicándole a Angular el locale a utilizar por defecto. A continuación, veremos cómo traducir componentes UI tanto de Angular Material como de terceros.

Traducir los componentes de Angular Material

De todos los componentes de Angular Material, solo requieren un trato especial los componentes Datepicker y Paginator (normalmente usado junto a Table), ya que el resto utilizan HTML donde podremos servirnos de ngx-translate para hacerlos multiidioma.

En cuanto a Datepicker, si configuraste el LanguageService en Parte I de forma que diga a Angular qué locale usar (ver Parte I en caso de no ser así): ¡enhorabuena! No es necesario hacer nada más: si abrimos el calendario con el navegador en español nos saldrá el lunes como primer día de la semana y los textos en español. Al cambiar a cualquier otro idioma, se nos mostrará en inglés, utilizando el Domingo (Sunday) como primer día de la semana. Asimismo, el valor del datepicker (única y exclusivamente la parte visual, no el valor interno) también cambia: en español el segundo número indica el mes, mientras que en inglés el mes lo indica el primer número.

Es posible ir un paso más allá. Por ejemplo, que el datepicker muestre la fecha con el mes en letras, de forma que no haya confusión al usarse en idiomas distintos, o forzar a que el mes sea siempre el segundo número. Personalmente, suelo preferir la implementación con momentjs del datepicker de Material: es mucho más cómodo trabajar con moment que con objetos Date de Javascript, y realmente la única pega es un tamaño del bundle algo superior, pero hay formas de solventar ese problema, cómo eliminar aquellos locales de momentjs que no se vayan a utilizar.

Para instalar momentjs y el adapter para el datepicker de material:

npm install moment @angular/material-moment-adapter --save

Creamos un archivo (llamado por ejemplo custom-date-formats.ts) y establecemos los formatos (de momentjs) que deseamos que se muestren en el Datepicker. Como ejemplo, vamos a hacer que el datepicker pase a mostrar la fecha con el mes en letras:

Datepicker Angular

A continuación, añadimos MatMomentDateModule a la lista de imports en nuestro AppModule y añadimos el provider para MAT_DATE_FORMATS, usando como valor la constante que acabamos de crear:

AppModule Angular

Finalmente implementamos un datepicker como lo haríamos normalmente y seleccionamos una fecha cualquiera, veremos que ahora aparece el mes en letra, y que si cambiamos el navegador de idioma este formato también lo hace.

Calendario Angular

En cuanto al Paginator de Material, la solución que vamos a implementar es la proporcionada por el usuario Ze Big Duck en StackOverflow. Lo primero es crear un PaginatorIntlService que será el que se encargue de traducir los distintos labels (lo divido en dos capturas, pero es el mismo archivo):

Paginator Angular

Translate Angular

Las claves que pasamos al TranslateService se corresponderían con la siguiente estructura en el JSON de idiomas:

JSON Idiomas Angular

A continuación, añadimos lo siguiente al array de providers dentro del AppModule, de forma que usemos nuestro PaginatorIntlService para mostrar los labels del Paginator en vez de los labels por defecto en inglés:

Providers dentro del AppModule - Angular

Y eso es todo. Paginator aparecerá con labels traducidos al idioma que tenga establecido el usuario.

Mostrar filas en Angular

Traducir módulos de terceros

Aquí es donde la cosa se complica un poco. Al utilizar módulos externos, la forma de hacer que sean multiidioma varía en gran medida según el módulo que utilicemos. Y eso suponiendo que se puedan traducir, cosa que nadie nos garantiza. Por poner un par de ejemplos:

La implementación en Angular de Fullcalendar es bastante simple, se importa un objeto con el locale a utilizar y se le pasa como input al componente del calendario. Para casos como este es por lo que creamos un LanguageService en Parte I, de forma que agrupemos todo nuestro código encargado de traducir la web en un único sitio de forma que pueda ser reutilizado. Lo que haríamos sería comprobar que el idioma del usuario está entre los soportados por nuestra web y elegir el locale correspondiente en ese caso. Después, desde el componente en que vayamos a usar el calendario llamamos a este servicio y obtenemos el locale correcto.

El editor de texto ngx-quill, por otro lado, es algo más enrevesado. Los textos que utiliza este editor de texto se le indican con CSS mediante la propiedad content. En este caso, lo que se puede hacer es sobrescribir el valor de esa propiedad mediante CSS de una forma similar a esta:

Programación con ngx-quill

Creamos una clase a nivel global (que aplicamos en la etiqueta body, por ejemplo) de forma que todo el CSS que escribamos dentro tenga prioridad por ser un selector más específico y así nos ahorramos tener que usar important. En este caso no es multiidioma, se vería siempre en español. Lo que se podría hacer en este caso es aplicar una clase adicional con el idioma del usuario, y usar esas clases en nuestro archivo SCSS, de forma que si se le aplica la clase “es” usemos textos en español, clase “en” textos en inglés, etc.

Cuando utilicemos un módulo de terceros, conviene analizarlo antes de empezar a usarlo en nuestro proyecto y ver si se adapta a nuestras necesidades (en este caso, que permita hacerlo multiidioma). De la misma forma que conviene adaptar estilos para que el diseño de la página sea homogéneo, aunque se estén usando módulos con distinta apariencia, también conviene que todo aparezca en el mismo idioma. De otro modo damos la sensación al usuario de estar utilizando partes inconexas y damos una mala imagen.

En caso de no encontrar ningún módulo que se adapte a nuestras necesidades, caben otras soluciones como desarrollar nuestros propios módulos y componentes reutilizables (aunque esto es algo que dependiendo de la complejidad que tengan puede no ser viable), aunque afortunadamente, la mayoría de componentes externos que utilicemos no requerirán ser adaptados. De hecho, algunos de ellos utilizan la misma configuración de LOCALE de Angular para saber cuándo mostrar textos en otro idioma.

En la Parte III veremos finalmente algunas técnicas o estrategias más avanzadas a la hora de usar ngx-translate, de forma que nuestra implementación multiidioma sea fácilmente escalable.

Juan Martín