Menu

Angular localization with Ivy

September 9th, 2020

Angular i18n logo with colored circles featuring Hello in different languages.

Part of the new Angular rendering engine, Ivy, 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.

Prior to 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 as well as content; they can include complexly 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.

The build pipeline of a translated Angular application — pre-Ivy
(build times will vary based on project size)

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 not possible 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.

Angular localization with Ivy
(build times will vary based on project size)

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 post-processing of the translations 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.

Similar improvements have been seen in real life by teams already using this approach:

Angular localization with Ivy

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.

Currently, the use of $localize in application code is not yet publicly supported or documented. We will be working on making this fully supported in the coming months. It requires new message extraction tooling — the current (pre-Ivy) message extractor does not find $localize text in application code. This is being integrated into the CLI now and should be released as part of 10.1.0.

We are also looking into how we can better support translations in 3rd party libraries using this new approach. Since this would affect the Angular Package Format (APF) we expect to run a Request for Comment (RFC) before implementing that.

In the meantime, enjoy the improved build times and keep an eye out for full support of application-level localization of text.

The original post Angular localization with Ivy.