logo

How to Localize an Existing Angular App with Angular Universal With all SEO aspects - Part One

Localization is essential for any web application targeting a global audience. It involves adapting your application to different languages and regions, providing users with a seamless experience in their native language. If you already have an Angular application and are using Angular Universal for server-side rendering (SSR), localizing your app might seem challenging. However, with the right approach, you can effectively localize your existing Angular app while leveraging Angular Universal's benefits. This article will guide you through the process.

1. Install Localization Packages

Localization in Angular requires the @angular/localize package. If your existing app doesn’t include this package, install it:

ng add @angular/localize

This package enables Angular's built-in i18n (internationalization) capabilities, which are necessary for handling translations.

1. Prepare Your Application for Localization

  1. Extract Translatable Content: Use Angular’s i18n system to mark text in your templates that needs translation. For example, add the i18n attribute to your HTML elements:

    <h1 i18n="@@welcomeMessage">Welcome to our application!</h1>
    
  2. Extract Translations: Generate an extraction file using Angular CLI:

    ng extract-i18n
    

    This command produces a messages.xlf file in your project’s root directory, containing all marked text strings. You can then use this file to create translations for other languages.

  3. Create Translation Files: Create a src/locale directory and add translation files for each supported language, such as:

    • src/locale/messages.ar.xlf for Arabic
    • src/locale/messages.ar-SA.xlf for Saudi Arabic

    Each file should include translations for the corresponding language.

If you have several alternate URLs targeted at users with the same language but in different locales, it's a good idea to also provide a catchall URL for geographically unspecified users of that language. For example, if you have specific URLs for English speakers in Ireland (en-ie), Canada (en-ca), and Australia (en-au), provide a generic English (en) page for searchers in the US, UK, and all other English-speaking locations. It can be one of the specific pages, if you choose. Source

3. Configure Localization in Angular

To serve localized content, you must configure the Angular build system for each language:

  1. Update angular.json: Open the angular.json file and add an i18n section:

    "i18n": {
        "sourceLocale": {
             "baseHref": "/",
             "code": "en-US"
         },
         "locales": {
             "ar-SA": {
                 "translation": "src/locale/messages.ar-SA.xlf",
                 "baseHref": "ar-SA/"
             },
             "ar": {
                 "translation": "src/locale/messages.ar.xlf",
                 "baseHref": "ar/"
              }
         }
     },
    
  2. Configure Build Options: Under the projects -> architect -> build -> configurations section, add configurations for each language:

      "ar-sa": {
       "localize": ["ar-SA"]
      },
     "ar": {
       "localize": ["ar"]
     },
    
  3. Serve Localized Content: Modify the serve configuration under the projects -> architect -> serve -> configurations section:

    "ar": {
      "browserTarget": "my-app:build:ar"
    },
    "ar-SA": {
      "browserTarget": "my-app:build:ar-SA"
    }
    

5. Integrate Localization with Angular Universal

Angular Universal requires additional configuration to handle server-side rendering for localized content. Follow these steps:

  1. Update server.ts to Detect Locale: Modify the server logic to detect and serve the correct locale based on the request URL.

     export function app(lang: string){
         const server = express();
         const distFolder = join(BROWSER_BASE_PATH, 'build', lang);
         const indexHtml = existsSync(join(distFolder, "index.original.html")) ? "index.original.html" : "index";
    
         server.engine("html", ngExpressEngine({
             bootstrap: AppServerModule,
             providers: [
                 {provide: LOCALE_ID, useValue: lang},
                 {provide: APP_BASE_HREF, useValue: `/${lang}`}
             ],
         }));
         .....
         server.get("*", (req, res) => {
              res.render(join(distFolder, "index.html"), {
                 req, 
                 providers: [
                     {provide: APP_BASE_HREF, useValue: `/${lang}`},
                     {provide: REQUEST, useValue: req},
                     {provide: RESPONSE, useValue: res},
                     {provide: LOCALE_ID, useValue: lang}
                 ]
             }, (error, html)=>{
                  if (error) {
                     res.end(indexHtml);
                 } else {
                      res.end(html);
                 }
             })
         })
     }
     
     function run() {
         const port = process.env.PORT || 4000;
         const appEn = app('en-US');
         const appAr = app('ar');
         const appArSA = app('ar-SA');
         const server = express();
    
         server.use('/ar', appAr);
         server.use('/ar-SA', appArSA);
         server.use('/en', appEn);
         server.use('/en-US', appEn);
         ....
         server.listen(port, () => {
             console.log(`Node Express server listening on http://localhost:${port}`);
         });
     }
    
  2. Serve Localized Server Bundles: Ensure the server serves the appropriate server bundle based on the detected locale in the URL by creating a new file called proxy-server.ts:

     const express = require("express");
     const path = require("path");
     
     const getTranslatedServer = (lang) => {
         const distFolder = path.join(
             process.cwd(),
             `build/server/${lang}`
         );
         const server = require(`${distFolder}/main.js`);
         return server.app(lang);
     };
     
     function run() {
         const port = process.env.PORT || 4000;
     
         // Start up the Node server
         const appAr = getTranslatedServer("ar");
         const appArSA = getTranslatedServer("ar-SA");
         const appEnUS = getTranslatedServer("en-US");
     
         const server = express();
         
         server.get("/health_check", (req, res) => {
             res.status(200)
                 .send();
         });
     
         server.use("/ar", appAr);
         server.use("/ar-SA", appArSA);
         server.use("/en-US", appEnUS);
     
         server.listen(port, () => {
             console.log(`Node Express server listening on http://localhost:${port}`);
         });
     }
     
     run();
    

6. Build and Deploy the Localized Application

Once everything is configured, build your localized Angular application:

ng build --localize && cpx proxy-server.js dist 
node ./dist/proxy-server.js

Your application should now serve the correct localized version based on the URL, providing users with content in their preferred language.

Conclusion

Localizing an existing Angular app with Angular Universal enhances your application's reach and user experience by delivering content in multiple languages. By following this guide, you can set up a robust localization system, ensuring that your application caters to a global audience. With Angular Universal, you also ensure that your localized content is delivered efficiently, improving both performance and SEO.