Construir aplicaciones multi idioma con Server Side Rendering en Angular 17

En Lostium llevamos unos cuantos años utilizando Angular en nuestros desarrollos. Hacemos toda clase de apps y aplicaciones complejas. No entraremos en la polémica de la guerra de los frameworks: que si Vue es mejor, que si React le da mil vueltas, que si Next es lo "siguiente"… En nuestro caso, sabiendo exprimir sus funciones Angular cubre todas nuestras necesidades. Y es que, al final, lo importante no es la herramienta en sí, sino cómo la utilizamos para crear soluciones que realmente aporten valor.

Cambio de look de Angular

En esta ocasión, el contenido del artículo está dirigido a desarrolladores con conocimientos en Angular. Queremos profundizar en aspectos técnicos clave que nos han sido útiles para superar retos avanzados con este framework. Nuestro objetivo es compartir estas experiencias, proporcionando una visión práctica sobre cómo abordarlas.

Para aquellos que trabajéis con Angular, las actualizaciones de la versión 17 son bastante relevantes. Google ha introducido mejoras importantes en esta versión, las cuales ya estamos utilizando en lostium.com y animajobs.com, el portal de empleo de nuestros clientes y amigos de Acttiv.

Esta versión destaca por su mayor eficiencia y rendimiento, con flujos de control simplificados que mejoran la sintaxis en el HTML y vistas diferidas que se cargan sólo cuando son necesarias. El SSR (Server-Side Rendering) integrado de serie, hace que la carga inicial de las páginas sea más rápida y es crucial para su indexación en buscadores. Además, han optimizado el entorno de desarrollo con Vite como servidor y esbuild sustituyendo a Webpack, reduciendo considerablemente los tiempos de ‘build’.

Aunque Angular 17 trae avances significativos, aún hay aspectos por mejorar, especialmente al integrar diversas tecnologías.

En el desarrollo de aplicaciones multilingües con Server Side Rendering, nos hemos enfrentado a un desafío importante, ya que no existe una guía 'oficial' para combinar estas funcionalidades de manera sencilla.

Por ejemplo, el portal de empleo animajobs.com, se implementó con Angular 14 usando la infraestructura nativa de internacionalización (i18n). Este sistema tiene una peculiaridad, y es que genera aplicaciones separadas para cada idioma.

graph LR codigo(Código fuente) --> animajobs.com/es codigo-->animajobs.com/en codigo-->animajobs.com/fr codigo-->animajobs.com/de

Esta arquitectura plantea un problema de base a la hora de integrar SSR. El servidor express que usa Angular, encargado de renderizar la aplicación, no está preparado para servir el contenido multi idioma, sólo puede servir una única aplicación por instancia.

Tras investigar y hacer múltiples pruebas sin éxito, encontramos una solución bastante buena gracias al artículo del desarrollador Pierre Machaux. Consiste en utilizar un servidor express que actue de proxy y responda, dependiendo de la ruta solicitada, con el servidor específico del idioma correspondiente. Esto evita tener que levantar un servidor en un puerto diferente por cada lenguaje.

Volviendo al proyecto de animajobs.com, se desarrolló con Angular 14. Como esta versión ha llegado al end of life de soporte, lo hemos migrado a Angular 17. El proceso de actualización funcionó sin problemas pero no activó por defecto Server Side Rendering. Este pequeño detalle nos retrasó identificar que la solución que teníamos no compilaba correctamente.

Aunque la aproximación era correcta, el código ya no era compatible, debido, entre otras cosas, a que ahora se utilizan módulos ESM y la sintaxis para manejar el servidor Express es ligeramente diferente.

Hemos refactorizado el código para adecuarlo a la versión 17 con los siguientes cambios:

  • Modificamos proxy-server, ahora con extensión mjs para incluir los servidores de cada idioma. Importamos la función app correspondiente y la asociamos al path del lenguaje que le corresponde.
proxy-server.mjs
  • También cambiamos server.ts para adecuarlo a las novedades:
    • Modificamos APP_BASE_HREF con el path desde donde va a responder la aplicación en cada idioma (en nuestro caso, dependiendo del idioma: /en, /es...)
    • Ajustamos LOCALE_ID con el idioma correspondiente. Esto es muy importante porque si no lo puede inferir de las otras instancias.
    • Y por último, establecemos los tokens REQUEST y RESPONSE. Al migrar de versión es necesario incluir el fichero express.tokens (creado al migrar de versión con ng update).
    • Eliminamos la instanciación del servidor en este fichero ya que es proxy-server.mjs el que se va a encargar de crear e instanciar cada idioma.
server.ts

Con estos cambios, solo es necesario arrancar proxy-server con node (o pm2) y verificar que la aplicación responde en todos los idiomas.

Aquí tenéis el código por si lo necesitáis adaptar. Esperamos que os sea de utilidad y que os permita internacionalizar y utilizar Server Side Rendering sin problemas.