Software localization

Angular Tutorial on Localizing with Transloco

Transloco is an Angular i18n library that lets you translate content in different languages and switch between them easily in runtime.
Software localization blog category featured image | Phrase

Angular localization is a process with many moving parts. If you want to go beyond Angular's built-in i18n library and look into third-party Angular libraries for internationalization, then Transloco may be the right choice for your needs.

This tutorial will walk you through the process of localizing Angular apps with Transloco step by step. We will create a sample feedback form for Phrase, the most reliable localization solution, and launch a demo app in three different languages.

On top of it, we will also use Phrase to create translation files for our app and speed up the overall localization process. Shall we start?

Getting Ready for Angular Localization

You can access the demo app via Google Firebase to understand how Transloco works with an Angular app in a production environment. The source code is available on GitHub.

Make sure that you have an Angular development environment set up on your machine. Should this not be the case, make sure you install the following software before proceeding:

Creating an Angular Application

Navigate to the directory where you want to create the new project. Open the command prompt, and run the command shown below to create a new Angular app named phrase-transloco-i18n.

ng new phrase-transloco-i18n --routing=false --style=css

Open the project in your code editor. Here, we are using VS code, hence we will execute the following set of commands:

cd phrase-transloco-i18n

code .

These will change the directory to the root folder of the project, i.e., phrase-transloco-i18n and open the project in VS Code.

What is Transloco?

Transloco is an internationalization (i18n) library for Angular. It allows us to define the content of our app in different languages and switch between them easily at runtime. We can easily manage the translation in our app with the help of Transloco's APIs.

Here are just a few of Transloco's best features:

  • Clean and DRY templates,
  • Support for lazy loading of modules,
  • Support for multiple languages,
  • Easy to test,
  • Support for localization (l10n).

To explore even more, check out the official Transloco docs.

Install Transloco in Your Angular Project

Run the following command in the root directory of the project:

ng add @ngneat/transloco

After running this command, you get two questions. Answer them as shown below:

  • Which languages do you need? (en, es, de)
  • Are you working with server-side rendering? (N)

Refer to the image shown below:

Command line install | Phrase

After the successful execution of the command, a folder called "i18n" with three JSON files inside it will be created in the assets folder. At this point in time, these files are empty. We will add the translations to them in the latter part of this article.

Asset folder | Phrase

The command will also create a new file called transloco-root.module.ts and inject it into the AppModule.

import { HttpClient } from '@angular/common/http';

import {

  TRANSLOCO_LOADER,

  Translation,

  TranslocoLoader,

  TRANSLOCO_CONFIG,

  translocoConfig,

  TranslocoModule

} from '@ngneat/transloco';

import { Injectable, NgModule } from '@angular/core';

import { environment } from '../environments/environment';

@Injectable({ providedIn: 'root' })

export class TranslocoHttpLoader implements TranslocoLoader {

  constructor(private http: HttpClient) { }

  getTranslation(lang: string) {

    return this.http.get<Translation>(`/assets/i18n/${lang}.json`);

  }

}

@NgModule({

  exports: [TranslocoModule],

  providers: [

    {

      provide: TRANSLOCO_CONFIG,

      useValue: translocoConfig({

        availableLangs: ['en', 'es', 'de'],

        defaultLang: 'en',

        // Remove this option if your application doesn't support changing language in runtime.

        reRenderOnLangChange: true,

        prodMode: environment.production,

      })

    },

    { provide: TRANSLOCO_LOADER, useClass: TranslocoHttpLoader }

  ]

})

export class TranslocoRootModule { }

This file sets up configuration options, such as the available languages for the translations, the default language of the app, and if the app should change, the language during runtime.

Install ngx-bootstrap

ngx-bootstrap is an open-source library that provides an easy way to integrate Bootstrap components into an Angular app. To add ngx-bootstrap to your application, run the following command in the root directory of the project.

ng add ngx-bootstrap

Update the AppModule

We will use the dropdown module of ngx-bootstrap to display a language selection dropdown in the navbar of our app. To create the feedback form, we will use the template-driven forms. Just add the following lines of code in the src/app/app.module.ts file to import the required modules.

import { BsDropdownModule } from 'ngx-bootstrap/dropdown';

import { FormsModule } from '@angular/forms';

@NgModule({

  ...

  imports: [

    ...

    FormsModule,

    BsDropdownModule.forRoot(),

  ],

    ...

})

Add the Font Awesome Library to Your Application

Font Awesome is an open-source library that provides a wide array of icons that can be used to style our app. To include Font Awesome in your app, add the following lines in the <head> section of the index.html file.

<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />

Create the nav-bar Component

Run the following command to create the nav-bar component for our application.

ng g c nav-bar --module app

Open src\app\nav-bar\nav-bar.component.html and replace what you see there with the following code.

<nav class="navbar navbar-dark navbar-expand-lg">

    <a class="navbar-brand">Phrase</a>

    <span class="spacer"></span>

    <div class="btn-group" dropdown>

        <button id="button-animated" dropdownToggle type="button" class="btn btn-link dropdown-toggle"

            aria-controls="dropdown-animated"><i class="fa fa-globe" aria-hidden="true"></i> {{siteLanguage}}

            <span class="caret"></span>

        </button>

        <ul id="dropdown-animated" *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu"

            aria-labelledby="button-animated">

            <li role="menuitem"><a class="dropdown-item" (click)="changeSiteLanguage('en')">English</a></li>

            <li role="menuitem"><a class="dropdown-item" (click)="changeSiteLanguage('es')">Español</a></li>

            <li role="menuitem"><a class="dropdown-item" (click)="changeSiteLanguage('de')">Deutsch</a></li>

        </ul>

    </div>

</nav>

We have created a nav-bar which contains a drop-down menu having three options to set the language of our app. When we click on the menu item, it will invoke the changeSiteLanguage method which will change the app language and the content will be served in the selected language. We will also display the siteLanguage in the nav-bar and it will update as we change the language of our app.

Open src\app\nav-bar\nav-bar.component.ts and replace the existing code with the following code.

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

import { BsDropdownConfig } from 'ngx-bootstrap/dropdown';

import { TranslocoService } from '@ngneat/transloco';

@Component({

  selector: 'app-nav-bar',

  templateUrl: './nav-bar.component.html',

  styleUrls: ['./nav-bar.component.css'],

  providers: [{ provide: BsDropdownConfig, useValue: { isAnimated: true, autoClose: true } }]

})

export class NavBarComponent {

  siteLanguage = 'English';

  languageList = [

    { code: 'en', label: 'English' },

    { code: 'es', label: 'Español' },

    { code: 'de', label: 'Deutsch' }

  ];

  constructor(private service: TranslocoService) { }

  changeSiteLanguage(language: string): void {

    this.service.setActiveLang(language);

    this.siteLanguage = this.languageList.find(f => f.code === language).label;

  }

}

Here, we have defined a list of languages and their standard locale codes. The changeSiteLanguage method will invoke the setActiveLang method of the Transloco service to set the active language of the app to the language selected in the nav-bar menu. We will then set the siteLanguage by fetching the language name from the list of languages corresponding to the currently active language.

Add the following style to src\app\nav-bar\nav-bar.component.css file

nav {

  background-color: #1565c0;

  color: #ffffff;

}

button:focus {

  outline: none;

  border: 0;

}

.spacer {

  flex: 1 1 auto;

}

#button-animated {

  text-decoration: none;

  color: #ffffff;

}

Create the Default Translation File

Before creating the feedback form, we will create the language JSON file for the default language of the app (English). Open the src\assets\i18n\en.json file, and put the following code inside it.

{

  "title": {

    "label": "Phrase Service Feedback"

  },

  "name": {

    "label": "Name",

    "placeholder": "Enter Name",

    "error": " Name is required"

  },

  "gender": {

    "label": "Gender",

    "error": " Gender is required",

    "options": {

      "male": "Male",

      "female": "Female"

    }

  },

  "comment": {

    "label": "Comment",

    "placeholder": "Enter Comment",

    "error": " Comment is required"

  },

  "rating": {

    "label": "Rate our customer service: ",

    "error": "Rating is required",

    "options": {

      "excellent": "Excellent",

      "good": "Good",

      "bad": "Bad"

    }

  },

  "submit": {

    "label": "Submit"

  }

}

Here we have created the key-value pairs for all our translatable content. The feedback form will have four fields – name, gender, comment, and rating.

Create a Model

We will use the template-driven form for creating the feedback form for our application. Therefore, we need to create a model. Set up a new folder called models inside the src/app folder. Create a file called feedback.ts inside the model's folder and insert the following code:

export class Feedback {

    name: string;

    gender: string;

    rating: string;

    comment: string;

    constructor() {

        this.name = '';

        this.gender = '';

        this.rating = '';

        this.comment = '';

    }

}

If you are not familiar with Angular forms, please refer to the Angular Forms guide.

Create the Feedback Component

Run the following command to create the feedback component.

ng g c feedback --module app

Open src\app\feedback\feedback.component.ts, and put the following code inside the FeedbackComponent class:

customerFeedback = new Feedback();

constructor() { }

saveFeedback(): void {

  alert('Thanks for your valuable feedback!!!\nThe feedback has been submitted succesfully.');

  console.table(this.customerFeedback);

}

We will create an object of the Feedback class type, which will bind to the form. The saveFeedback method will be invoked upon successful form submission.

Open src\app\feedback\feedback.component.html and add the form as shown below. You can refer to GitHub for the complete source code.

<form #feedbackForm="ngForm" (ngSubmit)="feedbackForm.form.valid && saveFeedback()" novalidate>

  <div class="form-group" *transloco="let t; read: 'name'">

    <label>{{ t('label') }}</label>

    <input type="text" [placeholder]="t('placeholder')" class="form-control" [(ngModel)]="customerFeedback.name"

      name="name" #name="ngModel" required>

    <span class="text-danger" *ngIf="(name.touched || feedbackForm.submitted) && name.errors?.required">

      {{ t('error') }}

    </span>

  </div>

  <div class="form-group" *transloco="let t; read: 'gender'">

    <label>{{ t('label') }}</label>

    <select class="form-control" data-val="true" [(ngModel)]="customerFeedback.gender" name="gender" #gender="ngModel" required>

      <option value="">Select Category</option>

      <option value="{{ t('options.male') }}">{{ t('options.male') }}</option>

      <option value="{{ t('options.female') }}">{{ t('options.female') }}</option>

    </select>

    <span class="text-danger" *ngIf="(gender.touched || feedbackForm.submitted)&& gender.errors?.required">

      {{ t('error') }}

    </span>

    </div>

    <div class=" form-group" *transloco="let t; read: 'comment'">

        <label>{{ t('label') }}</label>

        <textarea type="text" [placeholder]="t('placeholder')" class="form-control" [(ngModel)]="customerFeedback.comment" name="comment" #comment="ngModel" required>

        </textarea>

    <span class="text-danger" *ngIf="(comment.touched || feedbackForm.submitted)&& comment.errors?.required">

      {{ t('error') }}

    </span>

    </div>

    <div class="form-group" *transloco="let t; read: 'rating'">

    <label>{{ t('label') }}</label>

    <div>

      <div class="custom-control custom-radio custom-control-inline">

        <input id="excellent" type="radio" class="custom-control-input" name="rating" [value]="t('options.excellent')"

          [(ngModel)]="customerFeedback.rating" #rating="ngModel" required>

        <label class="custom-control-label" for="excellent">{{ t('options.excellent') }}</label>

      </div>

      <div class="custom-control custom-radio custom-control-inline">

        <input id="good" type="radio" class="custom-control-input" [value]="t('options.good')" name="rating" [(ngModel)]="customerFeedback.rating" #rating="ngModel" required>

        <label class="custom-control-label" for="good">{{ t('options.good') }}</label>

      </div>

      <div class="custom-control custom-radio custom-control-inline">

        <input id="bad" type="radio" class="custom-control-input" [value]="t('options.bad')" name="rating" [(ngModel)]="customerFeedback.rating" #rating="ngModel" required>

        <label class="custom-control-label" for="bad">{{ t('options.bad') }}</label>

      </div>

    </div>

    <span class="text-danger" *ngIf="(rating.touched || feedbackForm.submitted)&& rating.errors?.required">{{ t('error') }}</span>

  </div>

  <div class="row form-group">

    <div class="col d-flex justify-content-end" *transloco="let t; read: 'submit'">

      <button type="submit" class="btn btn-success"> {{ t('label') }}</button>

    </div>

  </div>

</form>

We now have a template-driven form with the following fields:

  • Name (an input field used to store the name of the user),
  • Gender (a select field displaying three different options to choose from),
  • Comment (a text area field used to store comments provided by the user),
  • Rating (a radio group field that asks the user to rate the service of Phrase).

All of these fields are mandatory. We used Transloco's pipe to read the key of the title field from the translation file. However, to read the key of the form fields, we used Transloco's structural directive. While running the application, these keys will be replaced by their corresponding values from the selected language JSON file.

Open src\app\feedback\feedback.component.css and insert the following style:

.card {

  margin: 30px;

}

.custom-radio {

  margin: 10px;

}

Adding Missing Translations

To serve the app in German and Spanish, we need to add translations for these languages. We will use Phrase to translate the content from the en.json file into our desired language.

Login to Phrase, and create a new project. Refer to the image shown below to fill the details while creating a new project.

Add project menu in Phrase | Phrase

After creating the project, you will be asked to set up your desired languages. Select English-en as the default locale. Select Spanish-es and German-de as target languages. Click on the "Create languages" button.

Desired language selection | Phrase

After setting the languages, click “Skip setup” to go to the Project Dashboard right away. Navigate to "More" and "Project Settings" from the menu bar on the top. In the dialog box, click on the "Advanced" option and then select "Enable machine translation". Finally, click "Pre-translation" to let Phrase translate your new content automatically. Click "Save".

Autofill menu Phrase | Phrase

Go to the Dashboard of your Phrase project, navigate to the Languages tab, and click "Upload file". Upload the src\assets\i18n\en.json file from your Angular project. Select Simple JSON as your format. Under the "Language" section, select “Use existing language” and then select “en” from the drop-down. Check the “Use pre-translation” option and click on the "Import" button.

Upload menu Phrase | Phrase

On the next page, you'll see a message that your upload was successful. Navigate to the "Languages" tab and you can see that the uploaded file has been translated automatically into your target languages (German and Spanish).

Language tab Phrase | Phrase

You will also get the option to download the translated files. Download and replace the src\assets\i18n\de.json and src\assets\i18n\es.json file with the translated file for these languages.

Set up the app component

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

<app-nav-bar></app-nav-bar>

<div class="container">

  <app-feedback></app-feedback>

</div>

Executing the App in the Local Environment

Run the following command to execute the app in your local environment:

ng serve -o

The app will be launched and you can see the screen as shown below. Select the language value from the drop-down in nav-bar and the site content will be delivered in the selected language.

Finished demo app | Phrase

Create the Production Build

Run the following command to create the production build of the app:

ng build --prod

Upon successful execution, this command will create a dist folder in the application’s root folder. Inside the dist folder, we will have another folder named phrase-transloco-i18n (the same name as the project name). The phrase-transloco-i18n folder will contain the build file to be deployed in the production environment.

Wrapping Up Our Angular Tutorial on Localization with Transloco

In this article, we learned about implementing localization in an Angular app with the help of Transloco. We used Phrase to create the translation files for our app and serve it in three different languages. If you're looking for a reliable partner in localizing your software for global markets, Phrase is the way to go! Sign up for a 14-day trial and see what it can do for you!