Need help?

Localization With Ivy

Angular Ivy

Ivy is a complete rewrite of Angular’s rendering engine. In fact, it is the fourth rewrite of the engine and the third since Angular 2. But unlike rewrites two and three, which you might not have even noticed, Ivy promises huge improvements to your application. With Ivy, you can compile components more independently of each other. This improves development times since recompiling an application will only involve compiling the components that changed.

Ivy also has a huge focus on tree-shaking. This is the process in which the TypeScript compiler looks at your code and figures out exactly which libraries are needed and then eliminates any unused code. As a result, the distributed code will be much smaller, and your application’s loading time will improve.

With all these improvements Ivy brought in, you should seriously consider upgrading your project to use Angular Ivy.
Ivy has been around in a preview version since Angular 8, but you previously had to manually opt in to using the new engine. With Angular 9, Ivy is the standard engine for rendering your content.

With all these massive changes behind the scenes, you might be scared and wonder how much you would need to refactor your code to be compatible with Ivy. It turns out that the Angular team has made backward compatibility a priority and, in most cases, you should not have to change anything in your application other than updating it to the latest Angular version.

Localization With Ivy

Ivy, part of the new Angular rendering engine, includes a new approach to localizing applications — specifically extracting and translating text. This article explains the benefits and some of the implementation of this new approach. Before Ivy, the only way to add localizable messages to an Angular application was to mark them in component templates using the i18n attribute:

<div i18n>Hello, World!</div>

The Angular compiler would replace this text when compiling the template with different text if a set of translations was provided in the compiler configuration. The i18n tags are very powerful — they can be used in attributes and content; they can include complex nested ICU (International Components for Unicode) expressions; they can have metadata attached to them. See our i18n guide for more information.

But there were some shortcomings to this approach.

The most significant concern was that translation had to happen during template compilation, which occurs right at the start of the build pipeline. The result of this is that that full build, compilation-bundling-minification-etc, had to happen for each locale that you wanted to support in your application.

If a single build took 3 minutes, then the total build time to support 9 locales would be 3 mins x 9 locales = 27 mins.

Moreover, it was impossible to mark text in application code for translation, only text in component templates. This resulted in awkward workarounds where artificial components were created purely to hold text that would be translated.

Finally, it was not possible to load translations at runtime, which meant it was not possible for applications to be provided to an end-user who might want to provide translations of their own, without having to build the application themselves.

The new localization approach is based around the concept of tagging strings in code with a template literal tag handler called $localize. The idea is that strings that need to be translated are “marked” using this tag:

const message = $localize `Hello, World!`;

This $localize identifier can be a real function that can do the translation at runtime in the browser. But, significantly, it is also a global identifier that survives minification. This means it can act simply as a marker in the code that a static post-processing tool can use to replace the original text with translated text before the code is deployed. For example, the following code:

warning = $localize `${this.process} is not right`;

could be replaced with:

warning = "" + this.process + ", ce n'est pas bon.";

The result is that all references to $localize are removed, and there is zero runtime cost to rendering the translated text.

The Angular template compiler for Ivy has been redesigned to generate $localize tagged strings rather than doing the translation itself. For example, the following template:

<h1 i18n>Hello, World!</h1>

would be compiled to something like:

ɵɵelementStart(0, "h1"); // <h1>
ɵɵi18n(1, $localize`Hello, World!`); // Hello, World!
ɵɵelementEnd(); // </h1>

This means that after the Angular compiler has completed its work, all the template text marked with i18n attributes have been converted to $localize tagged strings, which can be processed just like any other tagged string.

Notice also that the $localize tagged strings can occur in any code (user code or generated from templates in both applications or libraries) and are not affected by minification, so while the post-processing tool might receive code that looks like this,

...var El,kl=n("Hfs6"),Sl=n.n(kl);El=$localize`Hello, World!`;let Cl=(()=>{class e{constructor(e)...

it is still able to identify and translate the tagged message. The result is that we can reorder the build pipeline to do translation at the very end of the process, resulting in a considerable build time improvement.

Here you can see that the build time is still 3 minutes, but since the translation is done as a post-processing step, we only incur that build cost once. Also, the translations’ post-processing is very fast since the tool only has to parse the code for $localize tagged strings, in this case around 5 seconds.

The result is that the total build time for 9 locales is now 3 minutes + ( 9 x 5 seconds) = 3 minutes 45 seconds. Compared to 27 minutes for the pre-Ivy translated builds.

The post-processing of translations is already built into the Angular CLI and if you have configured your projects according to our i18n guide, you should already be benefitting from these faster build times.

The use of $localize in the application code is not yet publicly supported or documented. It requires new message extraction tooling — the previously (pre-Ivy) message extractor does not find $localize text in the application code. This is being integrated into the CLI now and should be released as part of 10.1.0.

How to implement localization in Angular 10

Internationalization (i18n) is the process of designing and preparing your app to be usable in various locales around the world. Localization is the process of building versions of your app for various locales, including extracting text for translation into different languages and formatting data for particular locales.

A locale identifies a region (such as a country) where people speak a particular language or language variant. The locale determines the formatting and parsing of dates, times, numbers, and currencies as well as measurement units and the translated names for time zones, languages, and countries.

To prepare your app for translations, you should have a basic understanding of the Template, Components, Angular CLI, XML.

Steps to localize your app

Create a new project with Angular CLI by running

ng new i18nDemo

For localization demo, Update app.component.html with the below code.

<h1 i18n> Localization Demo in Angular using i18n</h1>

<h3 i18n="@@myName"> Hello, My name is Darshan</h3>

<p>This text will remain same in all languages</p>

<hr />

<ng-container *ngFor="let language of languageList">

  <a href="/{{language.code}}/">

    <button class="button">{{language.label}}</button>

  </a>

</ng-container>

Update app.component.ts with below code

import { Component, LOCALE_ID, Inject } from '@angular/core';

@Component({

  selector: 'app-root',

  templateUrl: './app.component.html',

  styleUrls: ['./app.component.scss'],

})

export class AppComponent {

  title = 'i18nDemo';

  languageList = [

    { code: 'en-US', label: 'English' },

    { code: 'hi', label: 'हिंदी' },

    { code: 'es', label: 'Spanish' },

  ];

  constructor(@Inject(LOCALE_ID) protected localeId: string) {}

}

Add the localize package @angular/localize with Angular CLI by running.

ng add @angular/localize

This command updates your project’s package.json and polyfills.ts files to import the @angular/localize package.

Now create a translation file with Angular CLI by running.

ng xi18n --output-path src/locale

This will create messages.xlf translation file which looks like

<?xml version="1.0" encoding="UTF-8" ?>

<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">

  <file source-language="en-US" datatype="plaintext" original="ng2.template">

    <body>

 <trans-unit id="cd299a9a7bd5d4962e27040b729b21cb4eb7807a" datatype="html">

        <source>Localization Demo in Angular using i18n</source>

        <context-group purpose="location">

          <context context-type="sourcefile">src/app/app.component.html</context>

          <context context-type="linenumber">1</context>

        </context-group>

      </trans-unit>

      <trans-unit id="myName" datatype="html">

        <source>Hello, My name is Darshan</source>

        <context-group purpose="location">

          <context context-type="sourcefile">src/app/app.component.html</context>

          <context context-type="linenumber">2</context>

        </context-group>

      </trans-unit>

    </body>

  </file>

</xliff>

We will use Google Translate for translation, Now create Spanish messages.es.xlf translation file which looks like


<?xml version="1.0" encoding="UTF-8" ?>

<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">

  <file source-language="en-US" datatype="plaintext" original="ng2.template">

    <body>

<trans-unit id="cd299a9a7bd5d4962e27040b729b21cb4eb7807a" datatype="html">

    <source>Localization Demo in Angular using i18n</source>

     <target>Demostración de localización en angular usando i18n</target>

        <context-group purpose="location">

           <context   context-type="sourcefile">src/app/app.component.html</context>

          <context context-type="linenumber">1</context>

        </context-group>

      </trans-unit>

      <trans-unit id="myName" datatype="html">

        <source>Hello</source>

        <target>Hola</target>

        <context-group purpose="location">

          <context context-type="sourcefile">src/app/app.component.html</context>

          <context context-type="linenumber">2</context>

        </context-group>

      </trans-unit>

    </body>

  </file>

</xliff>

Now create Hindi messages.hi.xlf translation file which looks like

<?xml version="1.0" encoding="UTF-8" ?>

<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">

  <file source-language="en-US" datatype="plaintext" original="ng2.template">

    <body>

      <trans-unit id="cd299a9a7bd5d4962e27040b729b21cb4eb7807a" datatype="html">

        <source>Localization Demo in Angular using i18n</source>

        <target>I18n का उपयोग कर कोणीय में स्थानीयकरण डेमो</target>

        <context-group purpose="location">

          <context context-type="sourcefile">src/app/app.component.html</context>

          <context context-type="linenumber">1</context>

        </context-group>

      </trans-unit>

      <trans-unit id="myName" datatype="html">

        <source>Hello, My name is Darshan</source>

        <target>हेलो</target>

        <context-group purpose="location">

          <context context-type="sourcefile">src/app/app.component.html</context>

          <context context-type="linenumber">2</context>

        </context-group>

      </trans-unit>

    </body>

  </file>

</xliff>

Now Define locales in the build configuration. So update angular.json file like

{

  ...,

  "projects": {

    "i18nDemo": {

      ...,

      "i18n": {

        "sourceLocale": "en-US",

        "locales": {

          "es": {

            "translation": "src/locale/messages.es.xlf"

          },

          "hi": {

            "translation": "src/locale/messages.hi.xlf"

          }

        }

      },

      "architect": {

        "build": {

          ...,

          "options": {

            ...,

            "localize": true

          },

          "configurations": {

            "production": {

              "fileReplacements": [

                {

                  "replace": "src/environments/environment.ts",

                  "with": "src/environments/environment.prod.ts"

                }

              ],

              "optimization": true,

              "outputHashing": "all",

              "sourceMap": false,

              "extractCss": true,

              "namedChunks": false,

              "extractLicenses": true,

              "vendorChunk": false,

              "buildOptimizer": true,

              "budgets": [

                {

                  "type": "initial",

                  "maximumWarning": "2mb",

                  "maximumError": "5mb"

                },

                {

                  "type": "anyComponentStyle",

                  "maximumWarning": "6kb",

                  "maximumError": "10kb"

                }

              ]

            },

            "es": {

              "localize": ["es"]

            },

            "hi": {

              "localize": ["hi"]

            }

          }

        },

        "serve": {

          ...,

          "configurations": {

            "production": {

              "browserTarget": "i18nDemo:build:production"

            },

            "es": {

              "browserTarget": "i18nDemo:build:es"

            },

            "hi": {

              "browserTarget": "i18nDemo:build:hi"

            }

          }

        }

      }

    }

  },

  "defaultProject": "i18nDemo"

}

Now update script section of package.json like

{

  "name": "i18n-demo",

  "version": "0.0.0",

  "scripts": {

    "ng": "ng",

    "start": "ng serve --configuration=production",

    "start:es": "ng serve --configuration=es",

    "build": "ng build",

    "build:prod": "ng build --configuration=production",

    "test": "ng test",

    "lint": "ng lint",

    "e2e": "ng e2e"

  },

  "dependencies": {

    ...

  },

  "devDependencies": {

    ...

  }

}

Now we can serve the app with different locales by running npm run start OR npm run start:es

We can build the app including all locales by running npm run build OR npm run build:prod

To test the build, we will use the http-server npm package so install it with npm i -g http-server. Now go to your project path & run http-server dist/i18nDemo.

Conclusion

This is how we can use multiple locales in our Angular 10 application and also we see Part of the new Angular rendering engine, Ivy, includes a new approach to localizing applications.explains the benefits and some of the implementation of this new approach.

Hire Angularjs Developers With Flexible Hiring Models for Your Angular Projects.

Shrutika

Tech Expert

Full-stack developer with around 2 years of experience. Expert in Angular, Java, NodeJs integrated web application. Skills like designing, building, integrating, understanding of responsive web development and writing efficient, maintainable and reusable code.

Get in touch for a detailed discussion.

What’s on your mind? Tell us a little bit about yourself and your question, and we will be in touch with you within 12 hours

Free eBook on Telemedicine Platform Development: All About Telemedicine

Download Free eBook Now!