A Guide to Angular i18n and Localization

25 mins read
Angular i18n with Crowdin

Building Angular apps that will be accessible for people from different countries and cultures requires you to implement internationalization (i18n) and localization in web applications. In this article, we will explore the concepts of i18n and localization in Angular and provide a guide to implementing them effectively.

Why i18n and Localization are Important?

Angular is a popular web framework for developing dynamic and interactive web applications. Implementing Angular localization and internationalization features can improve your app’s accessibility, usability, and loyalty for people from different countries and cultures by providing them with a UI and content that is relevant to their region and culture. Furthermore, it can lead to increased revenue and growth, a gap in the global market which needs to be targeted.

The good news is that Angular provides built-in support for i18n and localization, making it relatively easy to implement these features in your application.

Guide to Angular i18n and Localization

We’ll provide you with a simple tutorial on how to implement Angular localization and internationalization in Angular. To view the demo app we’ll be using in this article, you can visit our GitHub repo.

Schedule a free demo with our manager to learn how to streamline your content localization

Step 1: Installing the Required Libraries

Generally, three basic libraries for Angular i18n can be used to implement internationalization:

  • @ngx-translate
  • @angular/localize
  • I18next

@angular/localize is the built-in module that is convenient and feature-rich. Most developers opt to go with it because it does not require any third-party library installation. In addition, it allows developers to easily extract translations and use an AoT (Ahead-of-Time) compiler, which converts Angular HTML and TypeScript code into efficient JavaScript code during the build phase before the browser downloads and runs that code and serves localized apps with very little effort.

On the other hand, there is a downside to using this module because @angular/localize supports either XLIFF or XMB (both are XML formats), whereas @ngx-translate supports JSON by default, but developers can write their language translation loader. Also, the built-in module forces you to build the application per language, which is tedious. You wouldn’t want to always reload your entire application when someone switches the language.

Lastly, we have I18next, a general localization framework that supports pure JavaScript and framework-powered applications. This framework is large and heavy, so we will not be using this for our example. But it has support for all features related to the translation process.

We chose to go with @ngx-translate for our example. Therefore, the following libraries are required:

@ngx-translate/core: this library provides Angular language translation services for applications.

@ngx-translate/http-loader: this library provides a way to load Angular translations from external files.

To install these libraries, run the following commands:

npm install @ngx-translate/core  
npm install @ngx-translate/http-loader

In the upcoming steps, we will see an Angular localization example application which will later be converted into Angular Internationalization.

Step 2: Set Up an Angular Application

Now, we need to set up a basic Angular application. You can create a new Angular application using the Angular CLI (Command Line Interface) by running the following command:

ng new my-app

Here, in the place of my-app you can place the name of the app, for now, let’s name it translation-app, so the command would now be:

ng new translation-app

This command will create a new Angular application named translation-app in the current directory. Once the application is created, you can navigate to the application’s directory and start the development server by running the following command:

cd translation-app  
ng serve

This command will start the development server, and you can access the application by navigating to http://localhost:4200 in your web browser.

Step 3: Adding Translatable Text

Once we have set up a basic Angular application, the next step is to add translatable text to the application’s UI, and to do that, you need to use the i18n attribute on the HTML element that contains the text. Open app.component.html and write the following text:

<h1 i18n="User welcome|An introduction header for this sample@@introductionHeader">
  Hello!
</h1>
<ng-container i18n>I cannot translate everything</ng-container>
<br />
<img [src]="logo" i18n-title title="OpenRecipe logo" alt="OpenRecipe logo"/>
<br>

<button type="button" (click)="inc(1)">+</button> <button type="button" (click)="inc(-1)">-</button>
<span i18n>Counter updated {minutes, plural, =0 {just now} =1 {one minute ago} other {{{minutes}} minutes ago}}</span>
({{minutes}})
<br><br>

<button type="button" (click)="male()">&#9794;</button> <button type="button" (click)="female()">&#9792;</button> <button type="button" (click)="other()">&#9895;</button>
<span i18n>The reader is {gender, select, male {male} female {female} other {other}}</span>
<br><br>

<span i18n>Updated: {minutes, plural,
  =0 {just now}
  =1 {one minute ago}
  other {{{minutes}} minutes ago by {gender, select, male {male} female {female} other {other}}}}
</span>

In this example, we mark the text as translatable by adding the i18n attribute to the elements. For example, Hello! in <h1> tag is marked translatable.

Step 4: Extracting Translations

After marking the translatable text, the next step is to extract it into a separate translation file. Again, angular provides a command-line interface (CLI) tool called ng xi18n.

To extract the translatable text, run the following command:

ng xi18n --output-path src/locale --i18n-format xlf

This command will extract the translatable text from your application and create an XLIFF (XML Localization Interchange File Format) file in the src/locale directory. The XLIFF file contains the source text and an empty target element for each marked translatable text, which will be replaced in the language files with .xlf extensions. So, for example, a glimpse of our English language file named messages.xlf file would look this way:

<?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="introductionHeader" datatype="html">
        <source> Hello! </source>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/app.component.html</context>
          <context context-type="linenumber">1,3</context>
        </context-group>
        <note priority="1" from="description">An introduction header for this sample</note>
        <note priority="1" from="meaning">User welcome</note>
      </trans-unit>
      <trans-unit id="5206857922697139278" datatype="html">
        <source>I cannot translate everything</source>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/app.component.html</context>
          <context context-type="linenumber">5</context>
        </context-group>
      </trans-unit>
    </body>
  </file>
</xliff>

Here’s what our French language file, named messages.fr.xlf, 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" datatype="plaintext" original="ng2.template">
    <body>
      <trans-unit id="introductionHeader" datatype="html">
        <source> Hello! </source>
        <target> Bonjour! </target>
        <context-group purpose="location">
          <context context-type="sourcefile">app\app.component.ts</context>
          <context context-type="linenumber">4</context>
        </context-group>
        <note priority="1" from="description">An introduction header for this sample</note>
        <note priority="1" from="meaning">User welcome</note>
      </trans-unit>
      <trans-unit id="5206857922697139278" datatype="html">
        <source>I cannot translate everything</source>
        <target>Je ne peux pas tout traduire</target>
        <context-group purpose="location">
          <context context-type="sourcefile">app\app.component.ts</context>
          <context context-type="linenumber">10</context>
        </context-group>
      </trans-unit>
    </body>
  </file>
</xliff>

Both messages.xlf and messages.fr.xlf files are quite similar but you can notice in the file messages.fr.xlf that there is a new tag name <target></target>, this is actually the translatable text written in the desired language that needs to be translated. So in this case, the target tag would have the French translation of the English text.

Step 5: Loading Translations for your Angular app

After extracting and translating the translatable text, the next step is to load the translations in your application. Angular provides a built-in module called HttpClientModule that allows you to fetch the translations from a remote server or load them from a local file. So our app.module.ts would look as follows:

import { NgModule } from  '@angular/core';
import { BrowserModule } from  '@angular/platform-browser';
import { TranslateLoader, TranslateModule } from  '@ngx-translate/core';
import { TranslateHttpLoader } from  '@ngx-translate/http-loader';
import { AppComponent } from  './app.component';

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ AppComponent ],
  bootstrap: [ AppComponent ]
})
export  class AppModule { }

And our app.component.ts would look as follows:

import { Component } from  '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export  class AppComponent {
  minutes = 0;
  gender = 'female';
  fly = true;
  logo = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSpvdaVGSQVXaKRkoDKFkb32BY37Pi6wEx_og&usqp=CAU';

  inc(i: number) {
    this.minutes = Math.min(5, Math.max(0, this.minutes + i));
  }

  male() { this.gender = 'male'; }
  female() { this.gender = 'female'; }
  other() { this.gender = 'other'; }
}

Step 6: Translating Text

It is finally time to translate our texts. Open index.html and replace it with the following:

<!DOCTYPE html>
<html>
  <head>
    <base href="/">
    <title>Angular i18n example</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <app-root>Loading...</app-root>
  </body>
</html>

Step 7: Setting up and Styling the Landing Page

Lastly, set up your main.ts file and styles.css to load and give a pleasing effect to your page. So, your main.ts should look as follows:

import { platformBrowserDynamic } from  '@angular/platform-browser-dynamic';

import { AppModule } from  './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));

Before adding styles.css to your app, it will look like as follows:

The page does not look aesthetically pleasing, right? Nevertheless, we can improve the overall design of our landing page by applying the following styles.css mentioned below:

* {
  font-family: Arial, Helvetica, sans-serif;
}

h1 {
  color: #264D73;
  font-size: 2.5rem;
}

h2,
h3 {
  color: #444;
  font-weight: lighter;
}

h3 {
  font-size: 1.3rem;
}

body {
  padding: 0.5rem;
  max-width: 1000px;
  margin: auto;
}

@media (min-width: 600px) {
  body {
    padding: 2rem;
  }
}

Toutes nos félicitations! (Congratulations!) you have successfully applied the Angular i18n and Angular Localization in your application. The original page should be:

And the translated page should be:

How do I Localize Plurals, Numbers, And Strings in the App?

Localizing plurals, dates, numbers, and strings is a common issue to face because it requires different formatting rules depending on the language and region.

In our example, as you can see above, we have localized the plurals and numbers by creating a counter to be displayed on the page, as mentioned in our app.component.html:

<span i18n>Counter updated {minutes, plural, =0 {just now} =1 {one minute ago} other {{{minutes}} minutes ago}}</span>

As you can see, we have used plural form selectors =0 for just now and =1 for one minute ago in the form of Angular’s syntax (according to our example), which overrides the language’s formal selectors one, few, other, etc. In addition, we have also added localization to the numbers when we start increasing the counter, whether the page is in English or French.

For English,

And for French,

Lastly, let’s again refer to our translation files, messages.xlf, where we can see <trans-unit></trans-unit> tags which Angular generates to refer that these strings need to be localized, as shown below:

<trans-unit id="5206857922697139278" datatype="html">
  <source>I cannot translate everything</source>
  <context-group purpose="location">
    <context context-type="sourcefile">src/app/app.component.html</context>
    <context context-type="linenumber">5</context>
  </context-group>
</trans-unit>

We can also see these tags in the messages.fr.xlf with the additional tag of <target></target> with <source></source> to replace the source text, which is in English and needs to be translated into French.

<trans-unit id="5206857922697139278" datatype="html">
  <source>I cannot translate everything</source>
  <target>Je ne peux pas tout traduire</target>
  <context-group purpose="location">
    <context context-type="sourcefile">app\app.component.ts</context>
    <context context-type="linenumber">10</context>
  </context-group>
</trans-unit>

How do I Use Crowdin to Help with Angular App Localization?

Here we can see that only dealing with two languages, we had to manage the translation files (.xlf files). Imagine if the scope of the application were even larger than this, it would have been very difficult to manage the translation files because each time we had to duplicate the source locale for each of our other supported locales to keep all our files in sync while we make changes in our app.

This is where Crowdin comes in. It can help you sync your source and translation files between your repo and a translation project in Crowdin. This way the process of sending new files for translation and downloading translations can become automated, no copy-pasting needed. Let’s go through some of the unique features that Crowdin offers you:

Adding Angular Application Files to Crowdin Using Command Line Interface (CLI)

You can use Crowdin CLI to integrate your Angular application files. For example, for our project, we have component, services, and modules files (.ts extension), HTML files (.html extension), and especially, translation files (.xlf extension) which need to be constantly in synchronization during development. The CLI makes it easier to keep those files in sync without worrying about keeping them up to date so that you can focus more on coding.

After the successful installation of Crowdin CLI, you need to use the following command to run the application:

crowdin

It will initiate the application. But before trying to use the Crowdin CLI, we first need to set up the configuration file in the root directory of the project, which is created using the command as follows:

crowdin init

On the other hand, if we did not create the file in the root directory of the project, we can always specify the path using the –config command:

crowdin upload sources --config /path/to/your/config/file

After creating the configuration file, we can finally start uploading our source files in Crowdin through the following command:

crowdin upload sources

And then, we can upload and automate the synchronization of our translation files by running the command mentioned below:

crowdin upload translations

As previously stated, with automatic synchronization, we would not have to worry about keeping them updated while developing our application. It is a time-consuming process that leads to more errors and inefficient code.

Additionally, since you have the feature to upload the files, Crowdin offers to download single (specific language translation) or multiple translations for our application using the commands below.

For downloading single-language translation:

crowdin download -l {language_code}

Here you can replace language_code with fr for French, en for English, es for Spanish, etc. For downloading multiple translations:

crowdin download

Or:

crowdin pull

Finally, if you want to check the status of your project for proofreading and translations, use the following command.

crowdin status

On the other hand, if you are not comfortable with using CLI, you can also upload your files manually or by using integration options such as GitHub, Bitbucket, etc.

Schedule a free demo with our manager to learn how to streamline your content localization

Multiple Languages Support

Crowdin offers and supports a wide range of languages. After adding the source files to Crowdin, you can add languages one at a time or import them from a file and specify which languages should be translated.

Translation Management Services

Crowdin will automatically detect changes to the source files in your Angular app and update the translations in real time, which allows you to keep your app updated with the most recent translations. In addition, it offers various tools for translation management, which include:

  • Glossary - a glossary of terms and phrases can be created to ensure translation consistency.

  • Machine Translation - can be used for quick translations of non-critical content or as a step before post-editing.

  • Translation Memory (TM) - allows for the automatic suggestion of translations based on previous translations.

  • Translation context - screenshots, comments, string max length, and more, so your translators can make accurate translations.

Translation Quality Assurance

Crowdin provides several quality assurance tools. Code parts, like placeholders or syntax will be highlighted for translators, so they’ll know whic parts shoudn’t be translated. Also, enabled QA checks will show a warning if a translator tries to save the translation with some of the untranslatable parts of the string altered.

You can preview the translations in the context of your app using the In-context Preview tool and make any necessary changes. You can also edit translations directly in the Crowdin platform using the Crowdin Editor. In addition, you can also run a report to look for untranslated strings or other problems. Finally, when you are satisfied with the translations, you can download them in the format you want and integrate them into your Angular app.

Go Ahead with Angular App Localization

Internationalization and localization are essential for creating web applications that a global audience can access. This article provided an example of an application built using Angular localization and Angular i18n features using the @ngx-translate/core and @ngx-translate/http-loader libraries. With the help of these angular libraries for i18n and localization, you can create a high-quality, internationalized application that can reach a global audience and provide a great user experience for all.

[Free E-book]Localize your content with Crowdin

Learn how to set up a continuous localization workflow to grow with multiple languages faster than with one. Experience and tips from 10+ localization experts.
Emad Bin Abid