In the digital landscape where diverse user bases and global reach are paramount, localizing content for different languages is a critical task. One efficient solution to this task lies within message formatting in JavaScript, particularly in the context of Angular applications. Two libraries, @messageformat
and ngx-translate-messageformat-compiler
, play pivotal roles in enhancing internationalization (i18n) support. By providing localized content that respects the grammatical and structural peculiarities of different languages, these libraries significantly improve the user experience.
Understanding @messageformat and ngx-translate-messageformat-compiler
@messageformat
is a standalone library for handling message formatting in JavaScript. It provides a comprehensive implementation of the MessageFormat
syntax, supporting a wide range of language features and customization options. It can be used independently or in conjunction with ngx-translate-messageformat-compiler
to handle message formatting in Angular applications.
The ngx-translate-messageformat-compiler
library is an extension of the ngx-translate
library, and allows for message formatting using the MessageFormat
syntax. Message formatting is essential for handling language-specific pluralization, gender, and other complex language patterns. It compiles the MessageFormat
strings into JavaScript functions, enabling efficient translation and rendering of dynamic content.
Real-World Applications of @messageformat and ngx-translate-messageformat-compiler
Both libraries are important in Angular applications because they allow for the translation and formatting of user-visible text, accommodating the grammatical and structural nuances of different languages. This improves the user experience by providing localized content that feels natural and appropriate to users in different regions or language preferences.
Tackling Pluralization Differences Across Languages
One of the use cases of this library is tackling pluralization differences between languages. Pluralization rules can vary widely across languages, creating a complex problem when it comes to translating and formatting user-visible text. For instance, in English, it’s fairly straightforward—we typically add an “s” for plurals. But in many other languages, the rules are more complicated, with different forms used depending on the number of items.
For example, consider how the phrase about the number of results found would be presented in English, Russian, and German:
- English:
Found no result. Found one result. Found # results.
- Russian:
Результатов не найдено. Нашел один результат. Найдено 3 результата (In between plural form, and it is not easy to solve). Найдено # результатов.
- German:
Keine Ergebnisse gefunden. Ein Ergebnis gefunden. Es wurden # Ergebnisse gefunden.
In the case of Russian, the in-between form for three results illustrates the complexity of plural forms that cannot be directly translated from English. Similarly, the German phrase changes structure entirely depending on the number of results.
The @messageformat
and ngx-translate-messageformat-compiler
libraries are designed to handle these kinds of linguistic complexities, ensuring that your application communicates effectively and naturally in any language.
Installation
Now that we understand the significance of @messageformat
and ngx-translate-messageformat-compiler
, it’s time to get practical. In this section, we offer a step-by-step guide to set up and install ngx-translate
in your Angular application. Let’s start with the installation process.
First, let’s set up ngx-translate
:
npm install @ngx-translate/core @ngx-translate/http-loader --save Adding library to root module app.module.ts: import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClient, HttpClientModule } from '@angular/common/http'; import { TranslateCompiler, TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'; import { AppComponent } from './app.component'; export function createTranslateLoader(http: HttpClient) { return new TranslateHttpLoader(http, './assets/i18n/', '.json'); } @NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, HttpClientModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: (createTranslateLoader), deps: [HttpClient], }, }) ], bootstrap: [AppComponent], }) export class AppModule { }
Then, set up simple application with translation:
app.component.html: <ul> <li *ngFor="let lang of languages"> <a (click)="changeLangTo(lang)"> {{ lang }} </a> </li> </ul> <section> <p> {{ "GREETING" | translate }} </p> </section> app.component.ts: import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent { languages = ['en', 'ru', 'de']; constructor(private readonly translateService: TranslateService) { translateService.setDefaultLang('en'); translateService.use('en'); } changeLangTo(lang: string): void { this.translateService.use(lang); } } en.json: { "GREETING": "Hello!" } ru.json: { "GREETING": "Привет!" } de.json: { "GREETING": "Hallo!" }
And the result will look like this:
Now, it’s time to add @messageformat
and ngx-translate-messageformat-compiler
:
npm install @messageformat/core ngx-translate-messageformat-compiler --save
After installation, TranslateModule
needs to be configured to use TranslateMessageFormatCompiler
:
import { TranslateCompiler } from '@ngx-translate/core'; import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'; .... @NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, HttpClientModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: (createTranslateLoader), deps: [HttpClient], }, compiler: { provide: TranslateCompiler, useClass: TranslateMessageFormatCompiler, }, }) ], bootstrap: [AppComponent], }) export class AppModule { }
It’s also possible to configure MessageFormat
by providing a configuration object for the MESSAGE_FORMAT_CONFIG
injection token. Here’s the default:
{ biDiSupport: false, formatters: {}, strictNumberSign: false, currency: "USD" }
MessageFormat
instances offer various options to control their behavior. These options include customFormatters
, biDiSupport
, and strict mode, which allow for customization and influence over how MessageFormat
operates. Learn more in the official docs.
Usage
After setting up ngx-translate
, let’s now tackle our initial problem: incorporating these translations into our .json files using the appropriate format. When using MessageFormat
in conjunction with ngx-translate
, it’s important to note some key differences from ngx-translate
’s default syntax:
- Accessing object properties in placeholders is not supported. For instance, syntax like
Hello {name.prop} {name.prop2}
won’t work. - Simple placeholders are enclosed in single curly braces instead of double. So, we need to use
Hello {name}
instead of the defaultHello {{name}}
.
Let’s examine the basic structure of a formatter to better understand these concepts:
{variable, formatter, option {#(variable placeholder) interpolated text}}
Here are the translation strings:
en.json { "RESULT_TEXT": "{count, plural, =0{Found no results} one{Found one result} other{Found # results} }" } de.json { "RESULT_TEXT": "{count, plural, =0{Keine Ergebnisse gefunden} one{Ein Ergebnis gefunden} other{Es wurden # Ergebnisse gefunden} }" } ru.json { "RESULT_TEXT": "{count, plural, =0{Результатов не найдено} one{Нашел один результат} few{Найдено # результата} other{Найдено # результатов} }" }
Let’s modify our code to see the results:
app.component.html: .... <p> {{ "RESULT_TEXT" | translate : { count: resultCount } }} </p> <button (click)="decrement()"> - </button> | <button (click)="increment()"> + </button> .... app.component.ts: .... resultCount = 0; .... increment(): void { this.resultCount++; } decrement(): void { this.resultCount--; } ....
Finally, we get:
Extend Capabilities with Additional Formatters
It doesn’t end here. The versatility of MessageFormat
extends beyond the basic usage, offering an array of ready-to-use formatters to accommodate a variety of scenarios, enhancing its adaptability and functionality. The options include:
- Select
- Plural – what we used here
- Date
- Duration
- Number
- Time
MessageFormat
’s flexibility also allows us to create custom formatters for unique or exceptional cases that may arise. This ability to tailor the library to our specific needs underlines the power and utility of MessageFormat
in the world of internationalization.
Conclusion
In summary, @messageformat
simplifies the task of creating dynamic and localized messages by allowing placeholders and variable substitution. It eliminates the need for manual string concatenation and provides support for language-specific rules and variations, making it easier to build multi-language applications.