How to build painless multi-language apps with Angular and ngx-translate
Tags: angular, ngxtranslate, multilanguage, internationalization
Published: 2020-06-18 12:11:07

I'm sure if you're reading this post is because either you're curious or you understand the pain of supporting multiple languages in Angular.

I love Angular and it's my main JS Modern Framework, but there is something that drives me nuts its poor multi-lingual support. It's extremely over-complicated to my taste.

I have developed multiple websites and over 11 apps for Windows and Android and I cannot understand how the creators couldn't be inspired by Google's most important OS, **Android**.

In Android, you only need a couple of XMLs (en, es, etc.), minor adjustments in the UI XMls, or code something in Java/Kotlin and magically, you have an app that supports multiple languages.

After several attempts, I found **ngx-translate**, which leveraged my work significantly. **So, how to use it?**

	npm install @ngx-translate/core @ngx-translate/http-loader --save

Next, configure your **app.module.ts**

####1. Enable the **translation service**:

```javascript
import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient, HttpClientModule } from '@angular/common/http';
```

####2. Configure the **loader**:

a. **Root domain** (www.mydomain.com):

```javascript
export function translateHttpLoaderFactory(http: HttpClient) {
  return new TranslateHttpLoader(http);
}
```

b. **Sub-domains** (myself.github.io/myapp):

```javascript
export function translateHttpLoaderFactory(http: HttpClient) {
  return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
```

In this option, you are going to configure the location of your JSON files.

####3. Configure your **constructor** (my version auto-detects the browser language and set it by default):

```javascript
availableLng = ['en', 'es'];

//start the translation service
constructor(private translateService: TranslateService) {
	//defines the default language
	let tmpLng = 'en';

	//gets the default browser language
	const currentLng = window.navigator.language.substring(0,2);

	if (this.availableLng.includes(currentLng))
	   tmpLng = currentLng;

	translateService.setDefaultLang(tmpLng);
}
```

####4. Add to your imports:

```javascript
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: translateHttpLoaderFactory,
        deps: [HttpClient]
      }
    })
```

Your final **app.module.ts** might look like this one:

```javascript
import { HttpClientModule, HttpClient } from '@angular/common/http';
//configure translation service
import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';

/* IMPORTANT:
   This only works if you are setting your app in your main domain: www.mydomain.com
*/
export function translateHttpLoaderFactory(http: HttpClient) {
  return new TranslateHttpLoader(http);
}

/*
   For sub-domains like myself.github.io/myapp
   You need to use this code or a variation with the location of your assets:
*/

/*
export function translateHttpLoaderFactory(http: HttpClient) {
  return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
*/

@NgModule({
  declarations: [
	AppComponent
  ],
  imports: [
	BrowserModule,
	HttpClientModule,
	TranslateModule.forRoot({
	  loader: {
		provide: TranslateLoader,
		useFactory: translateHttpLoaderFactory,
		deps: [HttpClient]
	  }
	})
  ],
  providers: [],
  bootstrap: [AppComponent]
})

export class AppModule {
  //define available languages
  availableLng = ['en', 'es'];

  //start the translation service
  constructor(private translateService: TranslateService) {
	//defines the default language
	let tmpLng = 'en';

	//gets the default browser language
	const currentLng = window.navigator.language.substring(0,2);

	if (this.availableLng.includes(currentLng))
           tmpLng = currentLng;

	translateService.setDefaultLang(tmpLng);
  }
}
```

After, **create** a new folder in **assets** called **i18n**. Inside of it, you are going to create the language assets like **en.json**:

```javascript
{
  "Title": "Translation demo",
  "WelcomeMessage": "Welcome to the international demo application"
}
```

And **es.json**:

```javascript
{
  "Title": "Demo de traducción",
  "WelcomeMessage": "Bienvenido a la aplicación de demostración internacional"
}
```

Now, inside the **HTML** part of your components, you can call it like this:

```html
<h1 translate>Title</h1>
```

Where **translate** indicates the tag that is going to be translated and **Title** the JSON key.

## Can this be extended beyond HTML tags?

Definitely, what if you have a **placeholder** in an **input**?

You can use it like this:

```html
<input placeholder="{{'Title' | translate}}" />
```

Or how do you use it from the **TypeScript** file?

First, enable it from the **component constructor**:

```javascript
constructor(private translateService: TranslateService) { }
```

Now, you can access it with a simple piece of code like this one (synchronous, but might return the same _key_ if it hasn't loaded yet):

```javascript
console.log(this.translateService.instant('WelcomeMessage'));
```

The best option would be to use the _async_ option:

```javascript
this.translateService.get('WelcomeMessage').subscribe((data: any) => { console.log(data); });
```

The greatest benefit in my experience with **ngx-translate** is that it works and looks similar to common Apps development in Android or Windows.

This post has some inspiration from Yaser's blog:

https://yashints.dev/blog/2018/01/17/multi-language-angular-applications

Plus, if you want to see a full action app using this framework, you can check it here:

https://fanmixco.github.io/gravitynow-angular

Also, here is its repository:

https://github.com/FANMixco/gravitynow-angular

### Follow me on:

|  Personal  |  LinkedIn  |YouTube|Instagram|Cyber Prophets|Sharing Your Stories|
|:----------|:----------|:------------:|:------------:|:------------:|:------------:|
|[![Personal](https://raw.githubusercontent.com/FANMixco/federiconavarrete/master/img/favicons/favicon.png)](https://federiconavarrete.com)|[![LinkedIn](https://i.stack.imgur.com/idQWu.png)](https://www.linkedin.com/in/federiconavarrete)|[![YouTube](https://i.stack.imgur.com/CFPMR.png)](https://youtube.com/c/FedericoNavarrete)|[![Instagram](https://i.stack.imgur.com/PIfqY.png)](https://www.instagram.com/federico_the_consultant)|[![RedCircle Podcast](https://i.stack.imgur.com/4XICF.png)](https://redcircle.com/shows/cyber-prophets)|[![RedCircle Podcast](https://i.stack.imgur.com/4XICF.png)](https://redcircle.com/shows/sharing-your-stories)|

[![sponsor me](https://raw.githubusercontent.com/FANMixco/Xamarin-SearchBar/master/bmc-rezr5vpd.gif)](https://www.buymeacoffee.com/fanmixco)