Migration guide v17 → v18
Migrating from v15 or v16? Follow the v15/v16 → v17 guide first, then return here.
v18 rebuilds ngx-translate on Angular Signals. TranslateModule is gone, language state is reactive, and defaultLang aliases have been removed. Most upgrades come down to find-and-replace plus a small number of structural changes.
Most v18 upgrades are mechanical replacements. Open a global search across your project for each string below.
| Search for | What it means |
|---|---|
"@ngx-translate/core": "^17 | Pre-flight: Angular peer dep is now >=18. See Angular 16 → 18 peer dependency bump. |
TranslateModule | Replace module imports with providers. The module is removed in v18. |
TranslateModule.forRoot | Replace with provideTranslateService() in your bootstrap providers. |
TranslateModule.forChild | Replace with provideTranslateService() (isolated) or provideChildTranslateService() (connected, parent fallback). |
.currentLang | Now a Signal. Read sites need (): translate.currentLang(). |
useDefaultLang | Removed. Use fallbackLang only. |
defaultLanguage | Renamed to fallbackLang in the config object. |
setDefaultLang | Renamed to setFallbackLang. |
getDefaultLang | Renamed to getFallbackLang. |
defaultLang (property access) | Replaced by the fallbackLang signal: translate.fallbackLang(). |
onDefaultLangChange | Renamed to onFallbackLangChange. |
DefaultLangChangeEvent | Renamed to FallbackLangChangeEvent. |
setValue( | Removed. Use insertValue(), which returns a new object instead of mutating. |
extend: true | Removed from the root config. Use provideChildTranslateService() for connected children. |
translate.langs | Replaced by translate.getLangs(). |
<el translate>KEY</el> (content as key) | Deprecated; will be removed in v19. Use [translate]="'KEY'" or *translateBlock. |
onTranslationChange (on TranslateStore) | Renamed to translationChange$. |
store.getTranslation( | Removed. Use service.instant(key) or store.getTranslationValue(lang, key). |
store.getValue( | Renamed to store.getTranslationValue() and now public. |
If your build or runtime breaks, find the message below.
| Error or warning | Fix |
|---|---|
npm ERR! ERESOLVE could not resolve … peer @angular/core@">=18" | Upgrade Angular first. See Angular 16 → 18 peer dependency bump. |
Cannot read currentLang of undefined / unexpected null from currentLang | currentLang is now a Signal<Language | null>. Call it: translate.currentLang(). |
TS: Property 'defaultLang' does not exist on type 'TranslateService' | Replace with translate.fallbackLang() (Signal) or translate.getFallbackLang(). |
TS: Property 'setDefaultLang' does not exist on type 'TranslateService' | Replace with translate.setFallbackLang(lang). |
TS: Property 'onDefaultLangChange' does not exist on type 'TranslateService' | Replace with translate.onFallbackLangChange. |
TS: Module '"@ngx-translate/core"' has no exported member 'TranslateModule'. | Remove module imports. Use provideTranslateService() and import TranslatePipe / TranslateDirective directly in component imports. |
TS: 'defaultLanguage' does not exist in type 'RootTranslateServiceConfig' | Rename the config key to fallbackLang. |
TS: 'useDefaultLang' does not exist in type 'RootTranslateServiceConfig' | Remove it. fallbackLang is enough. |
TS: 'extend' does not exist in type 'ChildTranslateServiceConfig' | Move to provideChildTranslateService() (where it is no longer needed, since connected children are the default). |
TS: 'langs' does not exist on type 'TranslateService' | Use translate.getLangs(). |
TS: Argument of type 'string' is not assignable to parameter of type 'string | null' on getCurrentLang() return | getCurrentLang() now returns Language | null. Add a null check or default. |
TS: Cannot find name 'DefaultLangChangeEvent' | Use FallbackLangChangeEvent. |
TS: Cannot find name 'setValue' | Use insertValue() and assign the return value back. |
Console warn: @ngx-translate/core: "<field>" received a bare class (<ClassName>); auto-wrapping with <helperName>(). For clarity, prefer <field>: <helperName>(<ClassName>). | Wrap your class with the matching provider helper, e.g. loader: provideTranslateLoader(MyLoader). |
Console warn: @ngx-translate/core: failed to load "<lang>". currentLang was NOT changed… or child failed to load "<lang>". Cause: | Your loader threw. v18 logs failures instead of swallowing them. Inspect the loader. |
Console warn: @ngx-translate/http-loader: error loading translation for <lang> | HTTP request failed. The language load now returns {} for that resource by default; opt back into v17 fail-fast with failOnError: true. |
Console warn: deprecation warning on <el translate>KEY</el> | Element-text-as-key is deprecated. Use [translate]="'KEY'" or *translateBlock. |
Angular 16 → 18 peer dependency bump — Impact: High (pre-flight)
Section titled “Angular 16 → 18 peer dependency bump — Impact: High (pre-flight)”v18 raises the minimum Angular peer dependency from ^16.0.0 to >=18
(Angular 18 through 22 are all supported). Projects on Angular 16 or 17 must
upgrade Angular first (ng update @angular/core@18) before installing
@ngx-translate/core@18. The library uses signal-based reactivity and Angular DI
features that landed in Angular 18.
If you’re on Angular 18 or newer already, no action needed.
Old code (v17):
@NgModule({ imports: [ TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: MyLoader }, fallbackLang: "en", }), ],})export class AppModule {}TypeScript error: Module '"@ngx-translate/core"' has no exported member 'TranslateModule'.
Fix (v18):
import { bootstrapApplication } from "@angular/platform-browser";import { provideTranslateService, provideTranslateLoader,} from "@ngx-translate/core";
bootstrapApplication(AppComponent, { providers: [ provideTranslateService({ loader: provideTranslateLoader(MyLoader), fallbackLang: "en", }), ],});import { TranslatePipe, TranslateDirective } from "@ngx-translate/core";
@Component({ imports: [TranslatePipe, TranslateDirective], template: `{{ 'HELLO' | translate }}`,})export class MyComponent {}TranslateModule.forChild({ extend: true }) → provideChildTranslateService() (Impact: Medium)
Section titled “TranslateModule.forChild({ extend: true }) → provideChildTranslateService() (Impact: Medium)”A child with extend: true was a connected child sharing the parent’s translation chain. In v18 this is what provideChildTranslateService() does by default.
Old code (v17):
@NgModule({ imports: [ TranslateModule.forChild({ extend: true, loader: { provide: TranslateLoader, useClass: FeatureLoader }, }), ],})export class FeatureModule {}Symptom: The TranslateModule import itself fails to resolve (Module '"@ngx-translate/core"' has no exported member 'TranslateModule'.), so there is no specific error for extend: true. The whole forChild call is gone. Any code still importing TranslateModule won’t compile.
Fix (v18), route-level providers:
import { provideChildTranslateService, provideTranslateLoader } from "@ngx-translate/core";
export const routes: Routes = [ { path: "feature", providers: [ provideChildTranslateService({ loader: provideTranslateLoader(FeatureLoader), }), ], component: FeatureComponent, },];Translation lookup checks the child store first, then walks up to the parent. Child writes never reach the parent store.
TranslateModule.forChild({ isolate: true }) → provideTranslateService() (Impact: Medium)
Section titled “TranslateModule.forChild({ isolate: true }) → provideTranslateService() (Impact: Medium)”An isolated child was a fully independent service with its own language state. In v18, provideTranslateService() creates exactly that: a root-level service with its own store, language, and fallback.
Old code (v17):
@NgModule({ imports: [ TranslateModule.forChild({ isolate: true, loader: { provide: TranslateLoader, useClass: FeatureLoader }, }), ],})export class FeatureModule {}Symptom: As above, the TranslateModule import fails to resolve, so isolate: true has no dedicated error. The whole forChild call needs to be replaced.
Fix (v18):
import { provideTranslateService, provideTranslateLoader } from "@ngx-translate/core";
export const routes: Routes = [ { path: "feature", providers: [ provideTranslateService({ loader: provideTranslateLoader(FeatureLoader), }), ], component: FeatureComponent, },];useDefaultLang and defaultLanguage removed (Impact: Medium)
Section titled “useDefaultLang and defaultLanguage removed (Impact: Medium)”Both config keys have been removed. Use fallbackLang only.
Old code (v17 module config, or v17 provider after the deprecation alias):
TranslateModule.forRoot({ defaultLanguage: "en", useDefaultLang: true,});TypeScript error after upgrading: Object literal may only specify known properties, and 'defaultLanguage' does not exist in type 'RootTranslateServiceConfig' — appears when v17 config keys are passed to v18’s provideTranslateService.
Fix (v18):
provideTranslateService({ fallbackLang: "en",});currentLang is now a Signal (Impact: High)
Section titled “currentLang is now a Signal (Impact: High)”In v17, currentLang was a string getter. In v18 it is a Signal<Language | null>.
Old code (v17):
if (translate.currentLang === "en") { // ...}
const lang: string = translate.currentLang;Runtime symptom: Comparisons silently return false because the signal function is compared to a string. TypeScript may not catch this on its own.
Fix (v18):
if (translate.currentLang() === "en") { // ...}
const lang: string | null = translate.currentLang();The upside: currentLang now plugs straight into computed() and effect(), and Angular template change detection tracks it automatically.
getCurrentLang() can return null (Impact: Medium)
Section titled “getCurrentLang() can return null (Impact: Medium)”The return type changed from Language to Language | null. Before any language has been set, it returns null.
Old code (v17):
const lang: string = translate.getCurrentLang();TypeScript error: Type 'string | null' is not assignable to type 'string'.
Fix (v18):
const lang: string | null = translate.getCurrentLang();// or guard:const lang = translate.getCurrentLang() ?? "en";defaultLang aliases removed (Impact: Medium)
Section titled “defaultLang aliases removed (Impact: Medium)”defaultLang, setDefaultLang(), getDefaultLang(), and onDefaultLangChange have been removed.
Old code (v17):
translate.setDefaultLang("en");const lang = translate.getDefaultLang();const lang2 = translate.defaultLang;translate.onDefaultLangChange.subscribe(({ lang }) => console.log(lang));TypeScript error: Property 'setDefaultLang' does not exist on type 'TranslateService'.
Fix (v18):
translate.setFallbackLang("en");const lang = translate.getFallbackLang();const lang2 = translate.fallbackLang(); // Signaltranslate.onFallbackLangChange.subscribe(({ lang }) => console.log(lang));DefaultLangChangeEvent renamed to FallbackLangChangeEvent (Impact: Low)
Section titled “DefaultLangChangeEvent renamed to FallbackLangChangeEvent (Impact: Low)”Old code (v17):
import { DefaultLangChangeEvent } from "@ngx-translate/core";
translate.onDefaultLangChange.subscribe((event: DefaultLangChangeEvent) => { console.log(event.lang);});TypeScript error: Module '"@ngx-translate/core"' has no exported member 'DefaultLangChangeEvent'.
Fix (v18):
import { FallbackLangChangeEvent } from "@ngx-translate/core";
translate.onFallbackLangChange.subscribe((event: FallbackLangChangeEvent) => { console.log(event.lang);});The mutating setValue() utility is gone. insertValue() returns a new object instead of mutating in place, so you need to assign the result.
Old code (v17):
import { setValue } from "@ngx-translate/core";
setValue(translations, "nested.key", "value"); // mutates translationsTypeScript error: Module '"@ngx-translate/core"' has no exported member 'setValue'.
Fix (v18):
import { insertValue } from "@ngx-translate/core";
translations = insertValue(translations, "nested.key", "value");Child store and parent fallback behavior (Impact: Medium)
Section titled “Child store and parent fallback behavior (Impact: Medium)”In v17, an extend: true child shared the parent’s TranslateStore. In v18, provideChildTranslateService() creates its own TranslateStore and the lookup walks the parent chain: local translations first, parent translations as fallback. Child writes stay in the child store.
Silent behavior change:
- Sibling lazy modules no longer accidentally see each other’s translations.
- A child that intentionally wrote into the shared store to surface keys at the root no longer does so. Use the parent injection context if you need to write to the root.
- Code that read
store.translationsafter a write must re-read, because state updates are now immutable.
If your child needs to be fully independent (no parent fallback at all), use provideTranslateService() at the child level instead.
Loading state inherits downward. v18 introduces isLoading: Signal<boolean> on
every service in the hierarchy. A load triggered at the root marks the root and all
descendants as loading; a load triggered at a child marks only that child’s subtree.
See isLoading and the
loading-indicator recipe for details.
setTranslation() no longer auto-merges with extend (Impact: Low)
Section titled “setTranslation() no longer auto-merges with extend (Impact: Low)”setTranslation(lang, translations) no longer silently merges based on the v17 extend: true config (which is also gone). Pass shouldMerge explicitly.
Old code (v17):
// merged automatically because extend: true was set on the servicetranslate.setTranslation("en", newTranslations);Silent behavior change: If your service was previously configured with extend: true, setTranslation(lang, translations) auto-merged. In v18, extend: true is gone and the call always replaces by default. Pass true as the third argument to keep the merge behavior.
Fix (v18):
translate.setTranslation("en", newTranslations, true);Mixed child content in [translate] elements (Impact: Medium)
Section titled “Mixed child content in [translate] elements (Impact: Medium)”[translate]="'KEY'" now sets el.textContent directly. Any child HTML inside the element is overwritten.
Old code (v17), may have rendered the <b>:
<span [translate]="'GREETING'"><b>Bold child</b></span>Runtime symptom: Child HTML disappears after first render.
Fix (v18): Pull the markup out of the translated element.
<span [translate]="'GREETING'"></span>Element text as translation key (Impact: Medium) (deprecated, removed in v19)
Section titled “Element text as translation key (Impact: Medium) (deprecated, removed in v19)”Using element text content as the key still works in v18, but emits a console.warn and will be removed in v19.
Old code (v17), still works in v18 with a warning:
<span translate>HELLO</span>Console warning: A deprecation message with the offending element as a second argument so DevTools can highlight it.
Fix (v18):
<span [translate]="'HELLO'"></span>
<!-- or, for multiple keys in one block --><ng-container *translateBlock="let t"> <span>{{ t('HELLO') }}</span></ng-container>Old code (v17):
const languages = translate.langs;TypeScript error: Property 'langs' does not exist on type 'TranslateService'.
Fix (v18):
const languages = translate.getLangs();get("") and instant("") now return an empty string instead of throwing. If you wrapped these calls in try/catch to detect an empty key, switch to an explicit check.
Old code (v17):
try { const value = translate.instant(key);} catch { // handle empty key}Fix (v18):
if (!key) { // handle empty key explicitly}const value = translate.instant(key);HTTP loader 404 → empty object (Impact: Medium)
Section titled “HTTP loader 404 → empty object (Impact: Medium)”A 404 on a translation file no longer fails the whole language load. v18 catches the error per resource, logs a console.warn, and treats the missing resource as {}.
Old code (v17): A 404 propagated, failing the language load.
Fix (v18): No code change needed if the new behavior is what you want. To restore v17 fail-fast (useful for catching missing translation files during deploy), pass failOnError:
provideTranslateHttpLoader({ prefix: "/assets/i18n/", failOnError: true,});TranslateStore API changes (direct injection only) (Impact: Low)
Section titled “TranslateStore API changes (direct injection only) (Impact: Low)”Most users do not touch TranslateStore. If you inject it directly:
store.getCurrentLang(),store.setCurrentLang(),store.onLangChange→ useTranslateServiceinstead.store.onTranslationChange→ renamed tostore.translationChange$, plus astore.lastTranslationChangesignal for template/computed reads.store.getTranslation(key)→ removed. Useservice.instant(key)orstore.getTranslationValue(lang, key).store.getValue(lang, key)→ renamed to publicstore.getTranslationValue(lang, key).- State updates are immutable: re-read
store.translations()orstore.getTranslations(lang)after a write.
Pipe and directive internals removed (Impact: Low)
Section titled “Pipe and directive internals removed (Impact: Low)”TranslatePipe.lastKey, lastParams, updateValue(), and TranslateDirective.checkNodes(), updateValue(), getContent(), setContent() have been removed. These were never part of the public API.
v18 finishes the migration to Angular’s standalone provider model. TranslateModule.forRoot/forChild are removed; provideTranslateService() and provideChildTranslateService() configure the same things with less boilerplate and better tree-shaking. Pipe and directive are imported directly into a component’s imports array.
Language state is now reactive. currentLang and fallbackLang are Signals, which means they compose naturally with computed(), effect(), and Angular’s template change detection, no manual onLangChange subscriptions for simple reactivity. The Observable API (onLangChange, onFallbackLangChange, onTranslationChange) is still there for push-style flows.
Naming and types match the Angular team’s inject()-first style: provider helpers, factory function support across all plugins, and stricter return types (getCurrentLang(): Language | null) that surface bugs at compile time instead of at runtime.
- Skim the v18 release notes for new features (signal-driven pipe/directive,
*translateBlock, per-calllang,translate()standalone function, hierarchical services,setCompiledTranslation()for precompiled payloads,getRoot()/getParent()for chain introspection). - For NgModule users, the NgModules support page covers how to bridge
provideTranslateService()into a legacy module. - File anything missing or wrong on the ngx-translate/core issue tracker.