Skip to content

Concepts

ngx-translate v18 is fully standalone, built on Angular Signals, and ships a small set of customizable plugins. This page summarizes the moving parts so the per-API reference pages can stay focused.

  • TranslateService The central service. Manages language state, loads translations, and exposes the API used by the pipe, directive, standalone translate() function, and by your own component code.

  • TranslatePipe and TranslateDirective Template-side translation. Both are standalone and imported directly into a component’s imports array. They are signal-driven in v18, so they react to language changes automatically without manual subscriptions.

  • translate() standalone function A functional API that returns a Signal<Translation>. Useful for component class fields where you want a reactive value without subscribing. Must be called inside an Angular injection context. The same surface is available as TranslateService.translate(key, params?, lang?) for use outside an injection context.

  • *translateBlock directive Exposes a typed t(key, params?) function to a template region. Cleaner than chaining pipes when several keys appear together. t() delegates to TranslateService.instant(), which reads the store’s translations signal; that establishes signal tracking during template evaluation, so the block updates reactively when the language or translations change.

ngx-translate is configured using standalone provider functions:

  • provideTranslateService Root provider. Call once at app bootstrap to set up the TranslateService, default plugins, and any plugin overrides you pass in.

  • provideChildTranslateService Child provider. Call inside route or component providers to create a child service with its own store. Child translations are checked first, then the parent chain provides fallbacks.

TranslateModule was removed in v18. See NgModules Support for the v17 → v18 transition.

v18 uses a parent-chain model. Translation lookups walk from the local store upward to the parent until a key resolves. Sibling subtrees cannot see each other’s translations.

Root injector
└─ provideTranslateService() ← root TranslateService + root store
├─ /feature
│ └─ provideChildTranslateService()
│ ↑ parent chain: keys resolve child-first, then root
└─ /other
└─ provideTranslateService()
↑ isolated subtree root: does NOT fall back to outer root,
own language state and store

Reading top to bottom:

  • The outer provideTranslateService() creates the application root.
  • provideChildTranslateService() on a route attaches a connected child: it has its own store and loader, but it delegates currentLang / fallbackLang to the root and walks up the parent chain when a key isn’t found locally.
  • A nested provideTranslateService() on another route creates an isolated subtree root with its own language state and store. It does not fall back to the outer root.

To introspect the chain at runtime use TranslateService.getParent(), which returns null for any service that terminates the chain (top-level root or isolated subtree root). For the topmost service of the current chain (handy from any depth), use TranslateService.getRoot() — it walks getParent() until it hits the boundary and stops at an isolated subtree root just like getParent() does.

Two siblings with isolated translations. Two routes that should not share keys both call provideTranslateService() again, each with their own loader:

app.routes.ts
export const routes: Routes = [
{
path: 'admin',
providers: [
provideTranslateService({
loader: provideTranslateHttpLoader({ prefix: '/i18n/admin/' }),
}),
],
loadChildren: () => import('./admin/routes'),
},
{
path: 'shop',
providers: [
provideTranslateService({
loader: provideTranslateHttpLoader({ prefix: '/i18n/shop/' }),
}),
],
loadChildren: () => import('./shop/routes'),
},
];

Neither subtree sees the other’s translations, and neither falls back to the outer root.

Three-level nesting. Root → feature → sub-feature, all connected:

app.config.ts
provideTranslateService({
loader: provideTranslateHttpLoader({ prefix: '/i18n/' }),
fallbackLang: 'en',
});
// feature.routes.ts
provideChildTranslateService({
loader: provideTranslateHttpLoader({ prefix: '/i18n/feature/' }),
});
// feature/sub.routes.ts
provideChildTranslateService({
loader: provideTranslateHttpLoader({ prefix: '/i18n/feature/sub/' }),
});

Lookup walks sub → feature → root. A key defined at the root is visible inside sub-feature templates as long as no intermediate level overrides it.

Sharing a key from the root in a child template. No special wiring — connected children walk the parent chain. Define COMMON.SAVE in the root translation file and {{ 'COMMON.SAVE' | translate }} works inside any child template.

Pitfall: child write that doesn’t reach the root. Calling translate.set() or translate.setTranslation() inside a child writes to the child store; the root never sees the value. If you intended to set a translation globally, capture the root service from a parent injection context first:

// In a child component, write to the root store explicitly.
// getRoot() walks the parent chain and stops at the nearest isolation
// boundary — works for any depth, unlike a single getParent() hop.
const root = inject(TranslateService).getRoot();
root.set('GLOBAL.MESSAGE', 'Hallo Welt!', 'de');

Showing a “language switching…” indicator. isLoading is a Signal<boolean> that flips true while a use() or setFallbackLang() call is fetching translations. Scope is downward: a load at the root marks the root and all descendants; a load at a child marks only that child’s subtree. So inject the service at the scope where the spinner should live — root for an app-shell spinner, the nearest child for a local spinner inside a lazy-loaded subtree:

@if (translate.isLoading()) {
<app-spinner />
}

Sibling subtrees that finished loading earlier do not flip back to loading when another subtree starts a fresh load. See the recipe Show a loading indicator during language switch.

v18 exposes several translate-named shapes. Pick the one that matches your context:

FormReturnsUse when
{{ 'KEY' | translate }}rendered stringone key per element in a template
<el [translate]="'KEY'">rendered text contentbinding text content of a specific element (overwrites any child HTML)
<el *translateBlock="let t">typed t(key, params?)multiple keys in one template block
translate('KEY') standalone fnSignal<Translation>reading translations in component class code, inside an injection context
service.translate('KEY')Signal<Translation>same as above but outside an injection context
service.instant('KEY')sync Translationone-shot read where you do not need reactivity
service.get('KEY')Observable<Translation>one-shot observable read (completes once)
service.stream('KEY')Observable<Translation>live observable that re-emits on lang/translation change

And a related distinction for writing into the store:

Each plugin has an abstract base class and a provideTranslate* helper. Default implementations are registered automatically when you call provideTranslateService(). Only override the ones you need.

  • TranslateLoader Loads translation data for a given language. The default TranslateNoOpLoader returns an empty object; @ngx-translate/http-loader ships an HTTP-backed implementation with built-in multi-resource support.

  • TranslateCompiler Pre-processes translations after they are loaded (useful for ICU MessageFormat or other one-time work). The default is a no-op.

  • TranslateParser Interpolates {{ placeholder }} values at translation time. Called on every lookup, so keep it lightweight. The default replaces {{name}} with params.name.

  • MissingTranslationHandler Decides what to return when a key is missing in the current and fallback languages. The default returns the key itself.

Provider functions are the canonical way to wire plugins. Each plugin slot on provideTranslateService() / provideChildTranslateService() accepts the matching provideTranslate* helper, an Angular Provider object, a bare class, or a bare factory function. See Configuration for the full shape.

import {
provideTranslateService,
provideTranslateLoader,
provideTranslateCompiler,
} from '@ngx-translate/core';
export const appConfig: ApplicationConfig = {
providers: [
provideTranslateService({
loader: provideTranslateLoader(MyLoader),
compiler: provideTranslateCompiler(MyCompiler),
fallbackLang: 'en',
}),
],
};
Imprint Privacy