Features / Angular

Angular

Polyglot provides distinct Angular workflows — scan finds hardcoded UI strings, wrap internationalizes them, and translate fills the catalogs. Angular is the one framework with three serious i18n libraries, so this page covers each and the nuances that come with them.

Saved-plan release status

These guides use CLI 0.14.4 and the extension 0.7.2 workflow. Install or update the CLI before starting. Confirm that polyglot start --help and polyglot wrap --help expose saved plans. In VS Code, use an extension build that includes Localize one screen and run Update CLI if your selected binary is older.

Download the extension below, then open the VS Code Command Palette and run Extensions: Install from VSIX. Select the downloaded file.

Download VS Code extension 0.7.2SHA-256 checksum

Choose and verify a runtime

Configure one installed runtime before wrapping. Missing runtime wiring receives setup guidance; a framework label alone does not make the app ready. ngx-translate and Transloco fixtures check German SSR output and changing state. The localize fixture checks source rendering and marker removal; target-language compilation still requires your app’s own extraction, translation and localized build.

Use the saved-plan workflow for one template and its companion setup changes. Check the runtime profile limits before expanding the migration.

Detection

Polyglot recognizes an Angular project from an angular.json file or an @angular/core dependency. It then scans two surfaces:

  • External templates — the .html files a component points at with templateUrl.
  • Inline templates — backtick template: strings inside a @Component({…}) decorator in a .ts file.

Templates are parsed with full understanding of Angular grammar: {{ }} interpolations, [prop] / (event) / [(ngModel)] bindings, *ngIf / *ngFor structural directives, #ref template variables, and the v17+ @if / @for / @switch / @defer control-flow blocks are all handled. Component-class UI strings passed to known calls (alert, confirm, a snackbar/toast/dialog .open()) are detected too. A .html file is only treated as an Angular template when the project framework is Angular, and host pages (index.html and full HTML documents) are skipped.

Supported libraries

Polyglot auto-detects which i18n library you use from package.json and emits the right code for it. If more than one is present, a runtime pipe library wins.

LibraryDependencyModelCatalog
ngx-translate@ngx-translate/coreRuntime (pipe + service)Nested JSON
Transloco@jsverse/translocoRuntime (pipe + service)Nested JSON
@angular/localize@angular/localizeCompile-time (markers)XLIFF

ngx-translate & Transloco (runtime)

These are the most common choice for retrofitting i18n into an existing app. wrap replaces strings with a translation pipe in templates and a service call in component logic:

<!-- template: element text -->
<h1>Welcome home</h1>
<h1>{{ 'home.welcome_home' | translate }}</h1>

<!-- template: a translatable attribute becomes a bound pipe -->
<input placeholder="Search products" />
<input [placeholder]="'home.search_products' | translate" />

<!-- component logic: a translatable call argument -->
this.snackBar.open('Saved successfully');
this.snackBar.open(this.translate.instant('save.saved_successfully'));

When a component-logic string is wrapped, Polyglot also injects the translator service into the component constructor (creating one if needed) and adds its import:

import { TranslateService } from '@ngx-translate/core';

@Component({ /* … */ })
export class SaveComponent {
  constructor(private translate: TranslateService) {}
}

Transloco is identical except for the call shapes — | transloco, this.transloco.translate('key'), and TranslocoService. Both descend a nested JSON catalog, written to src/assets/i18n/<lang>.json (override with output_dir in polyglot.toml).

Template pipes resolve against the globally provided pipe, so no per-component setup is needed for templates. You do need to register the library once (its provider + a loader pointing at assets/i18n) — polyglot init prints the exact snippet for your library.

@angular/localize (compile-time)

@angular/localize is the official Angular library and works differently: instead of a runtime pipe, you mark text and the compiler extracts it. wrap adds the markers in place — the text itself is never moved:

<!-- element + attribute -->
<h1 i18n="@@home.welcome_home">Welcome home</h1>
<input i18n-placeholder="@@home.search_products" placeholder="Search products" />

<!-- component logic -->
this.snackBar.open($localize`:@@save.saved_successfully:Saved successfully`);

There is no service and no constructor injection — $localize is an ambient global (provided by @angular/localize/init in your polyfills) and i18n is a compile-time attribute.

ng extract-i18n is the source of truth

Polyglot writes a zero-config starter messages.xlf covering the simple cases so you have something to translate immediately. But ng extract-i18n is the authoritative extractor: it generates the complete XLIFF — including the placeholder syntax for rich content — that the Angular build actually consumes. We deliberately do not hand-generate those placeholders (replicating the compiler's exact scheme is fragile and version-coupled). The recommended workflow is:

polyglot wrap            # add i18n / $localize markers
ng extract-i18n          # generate the authoritative messages.xlf
polyglot translate       # fill in target locales
ng build --localize      # one bundle per locale

Rich / inline-formatted content

A run that mixes text with inline elements — e.g. <p>Read the <a>docs</a> first</p> — is handled differently per library, because their capabilities genuinely differ:

  • ngx-translate / Transloco have no safe single-message API for inline markup, so these runs are flagged for manual review rather than fragmented into mistranslated pieces. (Set rich_text = false in polyglot.toml to opt into per-element wrapping instead.)
  • @angular/localize handles inline markup natively, so Polyglot marks the enclosing element with i18n and lets ng extract-i18n emit the placeholders. Nothing is flagged — this is where @angular/localize shines.

Simple links and icon-plus-label patterns (<a>Home</a>, <i></i> New Article) are always wrapped normally — only genuine prose split by formatting is treated as rich.

Safety & idempotency

Every file Polyglot rewrites is re-parsed before it is written — if a change would introduce a syntax error the file is left untouched and reported, never half-wrapped. Wrapping is idempotent: already-wrapped pipes, already-injected services, and i18n-marked elements are skipped on a second run, so wrap is safe to re-run as you add strings. A transparency report (polyglot-i18n-report.md) lists everything wrapped and everything flagged.

Quick start

# install one of: @ngx-translate/core | @jsverse/transloco | @angular/localize
polyglot init            # detects Angular + your library, prints setup
polyglot scan            # see what would be internationalized
polyglot wrap            # apply the codemod
# for @angular/localize: ng extract-i18n
polyglot translate --languages fr,es,de
Angular - Docs | Polyglot