Skip to content

Volodymyr-Mishyn/ng17-ssr-i18n-example

Repository files navigation

Ng17-Ssr-I18n-Example

This project was generated with Angular CLI version 17.0.9.

Live version

I used approach described in this README for my web-site for tracking russian losses during the invasion of Ukraine. Deployed app on AWS

About

This app was created for example purposes for anyone seeking how to make Angular built-in i18n and SSR work together simultaneously in production and dev environment.

Kudos to Lostium team for providing guidance and answering my questions. Their example is a robust approach everyone should consider as first option.

Nevertheless, I came up with more clunky and basic solution with some minor disadvantages.

Installation and running

 $ git clone https://github.com/Volodymyr-Mishyn/ng17-ssr-i18n-example.git
 $ cd ng17-ssr-i18n-example
 $ npm install

To run dev server for English version

 $ npm run start

To run dev server for Ukrainian version

 $ npm run start:uk

To run production server of Express with both locales

 $ npm run build
 $ npm run serve:ssr:ng17-ssr-i18n-example

Implementation details

To make your application work with ssr and i18n you'll have to make steps close to following

SSR basic setup

Creating server side rendering applications

 $ ng new --ssr

To add SSR to an existing project

 $ ng add @angular/ssr

Angular creates server.ts file with Express.js for Server-Side Rendering (SSR) We will need to modify this file later, depending on our locales.

Localizing your application

All information about preparing your application for i18n, extracting strings for localization, and working with locale files is present on Angular i18n.

Localization files

After preparing your files you'll have something like

messages.xlf, messages.{your-locale}.xlf, messages.uk.xlf,

Modifying angular.json and preparing dev environment

Configure i18n in your angular.json Provide your base locale and other locales you prepared

"i18n": {
    "sourceLocale": {
        "code": "en-US",
        "baseHref": "/en/"
    },
    "locales": {
        "uk": {
            "translation": "src/locale/messages.uk.xlf",
        },
    }
},

Setup localization during build time

 "architect": {
        "build": {
          "options": {
            "localize": true,
          }
        }
 }

Setup additional dev configuration for languages you want to serve with dev server

  "configurations": {
            "development-uk": {
              "localize": ["uk"],
              "optimization": false,
              "extractLicenses": false,
              "sourceMap": true
            }
  }
"serve": {
      "builder": "@angular-devkit/build-angular:dev-server",
      "configurations": {
        "production": {
          "buildTarget": "ng17-ssr-i18n-example:build:production"
        },
        "development": {
          "buildTarget": "ng17-ssr-i18n-example:build:development"
        },
        "development-uk": {
          "buildTarget": "ng17-ssr-i18n-example:build:development-uk"
        }
      },
      "defaultConfiguration": "development"
}

Add script to run your preferred languages to your package.json for quality of life improvement:)

 "scripts": {
    "start": "ng serve",
    "start:uk": "ng serve --configuration=development-uk",
  },

Making localized application work with SSR

When Angular scaffolds ssr for your application it creates server.ts. But after adding localization your dist folder will have another structure

  • browser/
    • en-US/
    • uk/
  • server/
    • en-US/
    • uk/
  • 3rdpartylicenses.txt
  • prerendered-routes.json

but the server.ts is generated without respecting this locales nested structure. So we have to make changes to it

  1. Modify app method to have locale parameter
export function app(locale: string): express.Express {
  /**/
}
  1. Use that parameter to build paths respecting locale
const serverDistFolder = resolve(dirname(fileURLToPath(import.meta.url)), "../", locale);
const browserDistFolder = resolve(serverDistFolder, "../../browser/", locale);
const indexHtml = join(serverDistFolder, "index.server.html");
  1. Configure server with this paths
server.set("view engine", "html");
server.set("views", browserDistFolder + "/");
// Serve static files from /browser
server.get(
  `*.*`,
  express.static(browserDistFolder + "/", {
    maxAge: "1y",
  })
);

// All regular routes use the Angular engine
server.get(`*`, (req, res, next) => {
  const { protocol, originalUrl, headers, baseUrl } = req;
  commonEngine
    .render({
      bootstrap,
      documentFilePath: indexHtml,
      url: `${protocol}://${headers.host}${originalUrl}`,
      publicPath: browserDistFolder + "/",
      providers: [
        { provide: APP_BASE_HREF, useValue: baseUrl },
        //provide locale to the app
        { provide: LOCALE_ID, useValue: locale },
      ],
    })
    .then((html) => res.send(html))
    .catch((err) => next(err));
});
  1. Create instance of Express server for every locale and set it to be responsible for your route
server.use("/en", app("en-US"));
server.use("/uk", app("uk"));
server.listen(port, () => {
  console.log(`Node Express server listening on http://localhost:${port}`);
});
  1. (Optional) Add redirects
server.get("/", (req, res) => {
  const { headers, protocol } = req;
  res.redirect(`${protocol}://${headers.host}/en`);
});
server.get("*", (req, res) => {
  const { headers, protocol } = req;
  res.redirect(`${protocol}://${headers.host}/en`);
});

Important note : Your basehref-s should be the same in your built index.html files and in Express server setup /en/ and /en, /uk/ and /uk

Running express server in production mode

When angular builds your application each locale folder has it's own server file. The interesting part, those files don't differ for locales, they are the same. So you can run

 $ npm run build
 $ node dist/ng17-ssr-i18n-example/server/en-US/server.mjs
 or
 $ node dist/ng17-ssr-i18n-example/server/{your-locale}/server.mjs

and it would lead to same result - same express server with all your configured locales will run. Congratulations!

About

Example Angular application with SSR and built-in i18n

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published