Building multi-language applications with Server Side Rendering in Angular 17

At Lostium, we've been using Angular in our development projects for several years. We create all sorts of apps and complex applications. We won't get into the debate of the framework wars: whether Vue is better, React surpasses it, or Next is the 'next' big thing… In our case, knowing how to leverage its features, Angular meets all our needs. Because in the end, what matters is not the tool itself, but how we use it to create solutions that truly add value.

Angular new look

This time, the article's content is aimed at developers with knowledge in Angular. We want to delve into key technical aspects that have been useful to overcome advanced challenges with this framework. Our goal is to share these experiences, providing a practical insight into how to tackle them.

For those of you working with Angular, the updates in version 17 are quite relevant. Google has introduced significant improvements in this version, which we are already using on lostium.com and animajobs.com, the job portal of our clients and friends at Acttiv.

This version stands out for its increased efficiency and performance, with simplified control flows that enhance syntax in HTML and deferred views that load only when necessary. The built-in Server-Side Rendering (SSR) makes the initial page loading faster and is crucial for search engine indexing. Additionally, they have optimized the development environment with Vite as the server and esbuild replacing Webpack, significantly reducing 'build' times.

Although Angular 17 brings significant advancements, there are still areas for improvement, especially when integrating various technologies.

In the development of multilingual applications with Server-Side Rendering, we have faced a significant challenge, as there is no 'official' guide to combining these functionalities seamlessly.

For example, the job portal animajobs.com was implemented with Angular 14 using the native internationalization (i18n) infrastructure. This system has a peculiarity in that it generates separate applications for each language.

graph LR codigo(Source code) --> animajobs.com/es codigo-->animajobs.com/en codigo-->animajobs.com/fr codigo-->animajobs.com/de

This architecture poses a fundamental challenge when integrating SSR. The Express server used by Angular, responsible for rendering the application, is not prepared to serve multilingual content; it can only serve a single application per instance.

After researching and conducting multiple unsuccessful tests, we found a fairly good solution thanks to the article by developer Pierre Machaux. It involves using an Express server as a proxy that responds, depending on the requested route, with the specific language server. This avoids the need to run a server on a different port for each language.

Returning to the animajobs.com project, it was originally developed with Angular 14. Since this version has reached the end of its support life, we have migrated it to Angular 17. The update process went smoothly but did not enable Server-Side Rendering by default. This small detail delayed us in identifying that our existing solution was not compiling correctly.

Although the approach was correct, the code was no longer compatible due, among other things, to the use of ESM modules and slight syntax differences in handling the Express server.

We have refactored the code to adapt it to version 17 with the following changes:

  • We modified the proxy-server, now with an .mjs extension, to include servers for each language. We imported the corresponding 'app' function and associated it with the path that corresponds to the language.
proxy-server.mjs
  • We also made changes to server.ts to adapt it to the updates:
    • We modified APP_BASE_HREF with the path from which the application will respond in each language (in our case, depending on the language: /en, /es, etc.).
    • We adjusted LOCALE_ID with the corresponding language. This is crucial because it cannot be inferred from other instances.
    • Finally, we set the REQUEST and RESPONSE tokens. When migrating versions, it's necessary to include the express.tokens file (created during the version migration with ng update).
    • We removed the server instantiation in this file since proxy-server.mjs will be responsible for creating and instantiating each language.
server.ts

With these changes, it's only necessary to start proxy-server with Node (or PM2) and verify that the application responds in all languages.

Here is the code in case you need to adapt it. We hope it proves useful and enables you to internationalize and use Server Side Rendering without issues.