AngularAtlased. 22.2

Cap. 17Securitate & globalizare75 / 75

Internaționalizare (i18n)

$localize, atributul i18n, ICU plural/select, build per limbă vs. traduceri la runtime și formatarea după locale.

Angular are internaționalizare integrată, prin pachetul @angular/localize. Marchezi textele în template cu atributul i18n și în TypeScript cu $localize. CLI-ul le extrage într-un fișier de traduceri (XLIFF, JSON…), iar la build construiește câte o variantă a aplicației pentru fiecare limbă, cu textele deja înlocuite. Rezultatul e la fel de rapid ca aplicația netradusă: nicio căutare de chei la runtime.

Pe lângă texte, i18n înseamnă și pluraluri și formatare. „2 fișiere”, dar „20 de fișiere”; 1.499,50 în română, 1,499.50 în engleză; data cu numele zilei în limba utilizatorului. Aici intră ICU-urile și datele de locale.

De ce contează
Să adaugi i18n mai târziu e scump: fiecare text hardcodat trebuie găsit, marcat și testat. Să-l pornești de la început costă aproape nimic, iar pluralurile și formatele corecte contează și într-o aplicație doar în română.

Marcarea textelor

ng add @angular/localize instalează pachetul și încarcă @angular/localize/init ca polyfill. Atributul i18n marchează un element; valoarea opțională are forma sens|descriere@@id. Sensul și descrierea ajută traducătorul. Id-ul custom (@@cart.title) face ca traducerea să nu se piardă când schimbi textul sursă. Pentru atribute folosești i18n-alt, i18n-title etc. În cod, $localize e un tagged template cu aceeași sintaxă, iar interpolările primesc nume (${count}:count:).

ng add @angular/localize
# adaugă @angular/localize/init la polyfills și tipurile în tsconfig — fără el, i18n și $localize nu pornesc

ICU: plural și select

Expresiile ICU aleg textul după valoare. plural folosește categoriile CLDR ale limbii (LOCALE_ID), plus potriviri exacte (=0). select alege după un string. Româna are trei categorii:

  • one pentru 1;
  • few pentru 0, 2–19 și 101–119;
  • other pentru restul (20, 100, 120…), unde apare „de”: „20 de fișiere”.

Regula e în datele de locale Angular (@angular/common/locales/ro). ICU-urile funcționează în orice template, chiar și fără traduceri.

LiveICU cu regulile limbii române (template real)

Ai încărcat 2 fișiere ca editor.

Categoria CLDR pentru 2 în limba română:few

Încearcă 0, 1, 2, 19, 20, 101 și 120 și urmărește cum se schimbă forma și categoria CLDR. Schimbă rolul pentru select.

Extragere și fișiere de traduceri

ng extract-i18n adună toate mesajele marcate. Implicit scrie messages.xlf (XLIFF 1.2); cu --format poți alege și XLIFF 2, XMB, JSON sau ARB. Copiezi fișierul pentru fiecare limbă (messages.en.xlf), traducătorii completează <target>, iar id-urile leagă sursa de traducere.

ng extract-i18n --output-path src/locale             # implicit: messages.xlf (XLIFF 1.2)
ng extract-i18n --format=xlf2 --output-path src/locale
ng extract-i18n --format=json --output-path src/locale

Build per limbă vs traduceri la runtime

Varianta standard e la build: în angular.json declari sourceLocale și locales, iar cu "localize": true CLI-ul produce câte un folder per limbă (/ro/, /en/…). Serverul alege folderul după URL sau preferința utilizatorului. Schimbarea limbii înseamnă navigare la alt folder, deci reîncărcare.

Alternativa e la runtime: un singur build, iar în main.ts încarci traducerile cu loadTranslations() înainte de bootstrap. Și aici, schimbarea limbii înseamnă reîncărcare, pentru că textele marcate cu i18n în template-uri sunt evaluate la crearea componentelor. Dacă ai nevoie să schimbi limba fără reload, folosește o bibliotecă de traducere la runtime.

{
  "projects": {
    "shop": {
      "i18n": {
        "sourceLocale": "ro",
        "locales": {
          "en": "src/locale/messages.en.xlf",
          "de": "src/locale/messages.de.xlf"
        }
      },
      "architect": {
        "build": {
          "options": { "localize": true }
        }
      }
    }
  }
}
Live$localize real, cu traduceri încărcate la runtime

Coșul tău

Ai 3 produse în coș.

Mesajele sunt apeluri $localize cu id-uri (@@cart.title), traduse cu loadTranslations().

Comută între ro, en și de. Demo-ul apelează clearTranslations()/loadTranslations() și reevaluează mesajele $localize (într-un computed). Schimbă numărul de produse: placeholder-ul {$count} se păstrează în traducere.

Demo-ul reevaluează apelurile $localize din cod. Textele marcate cu i18n în template nu se schimbă așa, de aceea aplicațiile reale reîncarcă pagina la schimbarea limbii.

Formatarea după locale

Pipe-urile date, number, currency și percent (și funcțiile formatDate, formatNumber…) folosesc LOCALE_ID. Pentru orice limbă în afară de en-US trebuie să înregistrezi datele cu registerLocaleData. Site-ul ăsta o face pentru ro, cu RON ca monedă implicită (DEFAULT_CURRENCY_CODE). Toate pipe-urile primesc și un parametru locale explicit.

import { registerLocaleData } from '@angular/common';
import localeRo from '@angular/common/locales/ro';
import { DEFAULT_CURRENCY_CODE, LOCALE_ID } from '@angular/core';

registerLocaleData(localeRo);   // necesar pentru orice locale în afară de en-US

export const appConfig: ApplicationConfig = {
  providers: [
    { provide: LOCALE_ID, useValue: 'ro' },              // folosit de date, number, currency, percent, ICU
    { provide: DEFAULT_CURRENCY_CODE, useValue: 'RON' },
  ],
};
LiveAceeași valoare, patru locale
LocaleDatăNumărMonedă (EUR)Procent
rovineri, 9 octombrie 20261.234.567,891.499,50 €18,8 %
enFriday, October 9, 20261,234,567.89€1,499.5018.8%
deFreitag, 9. Oktober 20261.234.567,891.499,50 €18,8 %
ja2026年10月9日金曜日1,234,567.89€1,499.5018.8%
Data, numărul, moneda și procentul sunt formatate cu formatDate/formatNumber/formatCurrency/formatPercent, pentru ro, en, de și ja. Atenție la separatori, ordinea elementelor și simbolul monedei.

Cum schimbă limba site-ul ăsta

Site-ul nu folosește i18n-ul de la build pentru propriul text. Limba interfeței e un semnal: serviciul LangStore (din core/i18n), cu metoda lang() și valorile 'en' sau 'ro'. Butonul din topbar apelează toggle(), care salvează alegerea în localStorage, sub cheia atlas-lang. La pornire se citește cheia; dacă lipsește, limba e engleza. Un effect ține sincronizat atributul lang al elementului <html>.

Schimbarea nu reîncarcă pagina: textul se reevaluează când se schimbă semnalul. Există două feluri de text bilingv:

  • Interfața (topbar, meniuri, căutare) are chei în messages.ts, tipate cu MessageKey. Pipe-ul t le traduce: {{ 'topic.minRead' | t: { n: 4 } }}. TypeScript cere ca fiecare cheie să existe în ambele limbi.
  • Textele acestui site stau în dicționare, câte un fișier JSON pe limbă (i18n/en/…, i18n/ro/…, i18n/de/…). Fiecare text are o cheie și se citește cu pipe-ul t: {{ 'signals:title' | t }}. Pipe-ul este impur, deci se reevaluează la schimbarea limbii.

Textul paginii e scris în template în ambele limbi (@if (lang.lang() === 'en')), iar codul e în două fișiere .snippets. Demo-ul $localize de mai sus are alt mecanism și propriile butoane: nu urmează limba site-ului.

Când DA, când NU

i18n-ul integrat (build per limbă)
  • Aplicații publice, SEO, performanță: fiecare limbă e un build optimizat, fără chei la runtime.
  • Limbi cunoscute la build și schimbate rar (preferința din cont, URL-ul /en/).
  • Echipe cu traducători profesioniști care lucrează în XLIFF.
  • Merge bine cu SSR / prerender: câte o variantă per limbă (vezi SSR).
Bibliotecă de traducere la runtime
  • Schimbarea limbii fără reîncărcare e o cerință de produs.
  • Traducerile vin dintr-un CMS și se schimbă fără deploy.
  • Zeci de limbi și nu vrei zeci de build-uri.
  • Aplicații interne mici, într-o singură limbă: doar LOCALE_ID + pipe-uri + ICU, fără extragere.

Capcane frecvente

  • Concatenarea de texte ('Ai ' + n + ' produse'): ordinea cuvintelor diferă între limbi. Folosește un singur mesaj cu placeholder sau un ICU.
  • Pluraluri cu n === 1 ? … : …: în română dai „20 fișiere” în loc de „20 de fișiere”. Folosește ICU plural.
  • Mesaje fără id custom: id-ul generat se schimbă la orice corectură de text, iar traducerea se pierde.
  • Formate hardcodate ('dd/MM/yyyy', toFixed(2) + ' lei'): ocolesc locale-ul. Folosește pipe-urile.
  • Locale neînregistrat: LOCALE_ID setat la ro fără registerLocaleData aruncă eroare la primul pipe.

Fișă rapidă

MARCARE      i18n="sens|descriere@@id" · i18n-title="@@id" · $localize`:@@id:text ${x}:nume:`
ICU          {n, plural, =0 {…} one {…} few {…} other {…}} · {x, select, a {…} other {…}}
             română: one = 1 · few = 0, 2–19, 101–119 · other = 20+, 120+ („20 de fișiere”)
EXTRAGERE    ng extract-i18n [--format=xlf|xlf2|xmb|json|arb] [--output-path src/locale]
BUILD        angular.json: i18n.sourceLocale + i18n.locales · "localize": true → un bundle per limbă
RUNTIME      loadTranslations() înainte de bootstrap · clearTranslations() · limbă nouă = reload
FORMATARE    registerLocaleData + LOCALE_ID + DEFAULT_CURRENCY_CODE · pipe-uri cu parametrul locale
ALTERNATIVE  biblioteci de traducere la runtime (ex. Transloco, ngx-translate) — schimbare de limbă fără reload

Cap. 17 · 0 din 4 terminate

Ai terminat lecția?

Marcheaz-o și apare pe harta ta: în cuprins și pe prima pagină.

Se marchează singură după ce citești toată lecția.

  1. Securitate: XSS, sanitizare & CSP
  2. Autentificare: token-uri & refresh
  3. OAuth 2.1 și OpenID Connect cu PKCE
  4. Internaționalizare (i18n)
Vezi toată harta ta

Toate cele 75 de pagini · scrie ca să filtrezi

  • 01Ce e nou în Angular 21 & 22Toate schimbările importante din v21 și v22, pe scurt, cu link către pagina care le explică.Start
  • 01Prima ta aplicație, pas cu pasO listă de lecturi construită de la o componentă goală la o aplicație terminată — signals, @for, input-uri și output-uri, un serviciu și un formular, pas cu pas.Start
  • 01Decizii rapide„Unde pun asta?”, „Signal sau Observable?”, „Shared sau feature?” — răspunsuri scurte la întrebările de zi cu zi.Start
  • 01Întrebări de interviu Angular60 de întrebări reale de interviu pentru junior, mid și senior, cu răspunsul de dat, întrebarea care urmează și greșelile de evitat. Plus o simulare de interviu.Start
  • 02Standalone & bootstrapCum pornește o aplicație fără NgModule: bootstrapApplication, app.config.ts, imports în componentă.Fundamente moderne
  • 02Data binding și evenimenteInterpolare, binding pe proprietăți și pe atribute, evenimente cu $event și filtre de taste și cum e construit [( )] — ce intră în DOM și ce se întoarce.Fundamente moderne
  • 02Control flow: @if, @for, @switchSintaxa de blocuri care înlocuiește *ngIf/*ngFor/ngSwitch, cu track, @empty și verificare exhaustivă.Fundamente moderne
  • 02@let și expresiile noi din templateVariabile locale cu @let, arrow functions, spread, regex, instanceof, comentarii în tag-uri.Fundamente moderne
  • 02@defer — lazy loading în templateÎncarci componente grele doar când e nevoie: triggere, prefetch, placeholder, loading, error.Fundamente moderne
  • 02Class & style bindingsDe ce [class.x] și [style.x] au înlocuit ngClass și ngStyle (cu migrare automată din v21).Fundamente moderne
  • 02Stilurile componentelor și încapsularea:host, cele trei moduri de încapsulare, teme cu variabile CSS și de ce ::ng-deep e ultima soluție.Fundamente moderne
  • 02Pipes: când și cumPipe pur vs impur, pipe custom și cum alegi între pipe, computed() și o metodă.Fundamente moderne
  • 03signal() și computed()Primitivele reactive: stare scriibilă, stare derivată, egalitate, untracked și reguli de imutabilitate.Signals & reactivitate
  • 03effect() — când DA, când NUEfecte secundare sincronizate cu semnale: localStorage, logging, API-uri imperative. Și capcanele lor.Signals & reactivitate
  • 03linkedSignal() — stare derivată editabilăUn semnal care se resetează când se schimbă sursa, dar pe care îl poți și seta manual.Signals & reactivitate
  • 03resource, rxResource, httpResourceDate asincrone ca semnale: status, value, error, reload, anulare automată. Stabile din v22.Signals & reactivitate
  • 03debounced() — semnale cu întârziereVarianta „debounce” a unui semnal, fără RxJS. Perfect pentru căutare.Signals & reactivitate
  • 03Liste: căutare, filtrare, sortare, paginareUn lanț de computed-uri care recalculează doar ce s-a schimbat, cu filtrele ținute în URL, ca un link să redeschidă aceeași vedere.Signals & reactivitate
  • 03Observables de la zeroCe e un Observable, subscribe și unsubscribe, operatorii pe care îi folosești în fiecare săptămână, cold vs hot — și când ajunge un signal.Signals & reactivitate
  • 03Signals ↔ RxJStoSignal, toObservable, takeUntilDestroyed, outputFromObservable — și când RxJS rămâne alegerea bună.Signals & reactivitate
  • 04input() și output()Inputuri ca semnale (required, transform, alias) și outputuri tipizate. Data down, events up.Componente: comunicare
  • 04model() — two-way bindingBanana-in-a-box [( )] cu un singur API. Când îl folosești și când e mai bine input + output.Componente: comunicare
  • 04viewChild & contentChildInterogări ca semnale: viewChild, viewChildren, contentChild, contentChildren, opțiunea read.Componente: comunicare
  • 04Lifecycle & DestroyRefCe hook-uri mai contează în era signals, afterNextRender, DestroyRef și ngOnChanges tipizat.Componente: comunicare
  • 04Content projectionng-content cu select, sloturi multiple, conținut implicit și capcana proiecției condiționale.Componente: comunicare
  • 04ng-template & ngTemplateOutletTemplate-uri ca valori: render props în Angular, context tipizat și personalizare de componente.Componente: comunicare
  • 05Smart vs DumbContainer vs presentational: cine ține starea, cine doar afișează, și cum le împarți corect.Design de componente
  • 05Componente generice <T>Un DataTable<T> sau Select<T> tipizat corect, cu template-uri pentru celule.Design de componente
  • 05Componente dinamiceNgComponentOutlet, createComponent cu inputBinding/outputBinding și un dashboard construit din config.Design de componente
  • 05Directive & host compositionDirective atribut, metadata host: {} și Directive Composition API (hostDirectives).Design de componente
  • 05Controale custom de formularFormValueControl (Signal Forms) vs ControlValueAccessor: un rating cu stele, în ambele variante.Design de componente
  • 05Change detectionOnPush e implicit din v22, zone.js lipsește din v21. Ce declanșează randarea și cum să nu te arzi.Design de componente
  • 06inject() & @Service()Noul decorator @Service() din v22, inject() vs constructor și contextul de injecție.Dependency Injection
  • 06Providers & ierarhia de injectoriRoot vs rută vs componentă, useClass/useValue/useFactory/useExisting și modificatorii optional/self/skipSelf.Dependency Injection
  • 06InjectionToken & provideX()Configurare tipizată, funcții provideFeature(), makeEnvironmentProviders și inițializatori.Dependency Injection
  • 06injectAsync() — servicii lazyÎncarci un serviciu greu abia când ai nevoie de el, cu prefetch la idle.Dependency Injection
  • 07Structura de folderecore / shared / features, ce intră în fiecare și cum arată un feature pe dinăuntru.Arhitectura aplicației
  • 07Shared vs featureCând muți o componentă în shared și (mai ales) când NU. Checklist concret.Arhitectura aplicației
  • 07Interfețe & modeleInterfață inline sau fișier separat? DTO vs model de domeniu, type vs interface, unde le pui.Arhitectura aplicației
  • 07State management cu signalsStore-uri simple pe semnale, facade, stare locală vs globală și când îți trebuie NgRx.Arhitectura aplicației
  • 07Style guide & namingConvențiile oficiale actuale: nume de fișiere fără sufixe, protected, readonly, ordinea membrilor.Arhitectura aplicației
  • 07Barrels, aliasuri & granițeindex.ts, path aliases, API public pe feature și reguli de dependență care se pot verifica automat.Arhitectura aplicației
  • 07Erori: @boundary & ErrorHandlerNoul bloc @boundary din v22.2, ErrorHandler global, erori HTTP și erori din resource.Arhitectura aplicației
  • 08Routing modern & lazy loadingloadComponent, rute copil, parametri ca input-uri și moștenirea parametrilor (default nou în v22).Routing
  • 08Guards & resolverscanActivate, canMatch, canDeactivate și resolve ca funcții, cu redirect prin UrlTree / RedirectCommand.Routing
  • 09Signal Forms — bazeleFormulare construite pe semnale: form(), [formField], validare prin schemă. Stabile din v22.Formulare
  • 09Signal Forms — avansatValidare custom și cross-field, async/HTTP cu debounce, liste dinamice, disabled/hidden, scheme reutilizabile.Formulare
  • 09Reactive Forms tipizateFormGroup tipizat, nonNullable, FormArray — și cum conviețuiesc cu Signal Forms.Formulare
  • 10HttpClient, interceptors & API layerHttpClient fără setup (v21), Fetch implicit (v22), interceptori funcționali și stratul de API cu mapare DTO.HTTP & date
  • 11Performanță — checklistCe chiar contează: OnPush + signals, track, @defer, lazy routes, imagini, bundle și hidratare.Calitate & performanță
  • 11Testare cu VitestVitest e runner-ul implicit din v21. TestBed, componente cu semnale, servicii, HTTP și harness-uri.Calitate & performanță
  • 11DevTools: cum te uiți în aplicațieChrome DevTools și Angular DevTools pas cu pas: ce arată fiecare panou, cu exerciții care merg chiar pe site-ul ăsta.Calitate & performanță
  • 11Angular AriaComponente headless, accesibile, pe care le stilizezi cum vrei: tabs, accordion, menu, listbox...Calitate & performanță
  • 12SSR, prerender & render modesCând randezi pe server, când la build și când doar în browser: RenderMode pe rută, app.routes.server.ts și costurile reale.SSR & hidratare
  • 12Hidratare: event replay & incrementalCum preia browserul HTML-ul de pe server fără să-l refacă: hidratare completă, event replay, @defer (hydrate on …) și erorile de nepotrivire.SSR & hidratare
  • 12Cod compatibil cu serverulwindow nu există pe server: PLATFORM_ID, afterNextRender, DOCUMENT, TransferState, cache-ul HTTP și PendingTasks.SSR & hidratare
  • 13Micro frontendsCând merită să spargi o aplicație în mai multe deploy-uri și când e doar complexitate: Native Federation, Module Federation, iframes, Web Components.Arhitectură la scară
  • 13Web Components cu Angular ElementscreateCustomElement: o componentă Angular folosită ca tag HTML oriunde, plus comunicarea între aplicații prin evenimente și BroadcastChannel.Arhitectură la scară
  • 13Monorepo, librării & granițeUn workspace cu mai multe aplicații și librării: ng generate library, Nx, entry points secundare, publicare și reguli de dependență.Arhitectură la scară
  • 13Arhitectură hexagonală (ports & adapters)Domeniul fără Angular, porturi ca clase abstracte și adaptoare interschimbabile: HTTP, localStorage sau in-memory, fără să atingi UI-ul.Arhitectură la scară
  • 14Semnalele pe dinăuntruGraful reactiv: producători și consumatori, push-pull, de ce computed e lazy și glitch-free, problema diamantului și când rulează efectele.Reactivitate avansată
  • 14RxJS avansat: operatori de ordin superiorswitchMap, mergeMap, concatMap și exhaustMap pe o diagramă live, plus retry cu backoff, shareReplay și cum eviți scurgerile.Reactivitate avansată
  • 14NgRx SignalStoreState management la scară pe semnale: withState, withComputed, withMethods, rxMethod, entități și hooks — și când un store simplu e suficient.Reactivitate avansată
  • 14Mașini de stări cu signalsFără stări imposibile: o mașină tipizată pe signals, cu tranziții, guards și diagrama stărilor, live.Reactivitate avansată
  • 15WebSockets: timp real care rezistă rețeleiUn socket tipizat ca signals: reconectare cu backoff și jitter, heartbeat, coadă de mesaje cât ești offline și siguranță la SSR.Timp real și offline
  • 15Actualizări optimiste și rollbackInterfață instantanee, dar onestă: snapshot și rollback, răspunsuri sosite în altă ordine, chei de idempotență.Timp real și offline
  • 15Offline-first: Service Worker și cacheService Worker-ul Angular: strategii de cache, anunțarea versiunilor noi și o coadă pentru modificările făcute offline.Timp real și offline
  • 16Web WorkersCalcule grele fără să blochezi interfața: un worker real în pagină, comunicare prin mesaje și când nu merită.UI & performanță avansată
  • 16Liste uriașe: virtual scroll100.000 de rânduri fără lag: CDK virtual scroll, itemSize, trackBy și alternativele (paginare, infinite scroll).UI & performanță avansată
  • 16Animații moderne & View Transitionsanimate.enter și animate.leave în locul @angular/animations (depreciat), tranziții CSS și View Transitions la navigare.UI & performanță avansată
  • 16Memory leaks: cum le găsești și le repariSubscripții, timere și listeneri care trăiesc mai mult decât componenta lor — cum îi vezi în DevTools și cum îi închizi.UI & performanță avansată
  • 17Securitate: XSS, sanitizare & CSPCe face Angular automat împotriva XSS, când devine periculos bypassSecurityTrust, plus CSP cu nonce, Trusted Types și XSRF.Securitate & globalizare
  • 17Autentificare: token-uri & refreshUn interceptor care atașează token-ul, reîmprospătează o singură dată la 401 și reia cererile din coadă — plus unde ții token-ul.Securitate & globalizare
  • 17OAuth 2.1 și OpenID Connect cu PKCEFluxul authorization code cu PKCE, pas cu pas: verifier și challenge, state și nonce, ID token-ul și de ce un BFF e mai sigur.Securitate & globalizare
  • 17Internaționalizare (i18n)$localize, atributul i18n, ICU plural/select, build per limbă vs. traduceri la runtime și formatarea după locale.Securitate & globalizare