From 474c04041020645ebc0b79836ca8ac719a35bb17 Mon Sep 17 00:00:00 2001 From: Edwin van den Houdt Date: Sat, 27 Jun 2026 14:00:10 +0200 Subject: [PATCH] Step 2 (i18n): $localize sweep + JA_NEE dedup (M3, M4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap every user-facing Dutch string in Angular's first-party i18n — `i18n`/ `i18n-` in templates, `$localize` in TS (value-objects, machines, commands, label constants, shared-component defaults). Source locale stays nl; a second locale is now a translation file, not a code change. - M3: ~145 strings localized with stable @@ ids across registratie, herregistratie, auth, shared/ui, shared/layout. Skipped: showcase, debug-state, scenario interceptor, generated client, specs/stories, raw status enum tags, internal parse* diagnostics. - M4: single shared JA_NEE (localized labels) in radio-group; both wizard copies removed. Gate green: lint, check:tokens, build, test 77/77. Co-Authored-By: Claude Opus 4.8 --- src/app/auth/infrastructure/digid.adapter.ts | 2 +- .../ui/login-form/login-form.component.ts | 6 +- src/app/auth/ui/login.page.ts | 4 +- .../herregistratie/domain/intake.machine.ts | 6 +- .../herregistratie-wizard.component.ts | 22 +++--- .../herregistratie/ui/herregistratie.page.ts | 6 +- .../intake-wizard/intake-wizard.component.ts | 53 +++++++-------- src/app/herregistratie/ui/intake.page.ts | 4 +- .../domain/change-request.machine.ts | 2 +- .../domain/registratie-wizard.machine.ts | 10 +-- src/app/registratie/domain/tasks.ts | 16 ++--- .../domain/value-objects/big-nummer.ts | 2 +- .../registratie/domain/value-objects/email.ts | 2 +- .../domain/value-objects/postcode.ts | 2 +- .../registratie/domain/value-objects/uren.ts | 2 +- .../address-fields.component.ts | 10 +-- .../change-request-form.component.ts | 13 ++-- src/app/registratie/ui/dashboard.page.ts | 38 +++++------ .../registratie-wizard.component.ts | 68 ++++++++++--------- src/app/registratie/ui/registratie.page.ts | 3 +- .../ui/registration-detail.page.ts | 2 +- .../registration-summary.component.ts | 20 +++--- .../registration-table.component.ts | 6 +- src/app/shared/application/submit.ts | 6 +- .../layout/breadcrumb/breadcrumb-trail.ts | 12 ++-- .../layout/breadcrumb/breadcrumb.component.ts | 2 +- .../layout/page-shell/page-shell.component.ts | 2 +- .../shared/layout/shell/shell.component.ts | 2 +- .../layout/side-nav/side-nav.component.ts | 2 +- .../site-footer/site-footer.component.ts | 16 ++--- .../site-header/site-header.component.ts | 8 +-- .../wizard-shell/wizard-shell.component.ts | 10 +-- .../ui/form-field/form-field.component.ts | 2 +- .../ui/radio-group/radio-group.component.ts | 7 ++ .../shared/ui/spinner/spinner.component.ts | 2 +- .../shared/ui/stepper/stepper.component.ts | 4 +- 36 files changed, 193 insertions(+), 181 deletions(-) diff --git a/src/app/auth/infrastructure/digid.adapter.ts b/src/app/auth/infrastructure/digid.adapter.ts index fb30287..6be75a6 100644 --- a/src/app/auth/infrastructure/digid.adapter.ts +++ b/src/app/auth/infrastructure/digid.adapter.ts @@ -9,7 +9,7 @@ export class DigidAdapter { // Swap for a real OIDC redirect flow when there's a backend. async authenticate(bsn: string): Promise> { const t = bsn.trim(); - if (!/^\d{9}$/.test(t)) return err('Voer een geldig BSN van 9 cijfers in.'); + if (!/^\d{9}$/.test(t)) return err($localize`:@@validation.bsn:Voer een geldig BSN van 9 cijfers in.`); return ok({ bsn: t, naam: 'Dr. A. (Anna) de Vries' }); } } diff --git a/src/app/auth/ui/login-form/login-form.component.ts b/src/app/auth/ui/login-form/login-form.component.ts index 7314933..1b39667 100644 --- a/src/app/auth/ui/login-form/login-form.component.ts +++ b/src/app/auth/ui/login-form/login-form.component.ts @@ -10,15 +10,15 @@ import { ButtonComponent } from '@shared/ui/button/button.component'; imports: [FormsModule, FormFieldComponent, TextInputComponent, ButtonComponent], template: `
- + - + - Inloggen met DigiD + Inloggen met DigiD `, }) diff --git a/src/app/auth/ui/login.page.ts b/src/app/auth/ui/login.page.ts index 7751b1d..1bb7b4b 100644 --- a/src/app/auth/ui/login.page.ts +++ b/src/app/auth/ui/login.page.ts @@ -9,8 +9,8 @@ import { SessionStore } from '@auth/application/session.store'; selector: 'app-login-page', imports: [PageShellComponent, AlertComponent, LoginFormComponent], template: ` - + @if (error()) { {{ error() }} } diff --git a/src/app/herregistratie/domain/intake.machine.ts b/src/app/herregistratie/domain/intake.machine.ts index 016d2e6..3131949 100644 --- a/src/app/herregistratie/domain/intake.machine.ts +++ b/src/app/herregistratie/domain/intake.machine.ts @@ -73,9 +73,9 @@ function validateStep(step: StepId, a: Answers, scholingThreshold: number): Resu const errors: Errors = {}; switch (step) { case 'buitenland': - if (!a.buitenlandGewerkt) errors.buitenlandGewerkt = 'Maak een keuze.'; + if (!a.buitenlandGewerkt) errors.buitenlandGewerkt = $localize`:@@validation.maakKeuze:Maak een keuze.`; else if (a.buitenlandGewerkt === 'ja') { - if (!a.land || a.land.trim() === '') errors.land = 'Vul een land in.'; + if (!a.land || a.land.trim() === '') errors.land = $localize`:@@validation.land:Vul een land in.`; const u = parseUren(a.buitenlandseUren ?? ''); if (!u.ok) errors.buitenlandseUren = u.error; } @@ -83,7 +83,7 @@ function validateStep(step: StepId, a: Answers, scholingThreshold: number): Resu case 'werk': { const u = parseUren(a.uren ?? ''); if (!u.ok) errors.uren = u.error; - if (lageUren(a, scholingThreshold) && !a.scholingGevolgd) errors.scholingGevolgd = 'Maak een keuze.'; + if (lageUren(a, scholingThreshold) && !a.scholingGevolgd) errors.scholingGevolgd = $localize`:@@validation.maakKeuze:Maak een keuze.`; // Nascholingspunten are only asked (and required) when scholing was followed. if (a.scholingGevolgd === 'ja') { const p = parseUren(a.punten ?? ''); diff --git a/src/app/herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component.ts b/src/app/herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component.ts index acfdd7c..73fcfa5 100644 --- a/src/app/herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component.ts +++ b/src/app/herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component.ts @@ -36,23 +36,23 @@ import { ApiClient } from '@shared/infrastructure/api-client'; (retry)="onRetry()"> @if (step() === 1) { - + + name="uren" [invalid]="!!errUren()" i18n-placeholder="@@herregWizard.urenPlaceholder" placeholder="bijv. 4160" /> - + + name="jaren" [invalid]="!!errJaren()" i18n-placeholder="@@herregWizard.jarenPlaceholder" placeholder="bijv. 5" /> } @else { - + + name="punten" [invalid]="!!errPunten()" i18n-placeholder="@@herregWizard.puntenPlaceholder" placeholder="bijv. 200" /> }
- Uw aanvraag tot herregistratie is ontvangen. + Uw aanvraag tot herregistratie is ontvangen.
`, @@ -69,8 +69,8 @@ export class HerregistratieWizardComponent { protected dispatch = this.store.dispatch; // Stepper labels + per-step heading titles (presentational only). - readonly stepLabels = ['Werkervaring', 'Nascholing']; - private stepTitles = ['Werkervaring (afgelopen 5 jaar)', 'Nascholing']; + readonly stepLabels = [$localize`:@@herregWizard.step.werkervaring:Werkervaring`, $localize`:@@herregWizard.step.nascholing:Nascholing`]; + private stepTitles = [$localize`:@@herregWizard.title.werkervaring:Werkervaring (afgelopen 5 jaar)`, $localize`:@@herregWizard.title.nascholing:Nascholing`]; private editing = computed(() => whenTag(this.state(), 'Editing')); protected step = computed(() => this.editing()?.step ?? 1); @@ -82,8 +82,8 @@ export class HerregistratieWizardComponent { // --- Presentational wiring for the shared wizard shell --------------------- protected stepTitle = computed(() => this.stepTitles[this.step() - 1]); - protected primaryLabel = computed(() => (this.step() === 1 ? 'Volgende' : 'Herregistratie aanvragen')); - protected errorMessage = computed(() => `Indienen mislukt: ${this.failedError()}`); + protected primaryLabel = computed(() => (this.step() === 1 ? $localize`:@@wizard.volgende:Volgende` : $localize`:@@herregWizard.indienen:Herregistratie aanvragen`)); + protected errorMessage = computed(() => $localize`:@@wizard.indienenMislukt:Indienen mislukt:` + ` ${this.failedError()}`); protected shellStatus = computed(() => { switch (this.state().tag) { case 'Editing': return 'editing'; diff --git a/src/app/herregistratie/ui/herregistratie.page.ts b/src/app/herregistratie/ui/herregistratie.page.ts index 65985a3..f943784 100644 --- a/src/app/herregistratie/ui/herregistratie.page.ts +++ b/src/app/herregistratie/ui/herregistratie.page.ts @@ -13,18 +13,18 @@ import { HerregistratieWizardComponent } from '@herregistratie/ui/herregistratie selector: 'app-herregistratie-page', imports: [PageShellComponent, AlertComponent, ...ASYNC, HerregistratieWizardComponent], template: ` - + @if (eligible) { - + Uw huidige registratie verloopt binnenkort. Vraag tijdig herregistratie aan.
} @else { - + Voor uw huidige registratiestatus is herregistratie niet mogelijk. } diff --git a/src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts b/src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts index e1dc727..6eb8086 100644 --- a/src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts +++ b/src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts @@ -2,7 +2,7 @@ import { Component, computed, effect, inject, input, untracked } from '@angular/ import { FormsModule } from '@angular/forms'; import { FormFieldComponent } from '@shared/ui/form-field/form-field.component'; import { TextInputComponent } from '@shared/ui/text-input/text-input.component'; -import { RadioGroupComponent } from '@shared/ui/radio-group/radio-group.component'; +import { RadioGroupComponent, JA_NEE } from '@shared/ui/radio-group/radio-group.component'; import { ButtonComponent } from '@shared/ui/button/button.component'; import { AlertComponent } from '@shared/ui/alert/alert.component'; import { DataRowComponent } from '@shared/ui/data-row/data-row.component'; @@ -26,7 +26,6 @@ import { ApiClient } from '@shared/infrastructure/api-client'; import { IntakePolicyAdapter } from '@herregistratie/infrastructure/intake-policy.adapter'; const STORAGE_KEY = 'intake-v3'; // ponytail: bump the suffix if the persisted state shape changes; no migration. -const JA_NEE = [{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }]; /** Organism: a BRANCHING intake questionnaire. All state lives in one signal driven by the pure `reduce` (intake.machine.ts). Which step renders is derived @@ -53,58 +52,58 @@ const JA_NEE = [{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }]; @switch (step()) { @case ('buitenland') { - + @if (answers().buitenlandGewerkt === 'ja') { - - + + - - + + } } @case ('werk') { - - + + @if (scholingZichtbaar()) { - + } @if (answers().scholingGevolgd === 'ja') { - - + + } } @case ('review') { - Controleer uw antwoorden en dien de aanvraag in. + Controleer uw antwoorden en dien de aanvraag in.
- + @if (answers().buitenlandGewerkt === 'ja') { - - + + } - + @if (scholingZichtbaar()) { - + } @if (answers().scholingGevolgd === 'ja') { - + }
} }
- Uw aanvraag tot herregistratie is ontvangen. + Uw aanvraag tot herregistratie is ontvangen.
- Opnieuw beginnen + Opnieuw beginnen
@@ -140,15 +139,15 @@ export class IntakeWizardComponent { protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? ''); // --- Presentational wiring for the shared wizard shell --------------------- - readonly stepLabels = ['Buitenland', 'Werk', 'Controle']; + readonly stepLabels = [$localize`:@@intake.step.buitenland:Buitenland`, $localize`:@@intake.step.werk:Werk`, $localize`:@@intake.step.controle:Controle`]; private stepTitles: Record = { - buitenland: 'Werken in het buitenland', - werk: 'Werkervaring in Nederland', - review: 'Controleren en indienen', + buitenland: $localize`:@@intake.title.buitenland:Werken in het buitenland`, + werk: $localize`:@@intake.title.werk:Werkervaring in Nederland`, + review: $localize`:@@intake.title.review:Controleren en indienen`, }; protected stepTitle = computed(() => this.stepTitles[this.step()]); - protected primaryLabel = computed(() => (this.step() === 'review' ? 'Aanvraag indienen' : 'Volgende')); - protected errorMessage = computed(() => `Indienen mislukt: ${this.failedError()}`); + protected primaryLabel = computed(() => (this.step() === 'review' ? $localize`:@@intake.indienen:Aanvraag indienen` : $localize`:@@wizard.volgende:Volgende`)); + protected errorMessage = computed(() => $localize`:@@wizard.indienenMislukt:Indienen mislukt:` + ` ${this.failedError()}`); protected shellStatus = computed(() => { switch (this.state().tag) { case 'Answering': return 'editing'; diff --git a/src/app/herregistratie/ui/intake.page.ts b/src/app/herregistratie/ui/intake.page.ts index dbd5a7f..6349c8b 100644 --- a/src/app/herregistratie/ui/intake.page.ts +++ b/src/app/herregistratie/ui/intake.page.ts @@ -9,8 +9,8 @@ import { IntakeWizardComponent } from '@herregistratie/ui/intake-wizard/intake-w selector: 'app-intake-page', imports: [PageShellComponent, AlertComponent, IntakeWizardComponent], template: ` - - + + Een paar vragen bepalen welke gegevens we nodig hebben. Afhankelijk van uw antwoorden verschijnen er extra vragen. Uw antwoorden blijven bewaard als u de pagina herlaadt. diff --git a/src/app/registratie/domain/change-request.machine.ts b/src/app/registratie/domain/change-request.machine.ts index dffd646..64fb85c 100644 --- a/src/app/registratie/domain/change-request.machine.ts +++ b/src/app/registratie/domain/change-request.machine.ts @@ -40,7 +40,7 @@ function validate(draft: Draft): Result { const straat = draft.straat.trim(); const postcode = parsePostcode(draft.postcode); const errors: Errors = {}; - if (!straat) errors.straat = 'Vul straat en huisnummer in.'; + if (!straat) errors.straat = $localize`:@@validation.straat:Vul straat en huisnummer in.`; if (!postcode.ok) errors.postcode = postcode.error; if (straat && postcode.ok) { return { ok: true, value: { straat, postcode: postcode.value, woonplaats: draft.woonplaats.trim() } }; diff --git a/src/app/registratie/domain/registratie-wizard.machine.ts b/src/app/registratie/domain/registratie-wizard.machine.ts index 85acd7a..c45b4b8 100644 --- a/src/app/registratie/domain/registratie-wizard.machine.ts +++ b/src/app/registratie/domain/registratie-wizard.machine.ts @@ -85,11 +85,11 @@ function validateStep(step: StepId, d: Draft): Result { const errors: Errors = {}; switch (step) { case 'adres': { - if (!d.straat || d.straat.trim() === '') errors.straat = 'Vul een straat en huisnummer in.'; + if (!d.straat || d.straat.trim() === '') errors.straat = $localize`:@@validation.straat2:Vul een straat en huisnummer in.`; const pc = parsePostcode(d.postcode ?? ''); if (!pc.ok) errors.postcode = pc.error; - if (!d.woonplaats || d.woonplaats.trim() === '') errors.woonplaats = 'Vul een woonplaats in.'; - if (!d.correspondentie) errors.correspondentie = 'Maak een keuze.'; + if (!d.woonplaats || d.woonplaats.trim() === '') errors.woonplaats = $localize`:@@validation.woonplaats:Vul een woonplaats in.`; + if (!d.correspondentie) errors.correspondentie = $localize`:@@validation.maakKeuze:Maak een keuze.`; // E-mail is only required when 'email' is the chosen channel. if (d.correspondentie === 'email') { const e = parseEmail(d.email ?? ''); @@ -100,7 +100,7 @@ function validateStep(step: StepId, d: Draft): Result { case 'beroep': { // A diploma must be chosen (or declared manually); its beroep is then known. if (!d.diplomaId || !d.beroep) { - errors.diploma = 'Kies het diploma waarmee u zich wilt registreren, of voer het handmatig in.'; + errors.diploma = $localize`:@@validation.diploma:Kies het diploma waarmee u zich wilt registreren, of voer het handmatig in.`; break; } // Every policy question the chosen diploma raised must be answered. Which @@ -108,7 +108,7 @@ function validateStep(step: StepId, d: Draft): Result { // they're answered. const open: Record = {}; for (const id of d.vraagIds ?? []) { - if (!(d.antwoorden[id] ?? '').trim()) open[id] = 'Beantwoord deze vraag.'; + if (!(d.antwoorden[id] ?? '').trim()) open[id] = $localize`:@@validation.beantwoordVraag:Beantwoord deze vraag.`; } if (Object.keys(open).length > 0) errors.antwoorden = open; break; diff --git a/src/app/registratie/domain/tasks.ts b/src/app/registratie/domain/tasks.ts index 01b652d..3a320fd 100644 --- a/src/app/registratie/domain/tasks.ts +++ b/src/app/registratie/domain/tasks.ts @@ -28,30 +28,30 @@ export function tasksFromProfile(reg: Registration, eligibleForHerregistratie: b if (eligibleForHerregistratie) { const deadline = herregistratieDeadline(reg); tasks.push({ - title: 'Vraag uw herregistratie aan', + title: $localize`:@@task.herregistratie.title:Vraag uw herregistratie aan`, description: deadline - ? `Verleng uw registratie vóór ${formatNL(deadline)}.` - : 'U kunt nu uw herregistratie aanvragen.', + ? $localize`:@@task.herregistratie.deadline:Verleng uw registratie vóór ${formatNL(deadline)}:deadline:.` + : $localize`:@@task.herregistratie.nodeadline:U kunt nu uw herregistratie aanvragen.`, to: '/herregistratie', - actionLabel: 'Herregistratie aanvragen', + actionLabel: $localize`:@@task.herregistratie.action:Herregistratie aanvragen`, }); } if (reg.status.tag === 'Geschorst') { tasks.push({ - title: 'Uw registratie is geschorst', + title: $localize`:@@task.geschorst.title:Uw registratie is geschorst`, description: reg.status.reden, to: '/registratie', - actionLabel: 'Bekijk uw gegevens', + actionLabel: $localize`:@@task.bekijkGegevens.action:Bekijk uw gegevens`, }); } if (reg.status.tag === 'Doorgehaald') { tasks.push({ - title: 'Uw registratie is doorgehaald', + title: $localize`:@@task.doorgehaald.title:Uw registratie is doorgehaald`, description: reg.status.reden, to: '/registratie', - actionLabel: 'Bekijk uw gegevens', + actionLabel: $localize`:@@task.bekijkGegevens.action:Bekijk uw gegevens`, }); } diff --git a/src/app/registratie/domain/value-objects/big-nummer.ts b/src/app/registratie/domain/value-objects/big-nummer.ts index d60fd93..fab3a0e 100644 --- a/src/app/registratie/domain/value-objects/big-nummer.ts +++ b/src/app/registratie/domain/value-objects/big-nummer.ts @@ -5,5 +5,5 @@ export type BigNummer = Brand; export function parseBigNummer(raw: string): Result { const t = raw.trim(); - return /^\d{11}$/.test(t) ? ok(t as BigNummer) : err('Een BIG-nummer bestaat uit 11 cijfers.'); + return /^\d{11}$/.test(t) ? ok(t as BigNummer) : err($localize`:@@validation.bigNummer:Een BIG-nummer bestaat uit 11 cijfers.`); } diff --git a/src/app/registratie/domain/value-objects/email.ts b/src/app/registratie/domain/value-objects/email.ts index fb9369e..4fb9c56 100644 --- a/src/app/registratie/domain/value-objects/email.ts +++ b/src/app/registratie/domain/value-objects/email.ts @@ -13,7 +13,7 @@ export function parseEmail(raw: string): Result { // Deliberately lax: a single @ with non-empty, dot-bearing parts. Good enough // for instant feedback; the server re-validates. if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)) { - return err('Voer een geldig e-mailadres in, bijv. naam@voorbeeld.nl.'); + return err($localize`:@@validation.email:Voer een geldig e-mailadres in, bijv. naam@voorbeeld.nl.`); } return ok(t as Email); } diff --git a/src/app/registratie/domain/value-objects/postcode.ts b/src/app/registratie/domain/value-objects/postcode.ts index 2339a45..2264fce 100644 --- a/src/app/registratie/domain/value-objects/postcode.ts +++ b/src/app/registratie/domain/value-objects/postcode.ts @@ -10,7 +10,7 @@ export type Postcode = Brand; export function parsePostcode(raw: string): Result { const t = raw.trim().toUpperCase(); if (!/^[1-9]\d{3}\s?[A-Z]{2}$/.test(t)) { - return err('Voer een geldige postcode in, bijv. 1234 AB.'); + return err($localize`:@@validation.postcode:Voer een geldige postcode in, bijv. 1234 AB.`); } // Normalise to "1234 AB" — the parser also cleans up. return ok(t.replace(/^(\d{4})\s?([A-Z]{2})$/, '$1 $2') as Postcode); diff --git a/src/app/registratie/domain/value-objects/uren.ts b/src/app/registratie/domain/value-objects/uren.ts index 8e8563c..de6f377 100644 --- a/src/app/registratie/domain/value-objects/uren.ts +++ b/src/app/registratie/domain/value-objects/uren.ts @@ -8,7 +8,7 @@ export function parseUren(raw: string): Result { const n = Number(t); // Number('') is 0 — guard the empty string explicitly. if (t === '' || !Number.isInteger(n) || n < 0) { - return err('Vul een geheel aantal in (0 of meer).'); + return err($localize`:@@validation.uren:Vul een geheel aantal in (0 of meer).`); } return ok(n as Uren); } diff --git a/src/app/registratie/ui/address-fields/address-fields.component.ts b/src/app/registratie/ui/address-fields/address-fields.component.ts index 69ca78e..1db77f3 100644 --- a/src/app/registratie/ui/address-fields/address-fields.component.ts +++ b/src/app/registratie/ui/address-fields/address-fields.component.ts @@ -27,17 +27,17 @@ export type AdresErrors = Partial>; template: `
{{ legend() }} - + - + + name="postcode" i18n-placeholder="@@address.postcodePlaceholder" placeholder="1234 AB" [ngModelOptions]="{ standalone: true }" /> - + @@ -50,6 +50,6 @@ export class AddressFieldsComponent { errors = input({}); /** Prefix for field ids/labels — keeps them unique if two blocks ever co-exist. */ idPrefix = input('adres'); - legend = input('Adres'); + legend = input($localize`:@@address.legend:Adres`); fieldChange = output<{ key: keyof AdresValue; value: string }>(); } diff --git a/src/app/registratie/ui/change-request-form/change-request-form.component.ts b/src/app/registratie/ui/change-request-form/change-request-form.component.ts index 3e132ad..7789154 100644 --- a/src/app/registratie/ui/change-request-form/change-request-form.component.ts +++ b/src/app/registratie/ui/change-request-form/change-request-form.component.ts @@ -20,14 +20,14 @@ import { ApiClient } from '@shared/infrastructure/api-client'; imports: [ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent], template: ` @if (state().tag === 'Submitted') { - + Uw adreswijziging is ontvangen (referentie {{ referentie() }}). U ontvangt binnen 5 werkdagen bericht.
- Nieuwe wijziging doorgeven + Nieuwe wijziging doorgeven
} @else { - Adreswijziging doorgeven + Adreswijziging doorgeven
@if (failedError()) { - Het indienen is niet gelukt: {{ failedError() }} + Het indienen is niet gelukt: {{ failedError() }} } - {{ state().tag === 'Submitting' ? 'Bezig met indienen…' : 'Wijziging indienen' }} + {{ state().tag === 'Submitting' ? submitBezigLabel : submitLabel }} } @@ -56,6 +56,9 @@ export class ChangeRequestFormComponent { readonly state = this.store.model; protected dispatch = this.store.dispatch; + protected readonly submitLabel = $localize`:@@changeRequest.submit:Wijziging indienen`; + protected readonly submitBezigLabel = $localize`:@@changeRequest.submitBezig:Bezig met indienen…`; + private editing = computed(() => whenTag(this.state(), 'Editing')); protected errors = computed(() => this.editing()?.errors ?? {}); protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? ''); diff --git a/src/app/registratie/ui/dashboard.page.ts b/src/app/registratie/ui/dashboard.page.ts index 875f8fd..3484aa8 100644 --- a/src/app/registratie/ui/dashboard.page.ts +++ b/src/app/registratie/ui/dashboard.page.ts @@ -25,13 +25,13 @@ import { tasksFromProfile } from '@registratie/domain/tasks'; RegistrationSummaryComponent, RegistrationTableComponent, ], template: ` - +
@if (store.pendingHerregistratie()) { - Uw herregistratie-aanvraag is in behandeling. + Uw herregistratie-aanvraag is in behandeling. } @@ -39,24 +39,24 @@ import { tasksFromProfile } from '@registratie/domain/tasks'; @let tasks = tasksFor($any(p).registration);
- Wat moet ik regelen + Wat moet ik regelen @if (tasks.length) { } @else { -

U heeft op dit moment niets openstaan.

+

U heeft op dit moment niets openstaan.

}
- Mijn registratie + Mijn registratie
- +
- - - + + +
@@ -67,7 +67,7 @@ import { tasksFromProfile } from '@registratie/domain/tasks';
- Specialismen en aantekeningen + Specialismen en aantekeningen
@@ -77,14 +77,14 @@ import { tasksFromProfile } from '@registratie/domain/tasks'; -

U heeft nog geen specialismen of aantekeningen.

+

U heeft nog geen specialismen of aantekeningen.

- Wat wilt u doen? + Wat wilt u doen?
    @for (a of acties; track a.to) {
  • @@ -116,16 +116,16 @@ export class DashboardPage { /** Portal sections (navigation, not actions). */ protected readonly nav: NavItem[] = [ - { label: 'Overzicht', to: '/dashboard' }, - { label: 'Mijn gegevens', to: '/registratie' }, - { label: 'Herregistratie', to: '/herregistratie' }, - { label: 'Inschrijven', to: '/registreren' }, + { label: $localize`:@@dashboard.nav.overzicht:Overzicht`, to: '/dashboard' }, + { label: $localize`:@@dashboard.nav.gegevens:Mijn gegevens`, to: '/registratie' }, + { label: $localize`:@@dashboard.nav.herregistratie:Herregistratie`, to: '/herregistratie' }, + { label: $localize`:@@dashboard.nav.inschrijven:Inschrijven`, to: '/registreren' }, ]; /** Primary transactional actions, as a card grid. */ protected readonly acties = [ - { to: '/registreren', titel: 'Inschrijven', tekst: 'Schrijf u in in het BIG-register via de registratiewizard.', actie: 'Start inschrijving' }, - { to: '/herregistratie', titel: 'Herregistratie aanvragen', tekst: 'Verleng uw registratie voor de komende periode.', actie: 'Vraag aan' }, - { to: '/registratie', titel: 'Gegevens wijzigen', tekst: 'Bekijk uw gegevens of geef een wijziging door.', actie: 'Bekijk gegevens' }, + { to: '/registreren', titel: $localize`:@@dashboard.actie.inschrijven.titel:Inschrijven`, tekst: $localize`:@@dashboard.actie.inschrijven.tekst:Schrijf u in in het BIG-register via de registratiewizard.`, actie: $localize`:@@dashboard.actie.inschrijven.actie:Start inschrijving` }, + { to: '/herregistratie', titel: $localize`:@@dashboard.actie.herregistratie.titel:Herregistratie aanvragen`, tekst: $localize`:@@dashboard.actie.herregistratie.tekst:Verleng uw registratie voor de komende periode.`, actie: $localize`:@@dashboard.actie.herregistratie.actie:Vraag aan` }, + { to: '/registratie', titel: $localize`:@@dashboard.actie.wijzigen.titel:Gegevens wijzigen`, tekst: $localize`:@@dashboard.actie.wijzigen.tekst:Bekijk uw gegevens of geef een wijziging door.`, actie: $localize`:@@dashboard.actie.wijzigen.actie:Bekijk gegevens` }, ]; } diff --git a/src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts b/src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts index 11eafdb..adf3b79 100644 --- a/src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts +++ b/src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts @@ -2,7 +2,7 @@ import { Component, computed, effect, inject, input, untracked } from '@angular/ import { FormsModule } from '@angular/forms'; import { FormFieldComponent } from '@shared/ui/form-field/form-field.component'; import { TextInputComponent } from '@shared/ui/text-input/text-input.component'; -import { RadioGroupComponent } from '@shared/ui/radio-group/radio-group.component'; +import { RadioGroupComponent, JA_NEE } from '@shared/ui/radio-group/radio-group.component'; import { ButtonComponent } from '@shared/ui/button/button.component'; import { AlertComponent } from '@shared/ui/alert/alert.component'; import { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component'; @@ -31,8 +31,10 @@ import { submitRegistratie } from '@registratie/application/submit-registratie'; import { ApiClient } from '@shared/infrastructure/api-client'; const STORAGE_KEY = 'registratie-v1'; // ponytail: bump the suffix if the persisted shape changes; no migration. -const KANALEN = [{ value: 'email', label: 'E-mail' }, { value: 'post', label: 'Post' }]; -const JA_NEE = [{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }]; +const KANALEN = [ + { value: 'email', label: $localize`:@@registratie.kanaalEmail:E-mail` }, + { value: 'post', label: $localize`:@@registratie.kanaalPost:Post` }, +]; const HANDMATIG = '__handmatig__'; // sentinel option: "my diploma isn't listed" /** Organism: the BIG-registration wizard. All state lives in one signal driven by @@ -58,7 +60,7 @@ const HANDMATIG = '__handmatig__'; // sentinel option: "my diploma isn't listed" [canGoBack]="cursor() > 0" [errors]="errorList()" [errorMessage]="errorMessage()" - submittingLabel="Uw registratie wordt verwerkt…" + i18n-submittingLabel="@@regWizard.submitting" submittingLabel="Uw registratie wordt verwerkt…" (primary)="onPrimary()" (back)="dispatch({ tag: 'Back' })" (cancel)="restart()" @@ -71,26 +73,26 @@ const HANDMATIG = '__handmatig__'; // sentinel option: "my diploma isn't listed" } @else { @switch (adresStatus()) { @case ('gevonden') { - Vooraf ingevuld op basis van de BRP. Controleer en pas zo nodig aan. + Vooraf ingevuld op basis van de BRP. Controleer en pas zo nodig aan. } @case ('geen') { - We vonden geen adres in de BRP. Vul uw adres hieronder handmatig in. + We vonden geen adres in de BRP. Vul uw adres hieronder handmatig in. } @case ('fout') { - We konden de BRP nu niet bereiken. Vul uw adres hieronder handmatig in. + We konden de BRP nu niet bereiken. Vul uw adres hieronder handmatig in. } } - + @if (draft().correspondentie === 'email') { - - + + } } @@ -98,20 +100,20 @@ const HANDMATIG = '__handmatig__'; // sentinel option: "my diploma isn't listed" @case ('beroep') { - + @if (handmatigActief()) { - Een handmatig ingevoerd diploma kan niet automatisch worden geverifieerd. Kies uw beroep en beantwoord de aanvullende vragen; uw aanvraag wordt daarna handmatig beoordeeld. - + Een handmatig ingevoerd diploma kan niet automatisch worden geverifieerd. Kies uw beroep en beantwoord de aanvullende vragen; uw aanvraag wordt daarna handmatig beoordeeld. + } @else if (draft().beroep) {
    - +
    } @@ -133,31 +135,31 @@ const HANDMATIG = '__handmatig__'; // sentinel option: "my diploma isn't listed"
    } @case ('controle') { - Controleer uw gegevens en dien de registratie in. + Controleer uw gegevens en dien de registratie in.
    - - - + + + @if (draft().correspondentie === 'email') { - + } - - + + @for (item of samenvattingVragen(); track item.vraag) { }
    - Adres wijzigen - Diploma wijzigen + Adres wijzigen + Diploma wijzigen
    } }
    - Uw registratie is ontvangen. Uw referentienummer is {{ referentie() }}. Bewaar dit nummer voor uw administratie. + Uw registratie is ontvangen. Uw referentienummer is {{ referentie() }}. Bewaar dit nummer voor uw administratie.
    - Nieuwe registratie starten + Nieuwe registratie starten
    @@ -176,8 +178,8 @@ export class RegistratieWizardComponent { seed = input(initial); readonly kanalen = KANALEN; - readonly stepLabels = ['Adres', 'Beroep', 'Controle']; // short labels for the stepper - private stepTitles = ['Adres en correspondentievoorkeur', 'Beroep op basis van uw diploma', 'Controleren en indienen']; + readonly stepLabels = [$localize`:@@regWizard.step.adres:Adres`, $localize`:@@regWizard.step.beroep:Beroep`, $localize`:@@regWizard.step.controle:Controle`]; // short labels for the stepper + private stepTitles = [$localize`:@@regWizard.title.adres:Adres en correspondentievoorkeur`, $localize`:@@regWizard.title.beroep:Beroep op basis van uw diploma`, $localize`:@@regWizard.title.controle:Controleren en indienen`]; readonly state = this.store.model; readonly dispatch = this.store.dispatch; @@ -190,8 +192,8 @@ export class RegistratieWizardComponent { protected failedError = computed(() => whenTag(this.state(), 'Mislukt')?.error ?? ''); // --- Presentational wiring for the shared wizard shell --------------------- - protected primaryLabel = computed(() => (this.step() === 'controle' ? 'Registratie indienen' : 'Volgende')); - protected errorMessage = computed(() => `Het indienen is niet gelukt: ${this.failedError()}`); + protected primaryLabel = computed(() => (this.step() === 'controle' ? $localize`:@@regWizard.indienen:Registratie indienen` : $localize`:@@wizard.volgende:Volgende`)); + protected errorMessage = computed(() => $localize`:@@regWizard.indienenMislukt:Het indienen is niet gelukt:` + ` ${this.failedError()}`); protected shellStatus = computed(() => { switch (this.state().tag) { case 'Invullen': return 'editing'; @@ -217,9 +219,9 @@ export class RegistratieWizardComponent { return [d.straat, [d.postcode, d.woonplaats].filter(Boolean).join(' ')].filter(Boolean).join(', '); }); // Readable labels for the controle summary (instead of raw enum values). - protected adresHerkomstLabel = computed(() => ({ brp: 'Automatisch uit de BRP', handmatig: 'Handmatig ingevoerd' }[this.draft().adresHerkomst ?? 'handmatig'])); - protected correspondentieLabel = computed(() => ({ email: 'Per e-mail', post: 'Per post' }[this.draft().correspondentie ?? 'post'])); - protected diplomaHerkomstLabel = computed(() => ({ duo: 'Geverifieerd via DUO', handmatig: 'Handmatig ingevoerd (wordt beoordeeld)' }[this.draft().diplomaHerkomst ?? 'handmatig'])); + protected adresHerkomstLabel = computed(() => ({ brp: $localize`:@@regWizard.herkomst.adresBrp:Automatisch uit de BRP`, handmatig: $localize`:@@regWizard.herkomst.adresHandmatig:Handmatig ingevoerd` }[this.draft().adresHerkomst ?? 'handmatig'])); + protected correspondentieLabel = computed(() => ({ email: $localize`:@@regWizard.corr.email:Per e-mail`, post: $localize`:@@regWizard.corr.post:Per post` }[this.draft().correspondentie ?? 'post'])); + protected diplomaHerkomstLabel = computed(() => ({ duo: $localize`:@@regWizard.herkomst.diplomaDuo:Geverifieerd via DUO`, handmatig: $localize`:@@regWizard.herkomst.diplomaHandmatig:Handmatig ingevoerd (wordt beoordeeld)` }[this.draft().diplomaHerkomst ?? 'handmatig'])); /** BRP lookup outcome. A failure or "geen adres" never blocks the wizard — both fall back to manual entry (PRD §7). 'geen' covers both no-address-found and a @@ -262,7 +264,7 @@ export class RegistratieWizardComponent { protected diplomaOptions = (data: DuoLookupDto) => [ ...data.diplomas.map((d) => ({ value: d.id, label: `${d.naam} — ${d.instelling} (${d.jaar})` })), - { value: HANDMATIG, label: 'Mijn diploma staat er niet bij' }, + { value: HANDMATIG, label: $localize`:@@regWizard.diplomaNietBij:Mijn diploma staat er niet bij` }, ]; protected beroepOptions = (data: DuoLookupDto) => data.handmatig.beroepen.map((b) => ({ value: b, label: b })); diff --git a/src/app/registratie/ui/registratie.page.ts b/src/app/registratie/ui/registratie.page.ts index d20fa43..206b4c7 100644 --- a/src/app/registratie/ui/registratie.page.ts +++ b/src/app/registratie/ui/registratie.page.ts @@ -10,9 +10,10 @@ import { RegistratieWizardComponent } from '@registratie/ui/registratie-wizard/r imports: [PageShellComponent, AlertComponent, RegistratieWizardComponent], template: ` - + In drie stappen schrijft u zich in: uw adres en correspondentievoorkeur, het diploma waarmee u zich registreert, en een controle. Uw gegevens blijven bewaard als u de pagina herlaadt. diff --git a/src/app/registratie/ui/registration-detail.page.ts b/src/app/registratie/ui/registration-detail.page.ts index 222193c..eb84513 100644 --- a/src/app/registratie/ui/registration-detail.page.ts +++ b/src/app/registratie/ui/registration-detail.page.ts @@ -13,7 +13,7 @@ import { BigProfileStore } from '@registratie/application/big-profile.store'; RegistrationSummaryComponent, ChangeRequestFormComponent, ], template: ` - + diff --git a/src/app/registratie/ui/registration-summary/registration-summary.component.ts b/src/app/registratie/ui/registration-summary/registration-summary.component.ts index 83b1ff9..b8b42e3 100644 --- a/src/app/registratie/ui/registration-summary/registration-summary.component.ts +++ b/src/app/registratie/ui/registration-summary/registration-summary.component.ts @@ -13,25 +13,25 @@ import { CardComponent } from '@shared/ui/card/card.component'; template: `
    - - - - + + + + - + @switch (reg().status.tag) { @case ('Geregistreerd') { - + } @case ('Geschorst') { - - + + } @case ('Doorgehaald') { - - + + } }
    diff --git a/src/app/registratie/ui/registration-table/registration-table.component.ts b/src/app/registratie/ui/registration-table/registration-table.component.ts index f6309ef..716e95a 100644 --- a/src/app/registratie/ui/registration-table/registration-table.component.ts +++ b/src/app/registratie/ui/registration-table/registration-table.component.ts @@ -11,9 +11,9 @@ import { Aantekening } from '@registratie/domain/registration'; - - - + + + diff --git a/src/app/shared/application/submit.ts b/src/app/shared/application/submit.ts index 041086f..bd4b28f 100644 --- a/src/app/shared/application/submit.ts +++ b/src/app/shared/application/submit.ts @@ -15,6 +15,6 @@ export async function runSubmit(fn: () => Promise, fallback: string): Prom } } -// ponytail: i18n in Step 3/M3 wraps this in $localize with a stable @@id, which -// dedupes it at the translation layer; for now it's the single shared default. -export const SUBMIT_FAILED = 'Het indienen is niet gelukt. Probeer het later opnieuw.'; +// Single shared default for a failed submit; the @@id dedupes it at the +// translation layer. +export const SUBMIT_FAILED = $localize`:@@submit.failed:Het indienen is niet gelukt. Probeer het later opnieuw.`; diff --git a/src/app/shared/layout/breadcrumb/breadcrumb-trail.ts b/src/app/shared/layout/breadcrumb/breadcrumb-trail.ts index 6eb9838..87776d3 100644 --- a/src/app/shared/layout/breadcrumb/breadcrumb-trail.ts +++ b/src/app/shared/layout/breadcrumb/breadcrumb-trail.ts @@ -6,12 +6,12 @@ import { BreadcrumbItem } from './breadcrumb.component'; interface Crumb { label: string; parent?: string } const ROUTES: Record = { - '/dashboard': { label: 'Mijn overzicht' }, - '/registratie': { label: 'Mijn gegevens', parent: '/dashboard' }, - '/registreren': { label: 'Inschrijven', parent: '/dashboard' }, - '/herregistratie': { label: 'Herregistratie', parent: '/dashboard' }, - '/intake': { label: 'Herregistratie-intake', parent: '/dashboard' }, - '/concepts': { label: 'Functionele patronen', parent: '/dashboard' }, + '/dashboard': { label: $localize`:@@crumb.dashboard:Mijn overzicht` }, + '/registratie': { label: $localize`:@@crumb.registratie:Mijn gegevens`, parent: '/dashboard' }, + '/registreren': { label: $localize`:@@crumb.registreren:Inschrijven`, parent: '/dashboard' }, + '/herregistratie': { label: $localize`:@@crumb.herregistratie:Herregistratie`, parent: '/dashboard' }, + '/intake': { label: $localize`:@@crumb.intake:Herregistratie-intake`, parent: '/dashboard' }, + '/concepts': { label: $localize`:@@crumb.concepts:Functionele patronen`, parent: '/dashboard' }, }; /** Build the breadcrumb trail for a router url (query/fragment stripped). diff --git a/src/app/shared/layout/breadcrumb/breadcrumb.component.ts b/src/app/shared/layout/breadcrumb/breadcrumb.component.ts index 286d4d3..a67c9b5 100644 --- a/src/app/shared/layout/breadcrumb/breadcrumb.component.ts +++ b/src/app/shared/layout/breadcrumb/breadcrumb.component.ts @@ -26,7 +26,7 @@ export interface BreadcrumbItem { :host(.inverse) .sep { color: var(--rhc-color-foreground-on-primary); } `], template: ` -
    TypeOmschrijvingDatumTypeOmschrijvingDatum