AngularAtlasAusg. 22.2

Kap. 11Qualität & Performance51 / 75

Testen mit Vitest

Vitest ist ab v21 der Standard-Runner. TestBed, Komponenten mit Signals, Services, HTTP und Harnesses.

v21v22 ~0 Min. Lesezeit Offizielle Doku

Seit v21 nutzt die Angular CLI Vitest als Standard-Runner für Unit-Tests (Karma ist deprecated). TestBed bleibt die wichtigste API, aber moderne Tests sehen anders aus: Inputs werden als Signals über bindings gesetzt, await fixture.whenStable() ersetzt fakeAsync (die Anwendungen sind zoneless), und Signal-Stores werden ohne DOM getestet.

Neues in 21 & 22 fürs Testen
  • v21: Vitest als Standard (@angular/build:unit-test), stabil.
  • v21: TestBed stellt standardmäßig eine Fake- PlatformLocation bereit.
  • v22: TestBed.getLastFixture(); ChangeDetectorRef.checkNoChanges entfernt (nutze fixture.detectChanges()).
  • v22.2: TestBed.createDirective() zum Testen von Direktiven.

Setup

# v21: Neue Projekte nutzen Vitest (Karma ist deprecated)
ng new my-app            # → "test": { "builder": "@angular/build:unit-test" } + vitest
ng test                  # führt die Tests aus (Watch-Modus in Dev)
ng test --no-watch --coverage

# Altes Projekt auf Karma/Jasmine → assistierte Migration
ng generate refactor-jasmine-vitest   # experimentelles Schematic aus @schematics/angular

Komponenten: Inputs als Signals

import { TestBed } from '@angular/core/testing';
import { inputBinding, signal } from '@angular/core';
import { StarRating } from './star-rating';

describe('StarRating', () => {
  it('zeigt die richtige Anzahl Sterne', async () => {
    const value = signal(3);
    const fixture = TestBed.createComponent(StarRating, {
      bindings: [inputBinding('value', value), inputBinding('max', () => 5)], // Inputs als Signals
    });
    await fixture.whenStable(); // zoneless: auf das Rendern warten

    const stars = fixture.nativeElement.querySelectorAll('button');
    expect(stars).toHaveLength(5);
    expect(fixture.nativeElement.querySelectorAll('button.on')).toHaveLength(3);

    value.set(5);                 // ändere den Input wie jedes Signal
    await fixture.whenStable();
    expect(fixture.nativeElement.querySelectorAll('button.on')).toHaveLength(5);
  });

  it('emittiert die Bewertung beim Klick', async () => {
    const onRate = vi.fn();
    const fixture = TestBed.createComponent(StarRating, {
      bindings: [inputBinding('value', () => 1), outputBinding('rate', onRate)],
    });
    await fixture.whenStable();

    fixture.nativeElement.querySelectorAll('button')[3].click();
    expect(onRate).toHaveBeenCalledWith(4);
  });
});

Stores

// Signal-Stores lassen sich am einfachsten testen: kein DOM, kein Async
describe('TodoStore', () => {
  let store: TodoStore;

  beforeEach(() => {
    TestBed.configureTestingModule({ providers: [TodoStore] });
    store = TestBed.inject(TodoStore);
  });

  it('berechnet, wie viele übrig sind', () => {
    store.add('A');
    store.add('B');
    store.toggle(store.todos()[0].id);
    expect(store.remaining()).toBe(1);
  });

  it('filtert', () => {
    store.add('A');
    store.setFilter('done');
    expect(store.visible()).toEqual([]);
  });
});

// Effects laufen asynchron — erzwinge sie mit:
TestBed.tick();
LiveDie Tests laufen direkt hier: Mach den Code kaputt und sieh, welcher Test es merkt
Bug einbauen
cart-store.ts
@Service()
export class CartStore {
private readonly _lines = signal<Line[]>([]);
readonly lines = this._lines.asReadonly();
readonly count = computed(() => this._lines().reduce((n, l) => n + l.qty, 0));
readonly total = computed(() =>
this._lines().reduce((sum, l) => sum + l.price * l.qty, 0),
);
add(p: Product): void {
const exists = this._lines().some((l) => l.id === p.id);
this._lines.update((list) =>
exists
? list.map((l) => (l.id === p.id ? { ...l, qty: l.qty + 1 } : l))
: [...list, { ...p, qty: 1 }],
);
}
remove(id: string): void {
this._lines.update((list) => list.filter((l) => l.id !== id));
}
}
ng test · watch
  1. CartStore › startet leer
  2. CartStore › count() folgt jedem add()
  3. CartStore › dasselbe Produkt zweimal ist eine Zeile mit qty 2
  4. CartStore › total() ist Preis × Menge
  5. CartStore › remove() entfernt nur dieses Produkt

Tests  5 passed (5)

Alle 5 Tests sind grün. Wähle jetzt einen Bug: Die Tests laufen sofort erneut, wie bei ng test im Watch-Modus, und du siehst, welcher ihn fängt.

Der Store ist echter Code mit Angular-Signals; die Assertions laufen in deinem Browser mit einem winzigen Runner, der dasselbe expect() wie Vitest hat. Jeden Bug fängt genau ein Test.

Dieselben Tests, geschrieben für ng test (Vitest + TestBed):

describe('CartStore', () => {
  const TEA = { id: 'tea', price: 3.5 };
  const MUG = { id: 'mug', price: 4 };
  let cart: CartStore;

  // Jeder Test bekommt ein frisches TestBed, also einen frischen CartStore
  beforeEach(() => (cart = TestBed.inject(CartStore)));

  it('startet leer', () => {
    expect(cart.count()).toBe(0);
    expect(cart.total()).toBe(0);
  });

  it('count() folgt jedem add()', () => {
    expect(cart.count()).toBe(0); // vor add() gelesen: genau das fängt das direkt veränderte Array
    cart.add(TEA);
    expect(cart.count()).toBe(1);
    cart.add(MUG);
    expect(cart.count()).toBe(2);
  });

  it('dasselbe Produkt zweimal ist eine Zeile mit qty 2', () => {
    cart.add(TEA);
    cart.add(TEA);
    expect(cart.lines()).toHaveLength(1);
    expect(cart.lines()[0].qty).toBe(2);
  });

  it('total() ist Preis × Menge', () => {
    cart.add(TEA);
    cart.add(TEA);
    cart.add(MUG);
    expect(cart.total()).toBe(11);
  });

  it('remove() entfernt nur dieses Produkt', () => {
    cart.add(TEA);
    cart.add(MUG);
    cart.remove('tea');
    expect(cart.lines().map((l) => l.id)).toEqual(['mug']);
  });
});

HTTP

import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';

describe('ProductsApi', () => {
  let api: ProductsApi;
  let http: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({ providers: [provideHttpClient(), provideHttpClientTesting()] });
    api = TestBed.inject(ProductsApi);
    http = TestBed.inject(HttpTestingController);
  });

  afterEach(() => http.verify()); // kein unerwarteter Request

  it('mappt DTO → Product', async () => {
    const result = firstValueFrom(api.get(1));
    http.expectOne('/api/products/1').flush({ id: 1, name: 'Mouse', price: 99, stock: 0 });
    expect(await result).toEqual({ id: 1, name: 'Mouse', price: 99, inStock: false });
  });
});

Direktiven und Fixtures (v22)

// v22.2: TestBed.createDirective — du testest eine Direktive ohne handgeschriebene Host-Komponente
it('wendet das Highlight beim Hover an', async () => {
  const fixture = TestBed.createDirective(Highlight, {
    tagName: 'span',
    bindings: [inputBinding('appHighlight', () => 'yellow')],
  });
  fixture.nativeElement.dispatchEvent(new MouseEvent('mouseenter'));
  fixture.detectChanges();
  expect(fixture.nativeElement.style.backgroundColor).toBe('yellow');
});

// v22: TestBed.getLastFixture() — die zuletzt erzeugte Fixture (nützlich in Helpern)
const fixture = TestBed.getLastFixture<MyComponent>();

Timer und Debounce (zoneless)

// Zoneless: kein fakeAsync/tick aus zone.js → Fake-Timer in Vitest
beforeEach(() => vi.useFakeTimers());
afterEach(() => vi.useRealTimers());

it('debounce', async () => {
  const fixture = TestBed.createComponent(Search);
  fixture.componentInstance.query.set('ang');
  await vi.advanceTimersByTimeAsync(300);
  await fixture.whenStable();
  // ...
});

Harnesses

// Component Harnesses (CDK): Tests, die nicht von der DOM-Struktur abhängen
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatButtonHarness } from '@angular/material/button/testing';

const loader = TestbedHarnessEnvironment.loader(fixture);
const save = await loader.getHarness(MatButtonHarness.with({ text: 'Speichern' }));
await save.click();

// Angular Aria hat ebenfalls Harnesses: @angular/aria/tabs/testing etc.

Was du testest, und wo

WasWieAufwand
Pure Funktionen, Mapper, Validatoreneinfache Unit-Tests, ohne TestBedminimal
Stores / Services mit SignalsTestBed.inject, ohne DOMgering
Dumb-KomponentencreateComponent + bindingsgering
Smart-Komponenten / Seitenein Fake-Store in providers + HttpTestingControllermittel
Komplette AbläufeE2E (Playwright / Cypress)hoch

Leicht testbare Stores beschreibt State Management mit Signals; einen Test, der ein Memory Leak vor der Produktion fängt, Memory Leaks finden und beheben.

Häufige Stolperfallen

  • Die Implementierung testen (dass eine private Methode aufgerufen wurde), statt das Verhalten (was der Nutzer sieht).
  • await fixture.whenStable() vergessen in zoneless → das DOM ist noch nicht aktualisiert.
  • fakeAsync ohne zone.js funktioniert nicht → Fake-Timer in Vitest.
  • Ungetestete Effects: Sie laufen asynchron — TestBed.tick() führt sie aus.

Kap. 11 · 0 von 4 abgeschlossen

Lektion abgeschlossen?

Markiere sie, dann erscheint sie auf deiner Karte: im Inhaltsverzeichnis und auf der Startseite.

Wird automatisch markiert, sobald du die ganze Lektion gelesen hast.

  1. Performance — eine Checkliste
  2. Testen mit Vitest
  3. DevTools: ein Blick in deine App
  4. Angular Aria
Deine ganze Karte ansehen

Alle 75 Seiten · tippen zum Filtern

  • 01Was ist neu in Angular 21 & 22Alle wichtigen Änderungen in v21 und v22 in Kürze, mit Link zur Seite, die sie jeweils erklärt.Start
  • 01Deine erste App, Schritt für SchrittEine Leseliste, von einer leeren Komponente bis zur fertigen App — Signals, @for, Inputs und Outputs, ein Service und ein Formular, Schritt für Schritt.Start
  • 01Schnelle Entscheidungen„Wohin damit?“, „Signal oder Observable?“, „Shared oder Feature?“ — kurze Antworten auf Alltagsfragen.Start
  • 01Angular-Interviewfragen60 echte Interviewfragen für Junior, Mid und Senior, mit der passenden Antwort, der Nachfrage und den Warnsignalen. Dazu ein Probeinterview.Start
  • 02Standalone & BootstrapWie eine App ohne NgModule startet: bootstrapApplication, app.config.ts, imports in der Komponente.Moderne Grundlagen
  • 02Data Binding & EventsInterpolation, Property- und Attribut-Bindings, Events mit $event und Tastenfiltern und wie [( )] aufgebaut ist — was in das DOM geht und was zurückkommt.Moderne Grundlagen
  • 02Control Flow: @if, @for, @switchDie Block-Syntax, die *ngIf/*ngFor/ngSwitch ersetzt, mit track, @empty und Exhaustive Checks.Moderne Grundlagen
  • 02@let und die neuen Template-AusdrückeLokale Variablen mit @let, Arrow Functions, Spread, Regex, instanceof, Kommentare in Tags.Moderne Grundlagen
  • 02@defer — Lazy Loading in TemplatesSchwere Komponenten nur laden, wenn sie gebraucht werden: Trigger, Prefetch, Placeholder, Loading, Error.Moderne Grundlagen
  • 02Class- & Style-BindingsWarum [class.x] und [style.x] ngClass und ngStyle abgelöst haben (mit automatischer Migration ab v21).Moderne Grundlagen
  • 02Komponenten-Styles & Kapselung:host, die drei Kapselungsmodi, Theming mit CSS-Variablen und warum ::ng-deep der letzte Ausweg ist.Moderne Grundlagen
  • 02Pipes: wann und wiePure vs. Impure Pipes, eigene Pipes und wie du zwischen Pipe, computed() und Methode wählst.Moderne Grundlagen
  • 03signal() und computed()Die reaktiven Grundbausteine: beschreibbarer Zustand, abgeleiteter Zustand, Gleichheit, untracked und Regeln zur Immutability.Signals & Reaktivität
  • 03effect() — wann ja, wann neinMit Signals synchronisierte Seiteneffekte: localStorage, Logging, imperative APIs. Und ihre Stolperfallen.Signals & Reaktivität
  • 03linkedSignal() — editierbarer abgeleiteter ZustandEin Signal, das sich zurücksetzt, wenn sich seine Quelle ändert, das du aber auch manuell setzen kannst.Signals & Reaktivität
  • 03resource, rxResource, httpResourceAsynchrone Daten als Signals: status, value, error, reload, automatischer Abbruch. Stabil seit v22.Signals & Reaktivität
  • 03debounced() — verzögerte SignalsDie „Debounce“-Variante eines Signals, ohne RxJS. Perfekt für die Suche.Signals & Reaktivität
  • 03Listen: suchen, filtern, sortieren, blätternEine Kette von computed-Signals, die nur neu berechnet, was sich geändert hat — mit den Filtern in der URL, damit ein Link dieselbe Ansicht öffnet.Signals & Reaktivität
  • 03Observables von null anWas ein Observable ist, subscribe und unsubscribe, die Operatoren, die du jede Woche brauchst, cold vs. hot — und wann ein Signal reicht.Signals & Reaktivität
  • 03Signals ↔ RxJStoSignal, toObservable, takeUntilDestroyed, outputFromObservable — und wann RxJS weiterhin die richtige Wahl bleibt.Signals & Reaktivität
  • 04input() und output()Inputs als Signals (required, transform, alias) und typisierte Outputs. Daten nach unten, Events nach oben.Komponenten: Kommunikation
  • 04model() — Two-Way-BindingBanana-in-a-Box [( )] mit einer einzigen API. Wann du es nutzt und wann input + output besser ist.Komponenten: Kommunikation
  • 04viewChild & contentChildQueries als Signals: viewChild, viewChildren, contentChild, contentChildren, die Option read.Komponenten: Kommunikation
  • 04Lifecycle & DestroyRefWelche Hooks in der Signals-Ära noch zählen, afterNextRender, DestroyRef und typisiertes ngOnChanges.Komponenten: Kommunikation
  • 04Content Projectionng-content mit select, mehrere Slots, Standardinhalt und die Falle der bedingten Projektion.Komponenten: Kommunikation
  • 04ng-template & ngTemplateOutletTemplates als Werte: Render Props in Angular, typisierter Kontext und Anpassen von Komponenten.Komponenten: Kommunikation
  • 05Smart vs. DumbContainer vs. Presentational: wer den Zustand hält, wer nur anzeigt und wie du die beiden sauber trennst.Komponenten-Design
  • 05Generische Komponenten <T>Eine korrekt typisierte DataTable<T> oder Select<T>, mit Templates für Zellen.Komponenten-Design
  • 05Dynamische KomponentenNgComponentOutlet, createComponent mit inputBinding/outputBinding und ein Dashboard, das aus einer Konfiguration gebaut wird.Komponenten-Design
  • 05Direktiven & Host-KompositionAttribut-Direktiven, die host: {}-Metadaten und die Directive Composition API (hostDirectives).Komponenten-Design
  • 05Eigene Formular-ControlsFormValueControl (Signal Forms) vs. ControlValueAccessor: eine Sternebewertung, in beiden Varianten.Komponenten-Design
  • 05Change DetectionOnPush ist ab v22 der Standard, zone.js entfällt ab v21. Was das Rendering auslöst und wie du dir nicht die Finger verbrennst.Komponenten-Design
  • 06inject() & @Service()Der neue @Service()-Decorator ab v22, inject() vs. Konstruktor und der Injection-Kontext.Dependency Injection
  • 06Provider & die Injector-HierarchieRoot vs. Route vs. Komponente, useClass/useValue/useFactory/useExisting und die Modifier optional/self/skipSelf.Dependency Injection
  • 06InjectionToken & provideX()Typisierte Konfiguration, provideFeature()-Funktionen, makeEnvironmentProviders und Initializer.Dependency Injection
  • 06injectAsync() — Lazy ServicesLade einen schweren Service erst, wenn du ihn brauchst, mit Prefetch im Leerlauf.Dependency Injection
  • 07Ordnerstrukturcore / shared / features, was in welchen Ordner gehört und wie ein Feature von innen aussieht.Architektur der Anwendung
  • 07Shared vs. FeatureWann du eine Komponente nach Shared verschiebst und (vor allem) wann NICHT. Eine konkrete Checkliste.Architektur der Anwendung
  • 07Interfaces & ModelleInline-Interface oder eigene Datei? DTO vs. Domänenmodell, type vs. interface, wohin damit.Architektur der Anwendung
  • 07State Management mit SignalsEinfache Signal-Stores, Fassaden, lokaler vs. globaler Zustand und wann du NgRx brauchst.Architektur der Anwendung
  • 07Styleguide & NamensgebungDie aktuellen offiziellen Konventionen: Dateinamen ohne Suffixe, protected, readonly, Reihenfolge der Member.Architektur der Anwendung
  • 07Barrels, Aliase & Grenzenindex.ts, Pfad-Aliase, die öffentliche API eines Features und Abhängigkeitsregeln, die sich automatisch prüfen lassen.Architektur der Anwendung
  • 07Fehler: @boundary & ErrorHandlerDer neue @boundary-Block ab v22.2, der globale ErrorHandler, HTTP-Fehler und Fehler aus resource.Architektur der Anwendung
  • 08Modernes Routing & Lazy LoadingloadComponent, Child Routes, Parameter als Inputs und Parameter-Vererbung (neuer Standard in v22).Routing
  • 08Guards & ResolvercanActivate, canMatch, canDeactivate und resolve als Funktionen, mit Redirects über UrlTree / RedirectCommand.Routing
  • 09Signal Forms — die GrundlagenFormulare auf Basis von Signals: form(), [formField], schemabasierte Validierung. Stabil seit v22.Formulare
  • 09Signal Forms — fortgeschrittenEigene und feldübergreifende Validierung, async/HTTP mit Debounce, dynamische Listen, disabled/hidden, wiederverwendbare Schemas.Formulare
  • 09Typisierte Reactive FormsTypisierte FormGroup, nonNullable, FormArray — und wie sie neben Signal Forms bestehen.Formulare
  • 10HttpClient, Interceptors & die API-SchichtHttpClient ohne Setup (v21), Fetch als Standard (v22), funktionale Interceptors und eine API-Schicht mit DTO-Mapping.HTTP & Daten
  • 11Performance — eine ChecklisteWas wirklich zählt: OnPush + Signals, track, @defer, Lazy Routes, Bilder, Bundles und Hydration.Qualität & Performance
  • 11Testen mit VitestVitest ist ab v21 der Standard-Runner. TestBed, Komponenten mit Signals, Services, HTTP und Harnesses.Qualität & Performance
  • 11DevTools: ein Blick in deine AppChrome DevTools und Angular DevTools Schritt für Schritt: was jedes Panel zeigt, mit Übungen, die auf dieser Website funktionieren.Qualität & Performance
  • 11Angular AriaHeadless, barrierefreie Komponenten, die du nach Belieben stylst: Tabs, Accordion, Menü, Listbox...Qualität & Performance
  • 12SSR, Prerendering & Render ModesWann auf dem Server gerendert wird, wann beim Build und wann nur im Browser: RenderMode pro Route, app.routes.server.ts und die echten Kosten.SSR & Hydration
  • 12Hydration: Event Replay & inkrementellWie der Browser das Server-HTML übernimmt, ohne es neu zu rendern: vollständige Hydration, Event Replay, @defer (hydrate on …) und Mismatch-Fehler.SSR & Hydration
  • 12Servertauglicher Codewindow gibt es auf dem Server nicht: PLATFORM_ID, afterNextRender, DOCUMENT, TransferState, der HTTP-Cache und PendingTasks.SSR & Hydration
  • 13Micro FrontendsWann es sich lohnt, eine App in mehrere Deployments aufzuteilen und wann es nur Komplexität ist: Native Federation, Module Federation, iframes, Web Components.Architektur im großen Maßstab
  • 13Web Components mit Angular ElementscreateCustomElement: eine Angular-Komponente, die überall als HTML-Tag nutzbar ist, plus Kommunikation zwischen Apps über Events und BroadcastChannel.Architektur im großen Maßstab
  • 13Monorepo, Bibliotheken & GrenzenEin Workspace mit mehreren Apps und Bibliotheken: ng generate library, Nx, Secondary Entry Points, Publishing und Abhängigkeitsregeln.Architektur im großen Maßstab
  • 13Hexagonale Architektur (Ports & Adapters)Eine Domäne ohne Angular, Ports als abstrakte Klassen und austauschbare Adapter: HTTP, localStorage oder In-Memory, ohne die UI anzufassen.Architektur im großen Maßstab
  • 14Signals unter der HaubeDer reaktive Graph: Producer und Consumer, Push-Pull, warum computed lazy und glitch-frei ist, das Diamond-Problem und wann Effects laufen.Fortgeschrittene Reaktivität
  • 14Fortgeschrittenes RxJS: Operatoren höherer OrdnungswitchMap, mergeMap, concatMap und exhaustMap an einem Live-Diagramm, dazu Retry mit Backoff, shareReplay und wie du Leaks vermeidest.Fortgeschrittene Reaktivität
  • 14NgRx SignalStoreState Management im großen Stil auf Signals: withState, withComputed, withMethods, rxMethod, Entities und Hooks — und wann ein einfacher Store reicht.Fortgeschrittene Reaktivität
  • 14Zustandsautomaten mit SignalsKeine unmöglichen Zustände mehr: ein typisierter Automat auf Signals, mit Übergängen, Guards und einem Live-Zustandsdiagramm.Fortgeschrittene Reaktivität
  • 15WebSockets: Echtzeit, die das Netz überstehtEin typisierter Socket als Signals: Reconnect mit Backoff und Jitter, Heartbeat, ein Postausgang im Offline-Modus und SSR-Sicherheit.Echtzeit & offline
  • 15Optimistische Updates & RollbackEine sofortige UI, die ehrlich bleibt: Snapshot und Rollback, Antworten in falscher Reihenfolge, Idempotenzschlüssel.Echtzeit & offline
  • 15Offline-first: Service Worker & CachingDer Angular Service Worker: Caching-Strategien, Update-Hinweise und ein Postausgang für Änderungen, die offline entstehen.Echtzeit & offline
  • 16Web WorkersSchwere Berechnungen, ohne die UI zu blockieren: ein echter Worker in der Seite, Message Passing und wann es sich nicht lohnt.Fortgeschrittene UI & Performance
  • 16Riesige Listen: Virtual Scroll100.000 Zeilen ohne Ruckeln: CDK Virtual Scroll, itemSize, trackBy und die Alternativen (Paginierung, Infinite Scroll).Fortgeschrittene UI & Performance
  • 16Moderne Animationen & View Transitionsanimate.enter und animate.leave statt des veralteten @angular/animations, CSS-Transitions und View Transitions bei der Navigation.Fortgeschrittene UI & Performance
  • 16Memory Leaks finden und behebenSubscriptions, Timer und Listener, die ihre Komponente überleben – wie du sie in den DevTools findest und schließt.Fortgeschrittene UI & Performance
  • 17Sicherheit: XSS, Sanitizing & CSPWas Angular automatisch gegen XSS tut, wann bypassSecurityTrust gefährlich wird, dazu CSP mit Nonce, Trusted Types und XSRF.Sicherheit & Globalisierung
  • 17Authentifizierung: Tokens & RefreshEin Interceptor, der das Token anhängt, bei 401 einmal erneuert und die wartenden Requests wiederholt — dazu, wo du das Token aufbewahrst.Sicherheit & Globalisierung
  • 17OAuth 2.1 & OpenID Connect mit PKCEDer Authorization-Code-Flow mit PKCE Schritt für Schritt: Verifier und Challenge, State und Nonce, das ID-Token und warum ein BFF sicherer ist.Sicherheit & Globalisierung
  • 17Internationalisierung (i18n)$localize, das i18n-Attribut, ICU plural/select, ein Build pro Sprache vs. Übersetzungen zur Laufzeit und Formatierung nach Locale.Sicherheit & Globalisierung