How to Translate Angular Components with ngx-translate
ngx-translate allows you to easily integrate Angular translation functionality into both your component templates and your TypeScript code, making it simple to build multilingual Angular applications.
ngx-translate v18 is built on Angular Signals.
Signals are reactive values you call as a function to read the current value
(e.g. currentLang()). Reads inside templates, computed(), and effect() are tracked
automatically, so the UI updates whenever the underlying signal changes, with no subscriptions
and no manual change detection. If you’re new to Signals, the official Angular guide
is a 5-minute read and covers everything you need to follow the examples below.
Using translations in your component templates is straightforward. You just need to make sure that the
TranslatePipe or TranslateDirective are available in your component for internationalization.
Import the TranslatePipe and TranslateDirective in your component like this:
import {Component} from '@angular/core';import {TranslatePipe, TranslateDirective} from "@ngx-translate/core";
@Component({ selector: 'app-root', standalone: true, imports: [TranslatePipe, TranslateDirective], templateUrl: './app.component.html', styleUrls: ['./app.component.scss']})export class YourComponent { ...}The easiest way to translate text is using the TranslatePipe:
<div>{{ 'app.hello' | translate }}</div>It’s also possible to add parameters like this:
<div>{{ 'app.hello' | translate:{name} }}</div>And in your component define name like this:
name = "Andreas"In the language file, use {{name}} as placeholder:
{ "app.hello": "Hello {{name}}!"}This is how you use the directive:
<div [translate]="'app.hello'" [translateParams]="{name: 'John'}"></div>*translateBlock is a new structural directive in v18 that exposes a typed t()
function to a template block. It’s the cleanest way to translate several keys in one
place, and the recommended replacement for the deprecated content-as-key pattern:
<ng-container *translateBlock="let t"> <h1>{{ t('app.title') }}</h1> <p>{{ t('app.hello', { name: user.name }) }}</p></ng-container>Add TranslateBlockDirective to your component’s imports array.
Internally t() delegates to TranslateService.instant(). instant() reads the
store’s translations signal, which establishes Angular signal tracking during the
block’s template evaluation — so the block updates automatically when the language
changes or translations are reloaded, with no subscription or ChangeDetectorRef
work on your side.
Multiple keys in one block:
<ng-container *translateBlock="let t"> <h1>{{ t('page.title') }}</h1> <p>{{ t('page.body') }}</p> <button>{{ t('common.save') }}</button> <button>{{ t('common.cancel') }}</button></ng-container>With interpolation parameters:
<ng-container *translateBlock="let t"> <p>{{ t('greeting', { name: user.name }) }}</p> <p>{{ t('items.count', { count: items.length }) }}</p></ng-container>When to choose which form. *translateBlock is the cleanest fit when you have
three or more keys in one block, or when you want to pass params to several keys
without repeating the pipe syntax. For a single key per element, the pipe
({{ 'KEY' | translate }}) or the [translate] attribute directive is shorter.
For more details and the full comparison see
Concepts → Which API should I use?.
The same UI written two ways. With the pipe:
<div class="card"> <h1>{{ 'card.title' | translate }}</h1> <p>{{ 'card.body' | translate:{ name: user.name } }}</p> <button>{{ 'common.save' | translate }}</button> <button>{{ 'common.cancel' | translate }}</button></div>With *translateBlock:
<ng-container *translateBlock="let t"> <div class="card"> <h1>{{ t('card.title') }}</h1> <p>{{ t('card.body', { name: user.name }) }}</p> <button>{{ t('common.save') }}</button> <button>{{ t('common.cancel') }}</button> </div></ng-container>Both produce identical output and identical reactivity. Pick *translateBlock
when you have more than ~2 keys in one place, or when most translation calls
in the block share parameters.
You can easily use raw HTML tags within your translations.
{ "app.hello": "Welcome to my Angular application!<br><strong>This is an amazing app which uses the latest technologies!</strong>"}To render them, simply use the innerHTML attribute with the pipe on any element.
<div [innerHTML]="'app.hello' | translate"></div>[innerHTML] should be safe to use because it uses Angular’s DomSanitizer to filter potentially harmful tags like
<script> or <style>.
To use translations in your code, you can access the TranslateService
using Angular’s modern inject() function. This service allows you to load translations, switch languages, and retrieve
translated messages directly within your application logic.
import {Component, inject} from '@angular/core';import {TranslateService, _} from "@ngx-translate/core";
@Component({ selector: 'app-root', standalone: true, templateUrl: './app.component.html', styleUrls: ['./app.component.scss']})export class YourComponent { private translate = inject(TranslateService);
constructor() { this.translate.get(_('app.hello'), {value: 'world'}).subscribe((res: string) => { console.log(res); //=> 'hello world' }); }}The _() function is a marker function. It helps BabelEdit and the ngx-translate-extract
plugin to distinguish between regular string and translation IDs in your app for automated
extraction of translation stings. Simply wrap all IDs with _() - e.g. _('demo.title).
You might wonder why the get() method
uses a subscription to retrieve translations. The reason is that your language
file might not be loaded at the time the get() function is called, so the
update is triggered once the file finishes loading.
Alternatively, you can use stream(),
which works similarly to get() but has the added benefit of firing again
whenever the language changes. This ensures the translation stays up to date
even if the user switches languages.
For cases where you need an immediate, synchronous translation result, you can
use instant(). This method returns
the translation instantly but only works if the translation file is already
loaded or if translations have been manually set using
setTranslation().
Reacting to the current language with currentLang()
Section titled “Reacting to the current language with currentLang()”In v18 TranslateService.currentLang is a Signal<Language | null>. Call it as a function
to read the active language. It works directly in templates, computed(), and effect(),
so the UI tracks language changes without manual subscriptions:
<p>Current language: {{ translate.currentLang() }}</p>import { Component, computed, effect, inject } from '@angular/core';import { TranslateService } from '@ngx-translate/core';
@Component({ /* ... */ })export class AppComponent { translate = inject(TranslateService);
// Derive a value from the current language isGerman = computed(() => this.translate.currentLang() === 'de');
constructor() { // React to language changes effect(() => { console.log('Language switched to', this.translate.currentLang()); }); }}fallbackLang is the symmetric reactive Signal for the fallback language, with the same
shape: translate.fallbackLang(): Language | null. Use it wherever you’d reach for
currentLang() and want the fallback instead. For a non-reactive snapshot of either,
use getCurrentLang() or
getFallbackLang().
v18 adds a standalone translate() function for use inside an injection context (such as a
component class field). It returns a Signal<Translation> for a single key, or a
Signal<TranslationObject> for an array of keys, and updates automatically on language
changes or translation reloads. No subscription, no cleanup:
import { Component } from '@angular/core';import { translate } from '@ngx-translate/core';
@Component({ /* ... */ })export class MyComponent { greeting = translate('app.hello', { name: 'World' });}Each parameter (key, params, lang) accepts a plain value, an arrow function, or a
Signal. Signal reads inside the function are tracked automatically.
Requesting a translation in a specific language
Section titled “Requesting a translation in a specific language”get(), instant(), stream(), and the standalone translate() function all accept an
optional third lang parameter that bypasses the current/fallback chain. Use it to fetch a
specific language without changing the active one:
// Always returns the German text, regardless of the current languageconst greetingDe = this.translate.instant('app.hello', { name: 'Welt' }, 'de');
this.translate.get('app.hello', { name: 'John' }, 'fr').subscribe(text => { console.log(text); // French});You can construct the translation keys dynamically by using simple string concatenation inside the template:
<ul> @for (language of languages; track language) { <li>{{ ('languages.' + language) | translate }}</li> }</ul>Where languages is an array member of your component:
languages = ['en', 'fr', 'de'];You language file would look like this;
{ "languages": { "en": "English", "fr": "French", "de": "German" }}You can also use the output of the built-in pipes uppercase and lowercase in order to guarantee
that your dynamically generated translation keys are either all uppercase or all lowercase. For example:
<p>{{ 'roles.' + role | lowercase | translate }}</p>role = 'ADMIN';will match the following translation:
{ "roles": { "admin": "Administrator" }}