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
importsarray. They are signal-driven in v18, so they react to language changes automatically without manual subscriptions. -
translate()standalone function A functional API that returns aSignal<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 asTranslateService.translate(key, params?, lang?)for use outside an injection context. -
*translateBlockdirective Exposes a typedt(key, params?)function to a template region. Cleaner than chaining pipes when several keys appear together.t()delegates toTranslateService.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 storeReading 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 delegatescurrentLang/fallbackLangto 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:
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:
provideTranslateService({ loader: provideTranslateHttpLoader({ prefix: '/i18n/' }), fallbackLang: 'en',});
// feature.routes.tsprovideChildTranslateService({ loader: provideTranslateHttpLoader({ prefix: '/i18n/feature/' }),});
// feature/sub.routes.tsprovideChildTranslateService({ 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:
| Form | Returns | Use when |
|---|---|---|
{{ 'KEY' | translate }} | rendered string | one key per element in a template |
<el [translate]="'KEY'"> | rendered text content | binding 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 fn | Signal<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 Translation | one-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:
setTranslation(lang, source, shouldMerge?)runssourcethrough the configuredTranslateCompilerfirst.setCompiledTranslation(lang, alreadyCompiled, shouldMerge?)stores the data as-is — use it for build-time pre-compilation output.
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
TranslateNoOpLoaderreturns an empty object;@ngx-translate/http-loaderships 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}}withparams.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', }), ],};