---
title: "Angular Localization with ngx-translate: Complete Tutorial"
description: "Master Angular i18n with ngx-translate. Complete tutorial covering setup, translation files, language switching, and best practices. Includes working examples."
canonical: "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate"
date: "2025-08-01T00:00:00.000Z"
modified: "2025-08-01T00:00:00.000Z"
source: "html"
---

# Angular Localization with ngx-translate: Complete Tutorial

Andreas Löw

[babeledit](https://www.codeandweb.com/babeledit/tutorials.md),  [tutorial](https://www.codeandweb.com/tags/tutorial),  [ngx-translate](https://www.codeandweb.com/tutorials/ngx-translate.md),  [angular](https://www.codeandweb.com/tutorials/angular.md)

[GitHub](https://github.com/CodeAndWeb/ngx-translate-demo)

![Angular Localization with ngx-translate: Complete Tutorial](https://www.codeandweb.com/static/3d259f82fbc214c0061ca60d12df938b/a6312/how-to-translate-your-angular-app-with-ngx-translate.png)

## Who is this tutorial for?

This tutorial is for **Angular developers** who want to translate their applications into multiple languages. It covers **Angular 17 - 20** together with the corresponding **ngx-translate** versions, including the latest **ngx-translate 17**.

**It is the 'official' ngx-translate tutorial from the current developers of ngx-translate!**

If you are still using an **older Angular** version, please refer to the following tutorials:

*   [How to translate your Angular app with ngx-translate 16](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate-16)
*   [How to translate your Angular 8 - 16 app with ngx-translate 15](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular8-16-app-with-ngx-translate)
*   [How to translate your Angular 7 app with ngx-translate](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular7-app-with-ngx-translate)
*   [How to translate your Angular 6 app with ngx-translate](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular6-app-with-ngx-translate)

If you are not **sure which translation method to use** with your Angular application, check out our other tutorials, e.g., [How to translate Angular apps: @angular/localize and xlf](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-xlf-files.md).

## Optional: Create an Angular Project

For this tutorial, you'll start with a simple demo application. I assume that you already have basic knowledge of Angular. **You can, of course, skip this step and use your existing project.**

Create an empty new project:

```bash
npx -p @angular/cli ng new ngx-translate-demo-standalone \
    --style=scss \
    --routing=false \
    --ssr=false \
    --zoneless=false
```

This creates a new Angular project in a directory called `translation-demo`. The additional options have the following meaning:

*   `--style=scss`: We use SCSS for styling the app. This is an extension to plain CSS files.
*   `--routing=false`: We don't use a router for this simple application
*   `--ssr=false`: We don't use server side rendering
*   `--zoneless=false`: We don't want to use the zone-less preview

I do not install the `ng` command globally because I have several projects with different Angular versions and other frameworks on my computer. Adding everything globally can be cumbersome with all the dependencies and different node versions.

The only drawback is that you have to call `npm run-script ng` instead of just `ng`, but I think that this is acceptable. On a Mac or Linux system, you can set an alias to call `ng` directly while inside your project folder:

```
alias ng="npm run-script ng"
```

Finally, start the new project by running:

```bash
cd ngx-translate-demo-standalone
ng serve
```

Open your browser and visit the following URL: http://localhost:4200. You should see something similar to this:

  ![Your first Angular application](https://www.codeandweb.com/static/c9cbd3cb74fc016864a6eddb9adadcc7/283d3/your-first-angular-app%402x.png)

Default Angular Application at the first start.

Here's a step-by-step guide on how to use NGX-Translate with Angular:

## Step 1: Add ngx-translate to your Angular Application

Enter the following command in the terminal:

```bash
npm install @ngx-translate/core @ngx-translate/http-loader
```

*   **@ngx-translate/core** contains the core services and components: `TranslateService`, `TranslatePipe` and more.
*   **@ngx-translate/http-loader** loads the translation files dynamically from your webserver.

## Step 2: Set Up the TranslateService

Let's start by initializing ngx-translate in **src/app/app.config.ts**:

src/app/app.config.ts

```typescript
import {
    ApplicationConfig,
    provideBrowserGlobalErrorListeners,
    provideZoneChangeDetection,
    inject
} from '@angular/core';
import {provideHttpClient} from "@angular/common/http";
import {provideTranslateService, TranslateService} from "@ngx-translate/core";
import {provideTranslateHttpLoader} from "@ngx-translate/http-loader";

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideHttpClient(),
    provideTranslateService({
      lang: 'en',
      fallbackLang: 'en',
      loader: provideTranslateHttpLoader({
        prefix: '/i18n/',
        suffix: '.json'
      })
    }),
  ]
};
```

*   `provideHttpClient()` is required by the `TranslateHttpLoader` to load translation files from the server.
*   `provideTranslateService()` configures ngx-translate:
    *   `lang`: The language to use
    *   `fallbackLang`: The language to use in case a translation was not found in the current lang
    *   `loader`: The `TranslateHttpLoader`
        *   `prefix`: The folder on the server, from which translation files are loaded
        *   `suffix`: The suffix of the translation file.

This loads files with the filename schema `/i18n/<language>.json` - e.g. `/i18n/en.json` from the server.

Replace the content of **src/app/app.scss** with this:

src/app/app.scss

```css
div
{
  font-family: Arial, Helvetica, sans-serif;
  margin: 0;
  padding: 1rem 2rem;
}

p, .translated
{
  background-color: #d4f3ff;
  padding:0.5rem 1rem;
}

button {
  background-color: #008CBA;
  border: none;
  color: white;
  padding: 0.25rem 0.5rem;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  margin-left: 1rem;
  border-radius: 0.5rem;
  transition: background-color 0.3s ease;

  &:hover {
    background-color: #005f73;
  }
}

.title {
  background-color: #bde8f8;
  padding: 0.5rem 2rem 1.5rem 2rem;
  position: sticky;
  top: 0;
  z-index: 1000;
}
```

## Step 3: Use translations in your templates

To use ngx-translate in a component, you have to import the `TranslatePipe` or `TranslateDirective`. It's sufficient to work with one of them, but for this demo, import both to experiment with both methods.

src/app/app.config.ts

```typescript
import { Component, signal } from '@angular/core';
import {TranslatePipe, TranslateDirective} from '@ngx-translate/core';

@Component({
  selector: 'app-root',
  imports: [
    TranslatePipe,
    TranslateDirective
  ],
  templateUrl: './app.html',
  styleUrl: './app.scss'
})
export class App {
  protected readonly title = signal('ngx-translate-demo-standalone');
}
```

Now edit **src/app/app.html** and replace the static texts with references to the messages in the **en.json** file.

You have three choices when it comes to adding translations. All options should lead to the same result, but that's not always the case in practice:

*   `{{'id' | translate}}` — translation pipe recommended
    *   works in all use-cases
    *   acceptable readability
    *   also supports other pipes — e.g. making the translation string uppercase,...
*   `<element [translate]="'id'"></element>` — translation directive, id as attribute value deprecated
    *   does (currently) not work with the more complex translation texts — see our section on [Pluralization](#pluralization)
*   `<element translate>id</element>` — translation directive, id as a child deprecated
    *   shows errors/warnings in some IDEs (e.g. PhpStorm: `translate requires a value` )
    *   quite unreadable syntax when adding parameters

So: **If possible, go with variant 1**.

Replace the content of **src/app/app.html** with this:

src/app/app.html

```html
<div class="title">
    <h1>{{ 'demo.title' | translate }}</h1>
</div>
<div>
    <h2>Simple translations without parameters</h2>

    <p>{{ 'demo.simple.text-with-pipe' | translate }}</p>

    <p [translate]="'demo.simple.text-as-attribute'"></p>

    <p translate>demo.simple.text-as-content</p>
</div>
```

This file now contains four translation IDs: `demo.title`, `demo.simple.text-with-pipe`, `demo.simple.text-with-attribute` and `demo.simple.text-with-content`. The dots separate the parts of an ID. This creates a tree structure which gives you a great way to organize your IDs - e.g. by the screen or component they are used in.

*   demo
    *   title
    *   simple
        *   text-as-attribute
        *   text-as-content
        *   text-with-pipe

Let's now translate the files. I'll show you how to use translations with parameters and inside your code later.

## Step 4: Create your JSON translation files

### Start with empty files

Each language is stored in a separate file. Let's create the translation files for English and German:

*   **public/i18n/de.json**
*   **public/i18n/en.json**

You can leave the files empty or use the following content to make them valid JSON files:

public/i18n/de.json,en.json

```html
{}
```

Usually, you would have to add the IDs and create the structure from above, but there are simpler ways to do that.

### Get BabelEdit

It's possible to edit these files manually, but using a specialized editor for the job will save you a lot of hassle later. For this, download BabelEdit.

[Download BabelEdit 5.7.0](https://www.codeandweb.com/babeledit/download/5.7.0/win)

for Windows

[Also available for macOS and Linux](https://www.codeandweb.com/babeledit/download.md)

BabelEdit is available for Windows, macOS, and Linux. It's a translation editor running locally on your computer. Yes, it costs some money, but our licenses are perpetual and don't require a subscription. Compared to other services, it's a really low-cost solution with the benefit of support included. For this tutorial, you can use the free trial.

**Since we are the maintainers and developers of ngx-translate, you can be sure that BabelEdit and ngx-translate always work together.**

After the installation, drag & drop the **i18n** folder onto BabelEdit:

  ![Drag and drop the i18n folder onto BabelEdit](https://www.codeandweb.com/static/8dbfc0f794d5d17e84f3e874d22ef298/283d3/drag-and-drop-i18n-folder-onto-babeledit%402x.png)

Drag and drop the i18n folder onto BabelEdit

It automatically detects ngx-translate and configures the project for you. It also detects the two language files and configures them with the languages.

  ![Configure languages for your ngx-translate project](https://www.codeandweb.com/static/7b2c210ed27b93083751616ccf5b71ee/283d3/configure-languages-for-your-ngx-translate-project%402x.png)

Configure the languages for your ngx-translate project

Feel free to use `en-GB` instead of `en-US` in your project. Click **Ok** to accept the setup.

Next, click **Add ID** in the toolbar or press `⌘`+`n` (macOS) or `CTRL`+`n` (Windows).

You can now enter the translation ID manually, e.g., `demo.title`, but a much more convenient way is to use the extraction tool in BabelEdit. Click the **Extract from source code...** button in the lower left edge of the dialog:

  ![Extract translation IDs from the source code](https://www.codeandweb.com/static/753c9211f87f11e52e0639f188e241b4/283d3/extract-from-source%402x.png)

Extract translation IDs from the source

Next, you should see this dialog:

  ![Adding extracted IDs to your translation files.](https://www.codeandweb.com/static/cbcc5e463b16e5cf52a23db525b18ce2/283d3/add-extracted-ids%402x.png)

Adding extracted IDs to your translation files.

The left side of this dialog shows you the translation IDs BabelEdit found in your files. On the right side at the top, you see the files and lines in which the ID was spotted. Click on one to see the code block in the lower right panel.

Use the buttons at the bottom to add the currently selected ID or all IDs. You can also choose to ignore some or all of the IDs in case BabelEdit displays IDs that are not meant for translation.

If nothing shows up at all, a possible reason is that your source root is not set. You can do this in the toolbar using the **Configuration** button. Point the **source root** to the root folder of your Angular application.

In this project, all IDs should be fine, so click on **Add all**. This creates entries in all three translation files at once, ensuring your translation files never get out of sync. You also see that it creates a tree structure. With this, you can easily group your translations in different contexts, e.g., `main.title`, `settings.account.user_name`, and so on.

Let's now enter the text in English for the translation IDs:

| ID  | text |
| --- | --- |
| `demo.simple.text-as-attribute` | A simple text passing the ID as attribute. |
| `demo.simple.text-as-content` | A simple test using the ID as content. |
| `demo.simple.text-with-pipe` | A simple text using the pipe. |
| `demo.title` | Translation Demo |

## Step 5: Translate Your App to Other Languages

### Translating single messages

BabelEdit requires what we call the primary language, which is the language used as a base for the translations. Click on **Configure...** in the yellow dialog box at the bottom of the center area.

  ![Select your primary language for translations](https://www.codeandweb.com/static/a81c831125aa0814d14a1b1b16563a6a/283d3/select-primary-language%402x.png)

Select en-US as primary language

After the configuration, another box opens: **Display machine translations?** - select **Enable**. This enables Google Translate, DeepL, and Bing translation services inside BabelEdit that will speed up your development process.

In the field for `en-US` enter:

*   **en-US**: `App Translation Demo`

Switch to the `de-DE` field. Click on the German translation in the lower panel or press `⌘`+`1` on a Mac or `CTRL`+`1` on Windows to use the translation Google provided. You can also switch to other services such as OpenAI, DeepL, or Microsoft depending on your preferences.

  ![Using machine translation in ngx-translate](https://www.codeandweb.com/static/2ac23b4c8390a35fe7e305c8c34079d0/283d3/ngx-translate-with-google%402x.png)

Press CTRL/CMD+1 to accept the machine translation

### Translating many messages at once

Repeating this process for all languages takes quite some time, so let's speed it up. For this, use BabelEdit's **Pre-Translate** dialog to translate to all languages automatically. It's available from the toolbar. You can also use it if you've already entered many translations in your primary language and want to add translations to other languages.

  ![Translate to multiple languages with 2 clicks](https://www.codeandweb.com/static/63d8b61d70394d0d38a9b6374c706b3b/283d3/translate-for-multiple-languages%402x.png)

Pre-Translate: Translate to multiple languages with 2 clicks...

### Saving the translated files and the BabelEdit project

Finally, press **Save**. This saves the project configuration in a .babel project file and also updates the JSON files in your project.

The .babel file allows you to continue working on a project later without reconfiguring everything.

When reloading the app, you should now see the messages in English, but you can't switch to German yet.

## Step 6: Switching Languages at Runtime

### Adding a Language Selector

Let's now update the UI to switch between the language files you've just created.

Reloading the app will not show any differences because you've set the default language to `en` in **app.ts**.

Make the following changes to **app.html** to add a simple language switcher:

src/app/app.html

```html
<div class="title">
    <h1>{{ 'demo.title' | translate }}</h1>
    <span>Switch languages at runtime: </span>
    <button (click)="useLanguage('en')">en</button>
    <button (click)="useLanguage('de')">de</button>
</div>
```

Add the following method to **app.ts**:

src/app/app.ts

```typescript
import { Component, signal, inject } from '@angular/core';
import {TranslatePipe, TranslateDirective, TranslateService} from '@ngx-translate/core';

@Component({
  selector: 'app-root',
  imports: [
    TranslatePipe,
    TranslateDirective
  ],
  templateUrl: './app.html',
  styleUrl: './app.scss'
})
export class App {
  protected readonly title = signal('ngx-translate-demo-standalone');

   private translate = inject(TranslateService);

  useLanguage(language: string): void {
      this.translate.use(language);
  }
}
```

### Getting the Browser Default Language

The `TranslateService` contains two methods to receive the language set in the user's browser:

*   [`translate.getBrowserLang()`](https://ngx-translate.org/reference/translate-service-api/#getbrowserlang) gives you the language set in the user's browser (`en`)
*   [`translate.getBrowserCultureLang()`](https://ngx-translate.org/reference/translate-service-api/#getbrowserculturelang) which gives you the complete language code such as `en-US` or `en-GB`.

src/app/app.config.ts

```typescript
import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection,
    inject, provideAppInitializer
     } from '@angular/core';

...

export const appConfig: ApplicationConfig = {
  providers: [
    ...
    provideTranslateService({
      fallbackLang: 'en',
      loader: provideTranslateHttpLoader({
        prefix: '/i18n/',
        suffix: '.json'
      })
     }),
     provideAppInitializer(() => {
       const  translate = inject(TranslateService);
       translate.use(translate.getBrowserLang() || "en");
     })
  ]
};
```

This switches to the language set in your browser/computer by default. If the language can't be determined, it switches to English (`en`). If the language is not supported, e.g., `fr`, ngx-translate will fall back to `fallbackLang`, which is configured as `en`.

## Step 7: Complex Translations: Parameters & Translations in Code

### Using Parameters in Your Translations

ngx-translate also supports simple parameters in translations out of the box. They are passed as an object, and the keys can be used in the translation strings.

*   `{{'id' | translate: {parameter:value} }}`
*   `<element [translate]="'id'" [translateParams]="{parameter:value}"></element>`
*   `<element translate [translateParams]="{parameter:value}">id</element>`

In your **app.html**, it looks like this:

src/app/app.html

```html
<h2>Translations with parameters</h2>

<p>{{ 'demo.interpolation.pipe-with-parameters' | translate:{name} }}</p>

<p [translate]="'demo.interpolation.attribute-with-parameters'" [translateParams]="{name}"></p>

<p translate [translateParams]="{name}">demo.interpolation.id-as-content-with-parameters</p>
```

Also add the `name` property to **app.ts**:

src/app/app.ts

```typescript
export class App {
    name = "Andreas";
    ...
```

Switch back to BabelEdit and use the **Add ID** with the extractor as we did before.

| ID  | text |
| --- | --- |
| `demo.interpolation.attribute-with-parameters` | Attribute with parameter: Hello {{name}}! |
| `demo.interpolation.id-as-content-with-parameters` | ID as content with parameter: Hello {{name}}! |
| `demo.interpolation.pipe-with-parameters` | Pipe with parameter: Hello {{name}}! |

The parameters are put between `{{` and `}}`. So `\{\{name}}` is replaced with the value of the property name in the component.

Use the **Pre-translate** functionality to create the translation for the other language. Press **Save** to update the JSON files.

### Using Translations in Your Components (TypeScript Code)

Sometimes you have to translate messages in your code and not only in the template. To do so, start by adding a marker function to your code. This helps us to extract the translation IDs from the code later, making management of your translations much easier:

src/app/app.ts

```typescript
import { _ } from '@ngx-translate/core';
```

ngx-translate has three main functions to use translations in your code:

*   [`instant()`](https://ngx-translate.org/reference/translate-service-api/#instant)  
    Returns the translation for the current language, but requires that the translation file has already been loaded. Use with caution, as calling this too early may result in missing translations.
*   [`get()`](https://ngx-translate.org/reference/translate-service-api/#get)  
    Returns an observable that emits the translations for the current language. If the translation file is not yet loaded, it waits until loading is complete. The observable completes after emitting the translation.
*   [`stream()`](https://ngx-translate.org/reference/translate-service-api/#stream)  
    Returns an observable that emits updated translations whenever the active language changes. Also waits for the first load.

This code update demonstrates all three methods:

src/app/app.ts

```typescript
import {Component, inject, OnDestroy, OnInit} from "@angular/core";
import {Subscription} from "rxjs";
import {_, TranslateDirective, TranslatePipe, TranslateService} from "@ngx-translate/core";

@Component({
  selector: 'app-root',
  imports: [TranslateDirective, TranslatePipe],
  templateUrl: './app.html',
  styleUrl: './app.scss'
})

export class App implements OnInit, OnDestroy {
  public name = "Andreas";

  private subscription?: Subscription;
  private translate = inject(TranslateService);

  ngOnInit(): void
  {
    this.translate.get(_("demo.interpolation.instant"), {name: "John"})
        .subscribe((text: string) =>
        {
          console.log(`using get(): ${text}`);
        });

    // instant does not work here - the language is not loaded.
    const text2 = this.translate.instant(_("demo.interpolation.instant"), {name: "John"});
    console.log(`using instant() too early: ${text2}`);

    this.translate.use(this.translate.getCurrentLang())
        .subscribe(() =>
        {
          // instant can be used after the language is loaded
          // we wait for it by setting the current language again
          // the observable emits as soon as the language is loaded
          const text2 = this.translate.instant(_("demo.interpolation.instant"), {name: "John"});
          console.log(`using instant() after loading is done: ${text2}`);
        });

    this.subscription = this.translate.stream(_("demo.interpolation.instant"), {name: "John"})
        .subscribe((text: string) =>
        {
          // this subscription does not complete - we have to unsubscribe later
          console.log(`using stream(): ${text}`);
        });
  }

  ngOnDestroy(): void
  {
    this.subscription?.unsubscribe();
  }
  ...
```

Switch back to BabelEdit and use the **Add ID** with the extractor as we did before.

| ID  | text |
| --- | --- |
| `demo.interpolation.instant` | Attribute with parameter: Instant with parameter: Hello {{name}}! |

Open the JavaScript console in your browser and restart the app.

You should see the following:

```
app.ts:30 using instant() too early: demo.interpolation.instant
app.ts:39 using instant() after loading is done: Instant with parameter: Hello John!
app.ts:26 using get(): Instant with parameter: Hello John!
app.ts:45 using stream(): Instant with parameter: Hello John!
```

... and after switching languages:

```
app.ts:45 using stream(): Instant mit Parameter: Hallo John!
app.ts:45 using stream(): Instant with parameter: Hello John!
```

## Step 8: Testing

For testing, you have 2 choices:

*   without a language file loaded
*   with a language file loaded

If you don't load a language file during the test, ngx-translate will fall back to displaying the translation IDs. This is fine for testing the application logic. However, some functionalities can't be tested, like the use of parameters.

### Language Independent Tests

For testing the app without loading a language file, modify **src/app/app.spec.ts** like this:

This is the version for standalone components:

src/app/app.spec.ts

```typescript
import { TestBed } from '@angular/core/testing';
import { App } from './app';
import {} from 'jasmine';
import {provideTranslateService} from '@ngx-translate/core';

describe('App', () => {
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [App],
      providers: [
        provideTranslateService()
      ]
    }).compileComponents();
  });

  it('should create the app', () => {
    const fixture = TestBed.createComponent(App);
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });

  it('should render title', () => {
    const fixture = TestBed.createComponent(App);
    fixture.detectChanges();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('h1')?.textContent).toContain('demo.title');
  });
});
```

### Language Dependent Tests

To simplify the tests, we use a class called `StaticTranslationLoader`. It pretends to load the translations from the server, but in reality, it just uses an import.

src/app/static-translation-loader.ts

```typescript
import { TranslateLoader, TranslationObject } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';

import * as TranslationsDE from '../../public/i18n/de.json';
import * as TranslationsEN from '../../public/i18n/en.json';

const TRANSLATIONS: Record<string,TranslationObject> = {
    en: TranslationsEN,
    de: TranslationsDE
};

export class StaticTranslationLoader implements TranslateLoader {
    public getTranslation(lang: string): Observable<TranslationObject> {
        const translation = TRANSLATIONS[lang];
        if (translation) {
            return of(translation);
        } else {
            console.error(`Unknown language: ${lang}`);
            return of({});
        }
    }
}
```

Update the tests as follows:

src/app/app.i18n.spec.ts

```typescript
import { TestBed } from '@angular/core/testing';
import { App } from './app';
import {} from 'jasmine';
import {provideTranslateService, provideTranslateLoader, TranslateService} from '@ngx-translate/core';
import {StaticTranslationLoader} from "./static-translation-loader";

describe('App', () => {
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [App],
      providers: [
        provideTranslateService({
          loader:provideTranslateLoader(StaticTranslationLoader),
          lang: 'en'
        })
      ]
    }).compileComponents();
  });

  it('should create the app', () => {
    const fixture = TestBed.createComponent(App);
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });

  it('should render title in english (default)', () => {
    const fixture = TestBed.createComponent(App);
    fixture.detectChanges();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('h1')?.textContent).toContain('Translation Demo');
  });

  it('should render title in german', () => {
    const fixture = TestBed.createComponent(App);

    const translateService = TestBed.inject(TranslateService);
    translateService.use('de');

    fixture.detectChanges();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('h1')?.textContent).toContain('Übersetzungs Demo');
  });
});
```

## Step 9: Pluralization in Angular with NGX-Translate Using ICU Formatted Messages

> **Danger:** This currently does not work since the `ngx-translate-messageformat-compiler` is not yet available in a version that is compatible with ngx-translate 17.
> 2025-07-31

Sometimes it's not enough to simply add a value to your translations. There are cases where parts or even the whole sentence have to change.

Consider the following situation: You want to display the number of images a user has uploaded.

*   No image uploaded yet.
*   One image uploaded.
*   123 images uploaded.

Or, you want to display a dynamic value:

*   My favorite color is green.
*   My favorite color is red.
*   My favorite color is blue.

The `ngx-translate-messageformat-compiler` is what you need now! It parses messages using the ICU syntax.

Install the plugin using the following commands:

```bash
npm install ngx-translate-messageformat-compiler @messageformat/core
```

Next, you have to tell ngx-translate to use the message format compiler for rendering the translated messages in **src/app/app.module.ts** or **src/app/app.config.ts**:

src/app/app.module.ts

```typescript
// import ngx-translate-messageformat-compiler
import {TranslateMessageFormatCompiler} from 'ngx-translate-messageformat-compiler';

...

        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: HttpLoaderFactory,
                deps: [HttpClient]
            // highlight-start
            },

            // compiler configuration
            compiler: {
                provide: TranslateCompiler,
                useClass: TranslateMessageFormatCompiler
            }
            // highlight-end
        })
    ],
```

Update **app.html** to render the new demo messages:

```html
<div class="title">
    <h1>ngx-translate message format compiler demo</h1>

    <span>Switch languages at runtime: </span>
    <button (click)="useLanguage('en')">en</button>
    <button (click)="useLanguage('de')">de</button>
</div>

<div>
    <h3>Pluralization</h3>

    <label for="count-select">Select count: </label>
    <select id="count-select" [(ngModel)]="count" (change)="onCountChange($event)">
        <option value="0">0</option>
        <option value="1">1</option>
        <option value="5">5</option>
    </select>

    <p id="icuPluralization" translate="" [translateParams]="{count}">icu.pluralization</p>
    <p>{{'icu.pluralization' | translate:{ count } }}</p>

    <h3>Selection</h3>

    <label for="gender-select">Select Gender: </label>
        <select id="gender-select" [(ngModel)]="gender" (change)="onGenderChange($event)">
        <option value="male">Male</option>
        <option value="female">Female</option>
        <option value="other">Other</option>
    </select>

    <p id="icuSelect" translate [translateParams]="{ gender, 'product': 'BabelEdit' }">icu.select</p>
    <p>{{'icu.select' | translate:{ gender, 'product': 'BabelEdit' } }}</p>
</div>
```

For some reason not obvious to me, the following syntax does not work with the message compiler. Or better said: It works but gives an error in the browser console.

```html
<p [translate]="'icu.pluralization'" [translateParams]="{count}"></p>
```

Finally, update the **public/i18n/en.json** file with the new ICU syntax:

public/i18n/en.json

```json
{
	"icu": {
		"pluralization": "There {count, plural, =0{is no apple} one{is one apple} other{there are several apples}}.",
		"select": "{gender, select, male{He uses} female{She uses} other{They use }} {product}"
	}
}
```

and **public/i18n/de.json**

public/i18n/de.json

```json
{
	"icu": {
		"pluralization": "Da {count, plural, =0{ist kein Apfel} one{ist ein Apfel} other{sind mehrere Äpfel}}.",
		"select": "{gender, select, male{Er verwendet} female{Sie verwendet} other{Sie verwenden }} {product}."
	}
}
```

Please note that the ICU templates used by the message parser use single braces `{}` to enclose parameters, in contrast to the template format of ngx-translate.

You can read more about the ICU syntax here: [ICU User Guide](https://unicode-org.github.io/icu/userguide/).

## Step 10: Fix Glitches When Using TranslateLoader

The translation files are loaded after the application is initialized, which is why you can see the translation message IDs for a short time.

![Display glitch with ngx-translate when loading translations](https://www.codeandweb.com/static/loader-glitch-animation-f8bec65c7a5827401f712fdfb1095a72.gif)

The easiest way to avoid this is to add your main language as static data to your application. You can do this using the `StaticTranslationLoader` introduced above. Another option is to use [setTranslation()](https://ngx-translate.org/reference/translate-service-api/#settranslation) in your app.

First enable loading of JSON files in your **tsconfig.app.json**

tsconfig.app.json

```json
"compilerOptions": {
    ...
    "resolveJsonModule": true,
    "esModuleInterop": true
    ...
},
```

Open your **app.ts** and load your default language at the top using an import.

Change the constructor to set the translations from the file as follows:

src/app/app.ts

```typescript
import defaultLanguage from '../../public/i18n/en.json';
...

constructor(private translate: TranslateService) {
    translate.setTranslation('en', defaultLanguage);
    translate.setFallbackLang('en');
}
```

## Step 11: Use HTML Formatted Messages

It's also possible to use HTML-formatted messages in your translations.

E.g.

*   demo.paragraph: `Try <strong>BabelEdit</strong>! This translation editor is made for <strong>ngx-translate</strong>!`

To render them, simply use the `innerHTML` attribute with the pipe on any element.

src/app/app.html

```html
<div [innerHTML]="'demo.paragraph' | translate"></div>
```

`[innerHTML]` should be safe to use because it uses Angular's `DomSanitizer` to filter potentially harmful tags like `<script>` or `<style>`.

## Conclusion

With ngx-translate, it's easy to create a multilingual version of your Angular app.

Use ngx-translate-extract to keep your translation files up-to-date.

Finally, BabelEdit helps you manage and edit your translations.

## Structured data

```json
{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "Angular Localization with ngx-translate: Complete Tutorial",
  "author": [
    {
      "@type": "Person",
      "name": "Andreas Löw"
    }
  ],
  "dateModified": "2025-08-01T00:00:00.000Z",
  "image": "/static/3d259f82fbc214c0061ca60d12df938b/a6312/how-to-translate-your-angular-app-with-ngx-translate.png"
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "How to Use NGX-Translate with Angular",
  "image": {
    "@type": "ImageObject",
    "url": "https://www.codeandweb.com/static/3d259f82fbc214c0061ca60d12df938b/f3583/how-to-translate-your-angular-app-with-ngx-translate.png",
    "height": "600 px",
    "width": "1200 px"
  },
  "step": [
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#add-ngx-translate-to-app",
      "text": "Add NGX-Translate to Your Angular Application"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#setup-translate-service",
      "text": "Set Up the TranslateModule and TranslateService"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#use-translation-markup",
      "text": "Use Translations in Your Templates"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#create-json-translation-file",
      "text": "Create Your JSON Translation Files"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#translate-to-other-languages",
      "text": "Translate Your App to Other Languages"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#switch-languages-at-runtime",
      "text": "Switch Languages at Runtime"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#complex-translations",
      "text": "Complex Translations: Parameters and Translations in Code"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#testing",
      "text": "Testing Your Translations"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#pluralization",
      "text": "Pluralization in Angular with NGX-Translate Using ICU Formatted Messages"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#ngx-translate-glitches",
      "text": "Fix Glitches When Using TranslateLoader"
    },
    {
      "@type": "HowToStep",
      "url": "https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-angular-app-with-ngx-translate#use-html-messages",
      "text": "Use HTML Formatted Messages"
    }
  ],
  "totalTime": "PT15M"
}
```
