Angular 7

Localization In Angular Using i18n Tools

Introduction

In this article, we will learn how to make our Angular app available in different languages using i18n and localization. We will create an Angular application and configure it to serve the content in three different languages. We will also deploy our app to Google Firebase and see how localization works in real time.

We will use Angular 7 and VS Code to develop our application. Take a look at the application output.

Source Code

Get the source code for this application from GitHub. The source code has been updated to Angular 8.

What is i18n?

I18n , also known as internationalization is the process of making our app support various languages to extend the reach to a worldwide audience.

What is Localization?

Localization is the process for translating the app to a particular language. We need to apply internationalization to the application and after that we can localize it. Localization allows us to serve our application in different languages.

Creating an Angular 7 app

The first step is to create an Angular 7 app. If you are new to Angular, I would suggest you to read my article Getting Started With Angular 7.0 to learn how to setup Angular development environment in your machine.

Run the following command to create the app.

ng new i18nDemo

Open the i18nDemo app using VS code.

Setting up the app component

Open app.component.html file. Replace the already existing text with the following code.

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

<h3 i18n="@@myName">
  Hello, My name is Ankit
</h3>
<p>This text will remain same in all languages</p>
<hr />

You can observe that we have marked <h1> and <h3> tags with i18n attribute. This is a way to tell the Angular to consider this text as translatable content. We will explore i18n attribute in details in the next section.

Creating a translation source file

Run the following command in the CLI to create a translation source file.

ng xi18n --output-path translate

It will create a folder called translate and create a messages.xlf file inside it. Open the file and you can observe the following XML code inside it.

<?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="3f2feb6d5fb690628177afa3ade2519db69ba838" datatype="html">
        <source>Localization Demo in Angular using i18n</source>
        <context-group purpose="location">
          <context context-type="sourcefile">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 Ankit</source>
        <context-group purpose="location">
          <context context-type="sourcefile">app/app.component.html</context>
          <context context-type="linenumber">5</context>
        </context-group>
      </trans-unit>
    </body>
  </file>
</xliff>

This file contains a list of <trans-unit> tags. These tags will have all the content that was marked for translation using i18n attribute. You can also observe that each <trans-unit> tag has an id property associated with it. This unique id will be generated by default for each tag that was marked with i18n attribute. We can also customize the id by providing a name prefixed with @@ as we have done with <h3> tag in previous section. Hence, the id for <h3> tag is “myName” as we defined it. There is no entry for the <p> tag in translation file because we have not marked it with i18n attribute. Angular translation tool will not consider it for translations.

If you change the text for any tag in your HTML file, you need to regenerate the translation file. Regenerating the file will override the default id of <trans-unit> tags. Hence, it is advisable to provide custom ids to each translatable tag to maintain consistency.

Hence, we have successfully implemented i18n to our app. In the next section, we will extend it to make it available to different languages.

Translating the content

We will translate our application in two new language apart from English, which are Spanish and Hindi. Make three copies of the messages.xlf file and rename them to messages.en.xlf, messages.es.xlf and messages.hi.xlf. These file names can be customize as per your choice but the extension should be .xlf.

Open messages.es.xlf and put in the following content in it.

<?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="3f2feb6d5fb690628177afa3ade2519db69ba838" 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">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 Ankit</source>
        <target>Hola, mi nombre es Ankit</target>
        <context-group purpose="location">
          <context context-type="sourcefile">app/app.component.html</context>
          <context context-type="linenumber">5</context>
        </context-group>
      </trans-unit>
    </body>
  </file>
</xliff>

This is the same content as the original messages.xlf file, but we have added a <target> tag corresponding to each <source> tag. The <target> tag contains the translated text for the content inside the <source> tag. Here I am using Google translate for the translation but in real time applications, a language expert will translate the contents from messages.xlf file.

Similarly open the messages.hi.xlf and put in the following content in it.

<?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="3f2feb6d5fb690628177afa3ade2519db69ba838" datatype="html">
        <source>Localization Demo in Angular using i18n</source>
        <target>I18n का उपयोग कर कोणीय में स्थानीयकरण डेमो</target>
        <context-group purpose="location">
          <context context-type="sourcefile">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 Ankit</source>
        <target>हेलो, मेरा नाम अंकित है</target>
        <context-group purpose="location">
          <context context-type="sourcefile">app/app.component.html</context>
          <context context-type="linenumber">5</context>
        </context-group>
      </trans-unit>
    </body>
  </file>
</xliff>

Finally, we will make English translation file. Open messages.en.xlf and put in the following content in it.

<?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="3f2feb6d5fb690628177afa3ade2519db69ba838" datatype="html">
        <source>Localization Demo in Angular using i18n</source>
        <target>Localization Demo in Angular using i18n</target>
        <context-group purpose="location">
          <context context-type="sourcefile">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 Ankit</source>
        <target>Hello, My name is Ankit</target>
        <context-group purpose="location">
          <context context-type="sourcefile">app/app.component.html</context>
          <context context-type="linenumber">5</context>
        </context-group>
      </trans-unit>
    </body>
  </file>
</xliff>

Configure the app to serve in multiple language

Open angular.json file and add the following configuration.

"build": {
  ...
  "configurations": {
    ...
    "es": {
      "aot": true,
      "i18nFile": "src/translate/messages.es.xlf",
      "i18nFormat": "xlf",
      "i18nLocale": "es",
      "i18nMissingTranslation": "error"
    }
  }
},
"serve": {
  ...
  "configurations": {
    ...
    "es": {
      "browserTarget": "i18nDemo:build:es"
    }
  }
}

Here we have added the configuration for Spanish language. We have provided the path and format for i18n file and set the locale to “es”. When we execute the application, app content will be served from the i18n file path provided.

Similarly you can add configuration for other languages.

Execution Demo

Once you have added the configuration for all the languages in angular.json file, run the following command to start the server.

ng serve --configuration=es

This will launch the application in “es” configuration and our app will show the Spanish language translations.

Refer to the output screen as shown below:

The configurations that we have defined will only help the app to run in the local machine. We cannot change the configuration once the app is launched.

A production app will need the application to serve for different language just by changing the URL. e.g. mywebsite.com/es will provide the Spanish version of site and mywebsite.com/en will provide the English version. In this case, the app will be served from different virtual directories for different language. We will explore how to do this in next section.

Modify the app component for production

Open app.component.ts and put in the following code.

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'i18nDemo';

  languageList = [
    { code: 'en', label: 'English' },
    { code: 'hi', label: 'हिंदी' },
    { code: 'es', label: 'Espanol' }

  ];

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

Here we have defined a list of languages and its locale code. These locale codes are standard codes. You can easily get list of language and the corresponding locale code by a simple Google search.

Add the following codes in app.component.html file.

<ng-container *ngFor="let language of languageList">
  <a href="/{{language.code}}/"><button class="button">{{language.label}}</button></a>
</ng-container>

Here we have defined three buttons for three languages. On each button click, the locale id will change and the locale id will be appended to URL also. This will allow us to serve the application from different directory.

Put the following code in app.component.css file to apply styles to these buttons.

.button {
  background-color: darkslateblue;
  border-radius: 5px;
  color: white;
  padding: 5px;
  width: 10%;
  margin: 5px;
  text-decoration: none;
  cursor: pointer;
}

Script to compile the app for production

We need to have three different serving location for three different languages. To build the application package for one language for production we will use the following command:

ng build --prod --i18n-locale es --i18n-format xlf --i18n-file src/translate/messages.es.xlf --output-path=dist/es --baseHref /es/

Let us understand this command. We provided the locale id for package, which is “es” for Spanish. We also provide the i18n file path and format. The output path property is required to provide the location for application package. The baseHref property specifies the base URL from which this package will be served.

We need to run this command for every language we will provide by changing the i18n file path and baseHref attribute values. However, this will be a cumbersome task if we have a lot of languages. Therefore, we will write a script to generate package for all language. Open package.json file and add the following scripts inside the “scripts” section.

"build-locale:en": "ng build --prod --i18n-locale en --i18n-format xlf --i18n-file src/translate/messages.en.xlf --output-path=dist/en --baseHref /en/",
"build-locale:es": "ng build --prod --i18n-locale es --i18n-format xlf --i18n-file src/translate/messages.es.xlf --output-path=dist/es --baseHref /es/",
"build-locale:hi": " ng build --prod --i18n-locale hi --i18n-format xlf --i18n-file src/translate/messages.hi.xlf --output-path=dist/hi --baseHref /hi/",
"build-locale": "npm run build-locale:en && npm run build-locale:es && npm run build-locale:hi"

Here we have created three scripts for three languages we are using. The “build-locale” script will execute all of them at once. All these scripts are key-value pairs. The key names we are using here are customizable and you can use any name of your choice. To create the application package for all the language, run the following command:

npm run build-locale

On successful execution, it will create a “dist” folder in the application’s root folder. The dist folder has three sub-folders to serve our application in three different languages. Refer to the image shown below:

Deploying the application on Firebase

We will deploy this application on Firebase to see the language change in real time. Refer to my article Hosting A Blazor Application on Firebase and follow the steps mentioned to deploy this Angular app on Firebase.

Once the application is deployed, you will get the hosting URL. Open the URL and append the baseHref attribute as we defined earlier, to the URL. Hence, the URL will be yoursite.com/es/ for Spanish language and so on.

The application, which we built here, is hosted at https://i18ndemo-415ef.firebaseapp.com/en/. If you open this URL, you will see the output as shown below:

Click on the links provided. The URL will change and application will reload in new language.

Conclusion

We learned how to internationalize our Angular app using i18n tools. We also applied localization to Angular application. Localization allows us to serve our app in different language, which helps in extending the reach to a worldwide audience. We learned how localization works in a production environment by deploying our application on Firebase.

Get the source code from GitHub and play around for a better understanding. You can also read this article at C# Corner

Preparing for interviews !!! Read my article on C# Coding Questions For Technical Interviews

See Also

Ankit Sharma

Full Stack Consultant | GDE for Angular | Microsoft MVP | Author | Speaker | Passionate Programmer

View Comments

  • I have a similar setup in a project I'm working on. How do you go about maintaining the translated documents? Every time you add a new translation id, you have to extract again and manually reconcile the differences in the documents containing translation targets. Leaning towards scripting it myself, but maybe you know something I don't.

    • Yes.If you change the original content or add a new content you need to extract the translation file again. For large translation file you can integrate some kind of CMS tool to create and maintain the translation files. Another option is to write some sort of scripts to generate files automatically whenever the original content changes.
      Well these are just my opinion, it might differ with implementation.

  • Thanks a lot! Your sample runs on Firebase but not on localhost. There the base-href remains /. Any ideas?

    • My sample which is on GitHub will run only on Firebase. If you want to execute on localhost, then you need to change configuration as shown in this article.

          • Hi Ankit, I have the same problem. Building the three dist folders works fine, starting each folder works fine as well. However, I could not manage to start the angular app so that it would serve all three folders. It just does not find the other folders. I created build and start configs for each language in angular.json it just doesn't work. Do you have any hint? Kind regards Clemens

  • Hi,
    I was able to generate separate build for different languages & I could able to run it locally but couldn't deploy it to firebase to serve all different languages in a single deployment. I found your article about firebase deployment for this particular multi-language support as insufficient. Please guide me with this regards. Anyway this article was helpful.
    Thank you

    • The deployment steps mentioned are working fine. Please follow them properly and you will be able to deploy it on Firebase.

  • Hi. I'm trying to deploy the build on a subdomain but I haven't undestood if I've to upload all content under projectname directory and then the "en" and "it" generate subfolders to the root of subdomain. I've done so but I can't switch language.
    Thank you.

  • Dear Ankit Sharma,

    How to deploy on firebase this project...
    I deploy on firebase to this project, Langauage switcher not working.

  • As requesting again, Please mention which article to follow to execute on localhost, because i am unable to find could you pls provide the steps? so that i can run on my localhost. Thanks in advance

    • The steps mentioned in the article are working correctly. Please read the article again and follow the steps properly.

  • I am unable to find and read 3 times already that's why asking how to do it if u can help will be really appreciate

  • Hi Ankit,
    I built the app according to the guidelines in the article but while clicking on the bottom language button it does not change the language but only changes the url. Then I cloned your repo from github and tried to run it and it has also the same problem.

    • Replace this line to
      this.siteLocale = window.location.pathname.split('/')[1];

      this.siteLocale = window.location.pathname.split('/')[2];

Recent Posts

Announcing A New Blazor Course

Introduction Blazor is a .NET web framework that allows us to create client-side applications using…

3 years ago

How To Solve Sudoku Using Azure Form Recognizer

Introduction In this article, we are going to create a sudoku solver with the help…

4 years ago

Going Serverless With Blazor

Introduction In this article, we will learn how to implement Azure serverless with Blazor web…

5 years ago

Announcing A Free eBook On Angular and Firebase

Introduction Angular is an open-source framework that allows us to create applications for multiple platforms…

5 years ago

Optical Character Reader Using Angular And Azure Computer Vision

Introduction In this article, we will create an optical character recognition (OCR) application using Angular…

5 years ago

Optical Character Reader Using Blazor And Computer Vision

Introduction In this article, we will create an optical character recognition (OCR) application using Blazor…

5 years ago