Angular translation: ngx-translate vs automatic translation (2026)
Angular ships two approaches to i18n — a compile-time framework built into the platform and a popular runtime library — plus the option of DOM-based automatic translation that requires no code changes at all. Here is how each one works, when it fits, and how to choose.
Angular's two built-in i18n approaches
Angular has its own i18n framework, @angular/localize, which uses XLIFF files and compile-time translation. The key characteristic: you run a separate build per locale. Each language produces its own optimised bundle with translations baked in at build time — the fastest possible runtime because there is no translation lookup overhead in the browser.
The popular third-party alternative is @ngx-translate/core, which works at runtime. You ship one bundle and load locale JSON files on startup. Users switching languages get the new strings without a page reload.
| Approach | When translations apply | Bundles per language |
|---|---|---|
@angular/localize | Build time | One per locale |
@ngx-translate/core | Runtime (startup) | One shared bundle |
| Automatic translation | Runtime (DOM) | One shared bundle |
Scroll horizontally to view every column.
ngx-translate in practice
@ngx-translate/core is the most widely used Angular i18n library outside of the built-in framework. The core pattern involves three pieces:
- →TranslateModule — imported into every Angular module (or standalone component) that needs translations. Provides the pipe and directive.
- →TranslateService — injected into components that need to switch language programmatically or access translations in TypeScript.
- →translate pipe — used in templates:
{{ "HELLO" | translate }}. Reactive: updates automatically when the active language changes. - →HttpLoader — the default loader fetches
/assets/i18n/fr.jsonon language switch. You maintain one JSON file per locale.
The ecosystem is mature: community packages handle ICU-style plurals, lazy-loaded translation modules, and server-side rendering. Most Angular teams that started projects before Angular 13 are on ngx-translate.
The Angular i18n cost
Both built-in approaches carry a maintenance overhead that is easy to underestimate at the start of a project.
@angular/localize: one CI build per locale
A separate build per language is fast for visitors but expensive in CI. At 5 languages, build time and artifact storage roughly quintuples. At 10+ languages — a common target for global products — the matrix becomes a real infrastructure problem: longer pipelines, more storage, more complex deployment.
ngx-translate: locale JSON files to maintain
Every string in the app needs a key in every locale JSON file. As the app grows, keeping files in sync becomes a source of bugs: missing keys fall back silently, stale translations ship without warning, and type-safe key management requires additional tooling to enforce.
Both: translation files are a moving target
UI copy changes frequently during product development. Every string change means updating XLIFF or JSON files, re-extracting keys, and coordinating with translators or a translation management system. For teams without a dedicated localization workflow, this overhead accumulates quickly.
When Angular's i18n tooling is the right call
The built-in Angular i18n stack genuinely excels in specific contexts:
- →Enterprise apps with a dedicated localization team. If you have professional translators, a TMS, and a review workflow, XLIFF extraction plugs directly into those processes.
- →Compliance-requiring human-approved translations. Financial, legal, or medical apps that must not ship unreviewed copy benefit from the explicit translation approval step that XLIFF workflows enforce.
- →Heavy ICU message format usage.Plural and select rules (e.g., “{count, plural, one {1 result} other {# results}}”) are first-class citizens in
@angular/localizeand well-supported in ngx-translate with an ICU plugin. - →Build-time optimisation per locale is required. If bundle size is critical and each market has genuinely different content (not just translated strings), per-locale builds allow tree-shaking unused locale-specific code.
When automatic translation wins
DOM-based automatic translation — adding a script tag to the Angular app's host page — skips the translation pipeline entirely. There are clear scenarios where this is the better choice:
- →Existing Angular app adding multilingual without rebuilding. A working app with no i18n investment can become multilingual today without touching a single component.
- →Startup teams without i18n engineers. Setting up ngx-translate or
@angular/localizecorrectly takes time. A script tag can reduce integration work, but setup time varies by app. - →SaaS dashboards with rapidly changing UI. When the UI copy changes weekly, maintaining translation files becomes a full-time job. Automatic translation keeps up with the DOM without any file updates.
- →Time to market matters. Reaching French, German, Spanish, and Japanese users in a day — not a quarter — is a real competitive advantage for many products.
How Lingvit works with Angular
Lingvit is added to an Angular app via a single script tag in src/index.html before the closing </body> tag.
Angular's change detection updates the DOM as components re-render. Lingvit uses a MutationObserver to detect those DOM mutations and translate new content as it appears — this works correctly with both zone.js-based change detection and the newer signal-based reactivity introduced in Angular 16+.
Angular 15+ Standalone Components
Works without modification. Standalone components render to the same DOM; MutationObserver captures all updates regardless of whether the app uses NgModule or standalone architecture.
Angular Universal / SSR
Server-side rendering is supported. The script tag is added to the server-rendered HTML template. After hydration, MutationObserver handles any client-side DOM updates from Angular's router or component re-renders.
Older NgModule pattern
Apps on Angular 12 and earlier using NgModules work identically. The script tag is host-page level — it has no dependency on the Angular version or module system inside the app.
Setup comparison
| Step | @angular/localize | ngx-translate | Lingvit |
|---|---|---|---|
| Install | ng add @angular/localize | Install package, configure TranslateModule | — |
| Mark strings | Add i18n attribute to every element | Wrap every string with translate pipe | — |
| Translation files | Extract XLIFF, fill per locale | Write JSON per locale | — |
| CI pipeline | Build matrix per locale | Single build | Single build |
| Add languages | New build per locale | New JSON file | Dashboard toggle |
| Script tag in index.html | Not needed | Not needed | One tag before </body> |
Scroll horizontally to view every column.
Frequently asked questions
- Does Lingvit work with Angular SSR / Angular Universal?
- Yes. Add the Lingvit script tag to the HTML template served by the server (typically src/index.html or the Express server template). The widget initialises after Angular hydrates the page on the client side, and MutationObserver handles subsequent DOM updates from Angular's router and component re-renders.
- Does it work with Angular Material components?
- Yes. Angular Material renders standard DOM elements — MatButton, MatInput, MatTable, and all other components produce normal HTML that MutationObserver observes. Tooltips and overlays that attach to the document body are also captured.
- What about Nx monorepos with multiple Angular apps?
- Each Angular app in an Nx monorepo is a separate deployable with its own index.html. Add the Lingvit script tag to each app's index.html independently. You can use the same Lingvit project for apps on the same domain, or separate projects per app — the dashboard lets you manage both configurations.
Related guides
Add languages to your Angular app
Works with Angular 15+, Angular Material, and Angular Universal. One script tag.