Introduction to i18n for Angular

by Stephen Fluin

2019-12-07

Introduction to i18n for Angular
This article was written for version 9.0 of Angular or later
English is the most common language for the web, but if you ignore other languages and locales, you're missing out on 80% of the planet. In my experience at companies like Trello and Google, I saw first hand that localization (l10n) and internationalization (i18n) can be a competitive advantage.
Supporting customers in other regions by understanding their language and expectations can have great rewards. Every user prefers an application that treats you like a first class citizen?

I18n in Angular

Angular was built with internationalization in mind. There are built-in capabilities for localizing dates, currency, numbers, and more, but part of the magic comes from the declarative templates.
The way you build applications in Angular is to create a tree of components, each with an HTML template. In that template you'll see normal HTML, as well as custom Angular components.
In this intro, I'm going to momentarily ignore pipes and complexities like genders, plurals, and focus on the basics of i18n. There's an awesome full i18n guide on angular.io.
Alexander Nevsky Cathedral

Step 1:i18n markers in elements

Angular has built-in support for translations and i18n as part of the template syntax.
Let's imagine we have a span and a my-component. Both are marked for internationalization the same way. Add what looks like an i18n attribute. This will be stripped away by the build.
<span i18n>Hello!</span>
<my-component i18n>Goodbye!<my-component>

For both of these, the i18n markers indicate that the strings within the tags should be translated.

Step 2:i18n-X markers in properties/attributes

Similarly we can mark properties/attributes for translation. Let's imagine a simple image.
<img alt="Hello world" i18n-alt src="my-image.jpg">

The i18n-alt marker indicates that the "alt" attribute should be translated.

Step 3: Install @angular/localize

Add a dependency on @angular/localize in your package.json. The version number should match the rest of the Angular framework packages

Step 4: Extract Strings

The CLI has the capability of extracting all of the marked strings into an industry-standard format, such as XLIFF.
ng xi18n

x18n refers to 'extract internationalization". This will render a new file located atsrc/messages.xlf. Open this messages file in a translation tool (eg Virtaal) or pass it to a translation house for translation.
What you should get back is a translated XLIFF file. Let's imagine I get back a french file and store it at src/messages.fr.xlf.

Step 5: Configure angular.json

As of 9.0 and Ivy, multiple translated builds can be quickly produced as part of the CLI's standard build process. This used to be really slow and somewhat manual.
Within your project key in angular.json, add an i18n key and specify the source locale and locales you would like translated, along with their translated XLIFF file. The original strings in this example were written in English (en-US), and the the new language is French (fr).
"i18n": {
    "sourceLocale": "en-US",
    "locales": {
        "fr": "src/messages.fr.xlf"
    }
}

You'll also have to set "localize" to true in your angular.json configuration options. This belongs, for example, within angular.json, look for/projects/my-project/architect/build/configurations/production/localize.
"localize": true

Step 6: Run a build

Based on the newly configured angular.json file, every time you run a production build with ng build --prod, you'll get two (or more) folders in your /dist/ directory, one for each locale and the original locale.
With build-time i18n, there's NO performance overhead at runtime, as each set of static files is entirely dedicated to a single language. Runtime i18n, where messages are loaded as part of application bootstrap, will be made available in the future.
To see your site live, just host each version of the site in a different folder or different domain. This could be example.org/en-US/ and example.org/fr/, or it could be example.org and fr.example.org.