Skip to main content

How to use Internationalization (i18n) in Angular

The consumption of modern web and mobile experiences is a worldwide thing. It isn’t just related to the locals around you and the surrounding culture. Therefore, just as you’d ensure that your design is aesthetically-pleasing and accessible, you should also ensure that your text is localized.

This is the process of ensuring that your application supports multiple languages. This is traditionally done in an Angular application via multiple ways.

You can, for example, use third party libraries such as ngx-translate, or, you can use the built-in i18n functionality.

We’ll specifically be looking at using the built-in i18n inside of this article.

New Angular Project
Create a new Angular project with the Angular CLI to establish a common base:

# Install the Angular CLI globally
$ npm i @angular/cli -g

# Create a new Angular project && change directory
$ ng new AngularInt

> N
> SCSS

$ cd AngularInt

# Open this up in VS Code and Serve
$ code . && ng serve
Copy
Translations
By default, Angular considers everything to be in en-US locale. We’ll have to add other locales and update our configuration to support this. You can see a list of various locales here: http://cldr.unicode.org/core-spec#Unicode_Language_and_Locale_Identifiers.

Project Templates
To create the basis for our translation project, head over to app.component.html and create the following template:

<section>
  <article>
    <h1>Under Construction!</h1>
    <p>This page is under construction.</p>
  </article>
</section>
Copy
We can also add some css to make it a little more magic, inside of app.component.scss:

section {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: #8e2de2; /* fallback for old browsers */
  background: -webkit-linear-gradient(to right, #4a00e0, #8e2de2); /* Chrome 10-25, Safari 5.1-6 */
  background: linear-gradient(to right, #4a00e0, #8e2de2);
}

article {
  text-align: center;
  color: white;
  border: 1px solid white;
  padding: 40px;
  box-shadow: 1px 1px 100px 10px rgba(0, 0, 0, 0.8);
}
Copy
Finally, inside of styles.scss let’s default the html and body styles:

html,
body {
  padding: 0px;
  margin: 0px;
}
Copy
This is now what we have:



Text Marking
Let’s start by marking text that we’d like to translate within our application. We’ll be translating the application into fr and de with Google Translate providing the translations.

Add the i18n directive to all of the text that you’d like to translate:

<section>
  <article>
    <h1 i18n>Under Construction!</h1>
    <p i18n>This page is under construction.</p>
  </article>
</section>
Copy
Then we’ll make a script inside package.json that uses the Angular CLI to extract this into a  messages.xlf file which contains all of our marked items:

{
  "scripts": {
    "int:extract": "ng xi18n"
  }
}
Copy
After adding this, run npm run int:extract inside of your terminal. Then, open up messages.xlf and you’ll see something similar to this:

<?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="48a16ab522feaff81571155668deb1a4304291f4" datatype="html">
        <source>Under Construction!</source>
        <context-group purpose="location">
          <context context-type="sourcefile">app/app.component.html</context>
          <context context-type="linenumber">3</context>
        </context-group>
      </trans-unit>
      <trans-unit id="84c778d7a95cb5dc26c9cc9feb5b7abb4d295792" datatype="html">
        <source>This page is under construction.</source>
        <context-group purpose="location">
          <context context-type="sourcefile">app/app.component.html</context>
          <context context-type="linenumber">4</context>
        </context-group>
      </trans-unit>
    </body>
  </file>
</xliff>
Copy
For each item that needs translating (i.e. has the i18n directive), a trans-unit will be created.

<trans-unit id="48a16ab522feaff81571155668deb1a4304291f4" datatype="html">
  <source>Under Construction!</source>
  <context-group purpose="location">
    <context context-type="sourcefile">app/app.component.html</context>
    <context context-type="linenumber">3</context>
  </context-group>
</trans-unit>
Copy
We can also use this structure to provide more information about the translation. This is useful if you’re getting each message translated by a third party and want to provide more information.

Inside of app.component.html, update the i18n items with a description:

<article>
  <h1 i18n="Title for the under construction card">Under Construction!</h1>
  <p i18n="A description for the under construction card.">This page is under construction.</p>
</article>
Copy
You can further add context to this by using the | seperator. This gives an item meaning and each item with the same meaning will have the same translation.

<section>
  <article>
    <h1 i18n="Card Header|Title for the under construction card">Under Construction!</h1>
    <p i18n="Card Descritpion| A description for the under construction card.">This page is under construction.</p>
  </article>
</section>
Copy
We can also give each i18n item an ID by using @@ to enforce persistence when we generate our translations:

<article>
  <h1 i18n="Card Header|Title for the under construction card@@constructionHeader">Under Construction!</h1>
  <p i18n="Card Descritpion|A description for the under construction card.@@constructionDescription">This page is under construction.</p>
</article>
Copy
Let’s build our translations once again:

$ npm run int:extract
Copy
Our items will now be updated with the id, meaning and description:

<body>
  <trans-unit id="constructionHeader" datatype="html">
    <source>Under Construction!</source>
    <context-group purpose="location">
      <context context-type="sourcefile">app/app.component.html</context>
      <context context-type="linenumber">3</context>
    </context-group>
    <note priority="1" from="description">Title for the under construction card</note>
    <note priority="1" from="meaning">Card Header</note>
  </trans-unit>
  <trans-unit id="constructionDescription" datatype="html">
    <source>This page is under construction.</source>
    <context-group purpose="location">
      <context context-type="sourcefile">app/app.component.html</context>
      <context context-type="linenumber">4</context>
    </context-group>
    <note priority="1" from="description">A description for the under construction card.</note>
    <note priority="1" from="meaning">Card Descritpion</note>
  </trans-unit>
</body>
Copy
Translations
Now that we have a messages.xlf file that contains all of the items we want to translate, we can drag this into a src/locales folder and make the respective messages.de.xlf and  messages.fr.xlf files.

At this stage, we can also update our int:extract script to handle this:

"int:extract": "ng xi18n --output-path src/locale"
Copy
French
Starting with messages.fr.xlf, let’s look to translate the messages by using target and source.

Firstly, copy everything from messages.xlf into messages.fr.xlf. We can then add a target attribute for each item, which is equal to the translation in that language.

<body>
  <trans-unit id="constructionHeader" datatype="html">
    <source>Under Construction!</source>
    <target>En construction</target>
    <context-group purpose="location">
      <context context-type="sourcefile">app/app.component.html</context>
      <context context-type="linenumber">3</context>
    </context-group>
    <note priority="1" from="description">Title for the under construction card</note>
    <note priority="1" from="meaning">Card Header</note>
  </trans-unit>

  <trans-unit id="constructionDescription" datatype="html">
    <source>This page is under construction.</source>
    <target>Cette page est en construction</target>
    <context-group purpose="location">
      <context context-type="sourcefile">app/app.component.html</context>
      <context context-type="linenumber">4</context>
    </context-group>
    <note priority="1" from="description">A description for the under construction card.</note>
    <note priority="1" from="meaning">Card Descritpion</note>
  </trans-unit>

</body>
Copy
Do the same for messages.de.xlf in Germany:

<body>

  <trans-unit id="constructionHeader" datatype="html">
    <source>Under Construction!</source>
    <target>Im Bau</target>
    <context-group purpose="location">
      <context context-type="sourcefile">app/app.component.html</context>
      <context context-type="linenumber">3</context>
    </context-group>
    <note priority="1" from="description">Title for the under construction card</note>
    <note priority="1" from="meaning">Card Header</note>
  </trans-unit>

  <trans-unit id="constructionDescription" datatype="html">
    <source>This page is under construction.</source>
    <target>Diese Seite befindet sich im Aufbau</target>
    <context-group purpose="location">
      <context context-type="sourcefile">app/app.component.html</context>
      <context context-type="linenumber">4</context>
    </context-group>
    <note priority="1" from="description">A description for the under construction card.</note>
    <note priority="1" from="meaning">Card Descritpion</note>
  </trans-unit>

</body>
Copy
Locale builds
Great! We’ve now got versions of our application that are translated based on locale.

We can use the Angular CLI to generate specific builds for each locale that we want to support.

Head over to angular.json and inside of the build settings add the configurations. I’ve omitted most of the boilerplate that already exists.

{
  "projects": {
    "AngularInt": {
      "architect": {
        "build": {
          "configurations": {
            "fr": {
              "aot": true,
              "outputPath": "dist/under-construction-fr/",
              "i18nFile": "src/locale/messages.fr.xlf",
              "i18nFormat": "xlf",
              "i18nLocale": "fr",
              "i18nMissingTranslation": "error"
            },
            "de": {
              "aot": true,
              "outputPath": "dist/under-construction-de/",
              "i18nFile": "src/locale/messages.de.xlf",
              "i18nFormat": "xlf",
              "i18nLocale": "de",
              "i18nMissingTranslation": "error"
            }
          }
        }
      }
    }
  }
}
Copy
We can also update configurations inside of serve to allow us to serve the fr and de folders. Once again, I’ve kept this brief:

{
"serve": {
  "configurations": {
    "production": {
      "browserTarget": "AngularInt:build:production"
    },
    "fr": {
      "browserTarget": "AngularInt:build:fr"
    },
    "de": {
      "browserTarget": "AngularInt:build:de"
    }
  }
}
Copy
We can now make some more scripts inside of package.json which include the ability to build and serve our new locales:

{
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "start:fr": "ng serve --configuration=fr",
    "start:de": "ng serve --configuration=de",
    "build": "ng build",
    "build:fr": "ng build --configuration=fr",
    "build:de": "ng build --configuration=de",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "int:extract": "ng xi18n --output-path src/locale"
  }
}
Copy
We can start all of our projects by running the following in the terminal:

$ npm run start
$ npm run start:fr -- --port=4201
$ npm run start:de -- --port=4202
Copy
This gives us our application three times, running in a different language each:

Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...