Angular Translation transloco

Angular localization with Transloco

These days it is evident that translation and internationalization tools are becoming more and more popular among software developers. That’s why new libraries pop up providing better support for multi-lingual applications. Angular has a handful of solutions providing internationalization support including built-in i18n module, ngx-translate, and I18next. Transloco is a fresh newcomer with a vast array of exciting features and promising capabilities. In this article we are going to see how to implement Angular localization using Transloco in practice.

You may be also interested in learning how to localize Angular applications with the built-in I18n module or how to add internationalization support into Ionic apps.

Meet Transloco!

Core Features

Transloco is an outstanding library for creating multi-language applications. It has lots of features, a thriving community, and great documentation. The highly modular approach allows you to adjust it for different applications.

Transloco has the following features:

  • Runtime language change. This allows you to switch languages on the run, without reloading the whole application.
  • Schematics. This allows you to use Angular CLI to perform routine tasks like the creation of scaffold translation modules, configuring the library, and creating language resources.
  • Custom “loading” template. You can display a custom template while your translations are loading.
  • Using different languages simultaneously. You may force the different parts of your application to use a specific language without affecting the other parts.
  • Translation lazy loading. You may separate your translation files and load each file separately. Сonsequently, you can improve the loading times of your application.
  • Multiple fallbacks. You can define the fallback chain. If Transloco doesn’t find the requested translation in the current language, it tries to use the next set language. If the next option isn’t working either, the third one is used, and so on.
  • Testing capabilities. The library provides features for testing multi-language support.
  • Set of instruments for getting translations. This includes translation pipe, translation structural directive, attribute directive.
  • Official plugin for pluralization.
  • Official plugin for locale support. Dates, currencies, decimal formatting, according to the current locale.

Structure Overview

Speaking of the principal architecture, we can distinguish a few major parts of the Transloco library:

  • The core module. This is where all parts are integrated.
  • Translation loaders. You are loading translation files by using them (for example, through HTTP).
  • The transpiler. This module resolves the code inside the translation strings.

Actually, there are many more components in Transloco, but to get a jump-start you only need to:

  • Create assets with translations. You can achieve this with schematics by executing a proper command.
  • Import a module and configure it with a default loader. This provides available directives and pipes.
  • If you need to use Transloco within components, you have to inject the Transloco provider.

Laying Foundations

Installing and Configuring the Library

Start implementing Angular localization with Transloco by running the following command:

ng add @ngneat/transloco

This command asks you a few questions about the language you are going to deploy and server-side template rendering. As a result, you will get the assets/i18n folder with the JSON files containing translations for the chosen languages. Also, a transloco-root.module.ts file will be created to configure Transloco.

There is very detailed guide on configuration options, which can be found in the official documentation. In this tutorial, I will explain the most important options in detail. One of the first things you should set up in any multi-language support library is the default language and the list of the application’s languages:

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', 'ru'],
fallbackLang: 'en',
defaultLang: 'en',
// Remove this option if your application doesn't support changing language in runtime.
reRenderOnLangChange: true,
prodMode: environment.production,
flatten: {
aot: environment.production
}
})
},
{ provide: TRANSLOCO_LOADER, useClass: TranslocoHttpLoader }
]
})
export class TranslocoRootModule {}
  • defaultLang: ‘en’ sets the default language of your application.
  • availableLangs provides the array of supported languages.
  • There is another fairly similar option, which is: fallbackLang. What’s the difference? If you didn’t set the language variable, the defaultLang will be used. The fallback language, in turn, dictates what language to use if you don’t have any particular translation label.
  • The next option configured by default is  reRenderOnLangChange which is set to true. As the name implies, it dictates whether the page should be re-rendered once the language is changed.
  • prodMod enables disabled production mode.

Setting up Lokalise

Managing translations for multiple languages is a pretty challenging task, especially for a complex application. Things become even more complex if you have limited proficiency in the languages your application does support. What should you do in such cases? Use a proper translation management system, of course!

Allow me to introduce you Lokalise, the state-of-the-art translation management platform which allows you to comfortably manage translation files, collaborate with translators, order professional translation services, use integrations with various services, and much more. In this article I will briefly explain how to get started with Lokalise. For now, you will need to perform the following steps:

  • Grab your free trial (no credit card is required).
  • Proceed to Personal profile > API tokens.
  • Generate a new read-write token (make sure to keep it safe).
  • Download CLIv2 and unpack it somewhere on your PC.
  • cd into the created directory
  • Create a new Lokalise project by running: lokalise2 project create --name TransLoco --token YOUR_TOKEN_HERE --languages "[{\"lang_iso\":\"ru\"},{\"lang_iso\":\"en\"}]" --base-lang-iso "en". Adjust the supported languages according to your setup.
  • The above command is going to return an object with the project details. Copy the project_id as we will need it later.

The basic setup is done, and we can move on to the next part!

Configuring Transloco for Using in Lazy Loading Modules

Modern Angular applications contain many parts with different degrees of coupling: pipes, providers, modules, and directives. So how do we make Transloco available to the lazy loaded modules of your application?

Let’s create a separate module home.module.ts and configure it. Start by running the following command:

ng add component home

Next, let’s try to use the following translation directive:

<h1> {{ 'title' | transloco }} here</h1>

Unfortunately, we’ll get the below error:

core.js:6185 ERROR Error: Uncaught (in promise): Error: The pipe 'transloco' could not be found!
Error: The pipe 'transloco' could not be found!

To use Transloco, we need to import the root module into child modules:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HomeComponent } from './home.component';
import { RouterModule } from '@angular/router';
import { TranslocoRootModule } from '../transloco-root.module';
import { TranslocoLocaleModule } from '@ngneat/transloco-locale';
@NgModule({
declarations: [
HomeComponent
],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: HomeComponent }]),
TranslocoRootModule
],
exports: [HomeComponent]
})
export class HomeModule { }
view raw home.module.ts hosted with ❤ by GitHub

Performing Translations in Views and Components

Transloco offers you several ways of translating the text in your application. Essentially, most systems use the key-values principle. This means that you mark some parts of the code with a label and retrieve the desired text for the currently set language. Obviously, performing translation within the views is the most common task, but sometimes you may need to translate something in the component code. For example, your alert library may require setting the button text within the component and may not support templating within Angular views.

So, let’s review all the available options one by one.

Structural Directives

Structural directives in Angular are in charge of HTML layouts. Typically, they manipulate HTML elements: delete, remove, or hide them, or build descendants of the host element. Notable examples are: ngIf, ngFor, and ngSwitch. Structural directives start with an asterisk, so they are easy to recognize. The Transloco directive for translation is *transloco:

{
"title": "English title",
"hello": "Welcome",
"containers": "ng-containers are useful when you don't want to have too many html layers",
"dashboard": {
"description": "You may use scopes for structuring translations"
}
}
view raw en.json hosted with ❤ by GitHub
<ng-container *transloco='let t'>
{{ t('containers') }}
</ng-container>
<br>
<ng-container *transloco='let t; lang: "ru"'>
{{ t('containers') }}
</ng-container>
view raw home.component.html hosted with ❤ by GitHub
{
"title": "Русский заголовок",
"hello": "Добро пожаловать!",
"containers": "ng-containers полезны когда вам не хочется иметь слишком много уровней HTML",
"dashboard": {
"description": "Вы можете использовать scopes для структурирования перевода"
}
}
view raw ru.json hosted with ❤ by GitHub

So, in order to use Transloco, you define a function with the desired name, and you use this function to fetch translations based on the provided key and options. Moreover, you may set the language using the directive.

In many cases, it’s a good idea to use ng-container, which is a very useful tag element (it is deleted from HTML during the rendering phase). Therefore, you can use structural directives without affecting your HTML markup.

You may pass parameters to translation strings as a second option, like so:

"greetings": "Hello {{name}}"

t("greetings", {name: "world"})

Speaking of values, you may reference variables by their names from the same file, as follows:

{ "key1": "We can reference {{key2}}", "key2": "This text will appear in key 1" }

Translation Pipes

Pipes are instruments that take input and transform it in a specific way. Notable examples are: {{ birthday | date:"MM/dd/yy" }} and {{ object | json }}. You may chain pipes, for example: {{ variableOrFunction | pipe1 | pipe2 }}.

Transloco also supports pipes, see below:

<p> {{ ‘title’ | transloco }}</p>

You may pass parameters to translation strings, like so:

{{ "greetings" | transloco: { name: "world" } }}

You can also use pipes within attributes, as follows:

<input [placeholder]='placeholderLabel | transloco'></input>

Attribute Directives

Attribute directives usually change the appearance and behavior of certain application parts, for example:

<h4 transloco='title'></h4>

<h4 transloco='title' translocoLang='ru'></h4>

Uploading Translation Files to Lokalise

After we have created JSON files and started translating our app, we can upload language resource files to the Lokalise project we created earlier. To do this, run the following commands:

lokalise2 file upload --lang-iso en --file "PATH_TO_PROJECT\assets\i18n\en.json" --project-id PROJECT_ID --token YOUR_TOKEN
lokalise2 file upload --lang-iso ru --file "PATH_TO_PROJECT\assets\i18n\ru.json" --project-id PROJECT_ID --token YOUR_TOKEN

PROJECT_ID is the project identification string that you received in the previous step.

Translations in a Component

We’ve already mentioned a scenario in which you may need translation capabilities in the other parts of your application (not in the templates). Transloco is here to help you.

First, you will need to inject Transloco provider into a module, see below:

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

and add a constructor, as follows:

constructor(private translocoService: TranslocoService){}

After you inject the provider you may use it to get a translation, set the active language, and do many other things:

<h5>{{ componentVariable }}</h5>
<h5>{{ componentVariableSecond }}</h5>
<label for="lang">Choose a language:</label>
<select (change)="onChange($event.target.value)" id="lang">
<option *ngFor='let l of languages' [value]="l.code">{{l.label | transloco }}</option>
</select>
view raw home.component.html hosted with ❤ by GitHub
import { Component, OnInit } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
lang: string = 'en';
componentVariable: string;
componentVariableSecond: string;
languages: Object[] = [
{
label: "English",
code: 'en'
},
{
label: 'Russian',
code: 'ru'
}];
ngOnInit(): void {
this.translocoService.setActiveLang(this.lang)
this.componentVariable = this.translocoService.translate('hello');
this.componentVariableSecond = this.translocoService.translate('dashboard.description')
}
}
view raw home.component.ts hosted with ❤ by GitHub

Next let’s speak a little bit about what you can do with a Transloco provider within a component. The first and the most important function is translate. This function returns a value, so it’s your duty to ensure that resource files are loaded properly. If you want to work with the asynchronous observables, you may use the function: selectTranslate(). Similarly, you can pass the parameter hash as a second parameter in the same manner. You may pass a language or scope of translation as a third parameter:

this.translocoService.translate('greetings', {name: 'world'}, 'ru');

Available Functions

You also have the following functions:

  • getTranslationObject() returns the whole value of any key within your translation files. Basically, you are getting a subset of your translation files based on the path provided. If your translation strings require parameters, you may pass them as a second argument, as follows:
    getTranslationObject("title.dashboard", { "description": { "embededVar": "Some text"}, embededVar2: "Some other text"})
  • selectTranslationObject() does the same but loads a translation file first.
  • getTranslation() returns all translations. If you pass a language code to it, it will return the translation in the specified language.
  • selectTranslation() does the same but loads a language file first.
  • setTranslation() allows you to carry out the opposite operation to the commands above. It sets the translation object for a given language. If you want to merge this object with the translation you currently have, set the third option to true: {merge: true}.
  • Finally, there is an $events object that you can use to subscribe to translation events.

Speaking of the currently set language, you may set it with the setActiveLang() function. You may set the current language with select tag like this:

onChange(target) {
this.translocoService.setActiveLang(target);
}
view raw home.component.ts hosted with ❤ by GitHub

Get Browser Language

You can set the current language based on browser settings. To achieve this, you need to import the getBrowserLang module from Transloco:

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

Then simply enter:

this.lang = getBrowserLang();

Localization

Cultural differences may be confusing sometimes. For instance, 12/03/1965 and 03/12/1965 could be the same date. You probably know that in different countries the order of the day, month, and year is different. In some countries AM/PM abbreviations are used, whereas in others they utilize the 24-hour format. So, if you don’t have a translation library, it’s quite challenging to follow the necessary rules.

Let’s start by installing the Transloco locale module below:

npm i @ngneat/transloco-locale

Then importing this locale module into app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { TranslocoRootModule } from './transloco-root.module';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TranslocoLocaleModule } from '@ngneat/transloco-locale';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
TranslocoRootModule,
BrowserAnimationsModule,
TranslocoLocaleModule.init({
langToLocaleMapping: {
en: 'en-US',
ru: 'ru-RU'
}})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
view raw app.module.ts hosted with ❤ by GitHub

As you can see, we match the language code to the language cultural codes with the help of langToLocaleMapping. Cultural codes assume not only a language but a certain representation of different data: dates, currencies, numbers, etc. Now, once we’ve the matched languages, we can use the transloco-locale module to display this data correctly.

L10n Usage

Let’s start with the date pipe. Date pipe allows us to automatically format dates. We can initialize a variable in the component home.component.ts:

date: number = Date.now();

and use it in the following template:

{{ date | translocoDate }}

You may also use parameters to customize the date representation, like so:

{{ date | translocoDate: { dateStyle: 'medium', timeStyle: 'medium' }}

Pluralization Setup

We’ve already spoken about a part of multi-language libraries that resolves code within your translations. Generally, a transpiler allows you to use variables. What if you want to have pluralization support? In this case, we can use the official plugin for message formatting that supports the International Components for Unicode (ICU) pluralization format. Right now, ICU-compatible libraries are the de facto standard for pluralization.

npm i messageformat @ngneat/transloco-messageformat

After performing installation, you will need to import the messageformat into your root module:

import { TranslocoMessageFormatModule } from '@ngneat/transloco-messageformat';
@NgModule({
imports: [
TranslocoMessageFormatModule.init()
]
})
view raw app.module.ts hosted with ❤ by GitHub

So, how does this format work and what are the most popular use cases? You write and ICU expression in curly brackets {} within your translation strings.

The message starts with the variable name we pass to evaluate within the expression. After the comma, the second parameter is a keyword defining what mode we are going to use. There are two keywords: plural and select. Plural means that we pass a countable parameter and the expression will return a value based on the given number.

The third parameter is an expression with options. If plural is passed as a second parameter, the third parameter should resemble the following:

=0{String or expressing if the first param = 0} 1{String or expression if the first param = 1} ... someNumber{String or expression if the first param = someNumber}... other{String or expression if the first param is equal to something not specified here. Default value}

Here is a simple example. Suppose we would like to show how many apples Janny has. There are three possible cases:

  • First: Janny has no apples;
  • Second: Janny has one apple;
  • Third: Janny has X apples (more than one apple, that is).

Now represent this logic in your resource file, as follows:

"janny": "Janny {applesCount, plural, =0{has no apples} 1{has an apple} other{has {applesCount} apples}}"]

Downloading Translation Files from Lokalise

Once you have edited your translations in Lokalise, download them back to your project by running:

lokalise2 file download --dest PROJECT_PATH\src\assets\i18n --format json --token YOUR_TOKEN --project-id PROJECT_ID

Transloco Testing

Unlike many libraries, Transloco actually has out-of-the-box testing support. To be able to test the application, you have to add these two lines in tsconfig.json (the compilerOptions section):

"resolveJsonModule": true,
"esModuleInterop": true

This allows us to directly import JSON from the code.

Next, create the transloco-testing.module.ts file to keep your Transloco settings in one place. We import TranslocoConfig and TranslocoTestingModule in order to create a translation configuration. We import the configuration function into the specs, where you want to check translations. Сonsequently, you can write specifications for text in different languages:

import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import { getTranslocoModule } from './transloco-testing.module';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
getTranslocoModule(),
RouterTestingModule,
],
declarations: [
AppComponent
],
providers: []
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
console.log(compiled);
expect(compiled.querySelector('h1').textContent).toContain('Русский заголовок');
});
});
import {
TranslocoConfig,
TranslocoTestingModule
} from '@ngneat/transloco';
import en from '../assets/i18n/en.json';
import ru from '../assets/i18n/ru.json';
export function getTranslocoModule(config: Partial<TranslocoConfig> = {}) {
return TranslocoTestingModule.withLangs({ en: en, ru: ru }, { availableLangs: ['en', 'ru'], defaultLang: 'ru' });
}

Loading Templates

With Transloco, you can define a placeholder for translation blocks in the process of loading. You may use it in lazy loading modules, in root components, and templates:

@Component({
selector: 'app-comp',
templateUrl: './app-comp.component.html',
providers: [
{
provide: TRANSLOCO_LOADING_TEMPLATE,
useValue: '<p>Translation is loading...</p>'
}
]
})
export class AppComp {}
<ng-container *transloco="let t; loadingTpl: loading">
<h1>{{ t('title') }}</h1>
</ng-container>
<ng-template #loading>
<h1>Loading...</h1>
</ng-template>
view raw some-template.html hosted with ❤ by GitHub

Conclusions

Transloco is a great translation library for Angular. I would say it is one of the best I have ever seen. In short, it has tons of functionalities and you can get even more if you use official plugins. Moreover, there is very good documentation available and there is the chat where you can ask any questions you may have.

In this article, we’ve reviewed how you can install and configure Transloco, transloco-locale, and transloco-messageformat libraries. We spoke a little bit about how to use pipes, directives, and providers to get your text translated, as well as how to support different locales and handle plurals in translations.

Related posts

Sign up to our newsletter

Get the latest articles on all things localization and translation management delivered straight to your inbox.

Read also
Localization made easy. Why wait?
The preferred localization tool of 1500+ leading global companies