fix(i18n): translate viewer + Transcribe panel controls so EN/ES locales do not show German labels #319

Open
opened 2026-04-24 13:22:31 +02:00 by marcel · 8 comments
Owner

Context

With locale set to EN (and presumably ES — not yet sampled), several controls inside the document viewer and Transcribe panel render with German labels instead of being translated.

Captured on 2026-04-24 during Phase B2 audit, structural snap at /tmp/fa-audit/G2-snap.json:

  • Viewer page navigation: Zurück, Weiter
  • Viewer zoom controls: Verkleinern, Vergrößern
  • Transcribe panel footer: Für Training vormerken
  • Mobile header drawer: Menü öffnen

Other controls in the same surfaces (Read, Edit, Done, Close panel, Draw regions on the document to start transcribing.) are in English — so the surrounding i18n plumbing works; these specific strings just weren't wired up.

Stakes: the product mission is "readable for a younger generation." That generation may prefer English (or Spanish, for family in Spain / Latin America). Mixed-locale UI undermines trust in the translation and fails WCAG 3.1.2 ("language of parts").

Non-goals

  • No new DE strings invented — existing German strings are authoritative; translate them, don't rewrite.
  • No full i18n audit of the whole app (this issue scopes viewer + Transcribe; others can follow).
  • No language-detection heuristics.

Implementation plan

1. Discover every violation

cd frontend/src
rg --type svelte --type ts "Zurück|Weiter|Verkleinern|Vergrößern|Für Training vormerken|Menü öffnen"

Also grep broader for suspiciously hardcoded German in viewer/Transcribe files:

rg --type svelte "[äöüÄÖÜß]" src/lib/components/TranscribePanel.svelte src/lib/components/DocumentViewer.svelte

Each hit becomes a Paraglide key.

2. Add message keys

In frontend/messages/{de,en,es}.json — keys must exist in all three:

{
  "viewer_previous_page":            "Zurück" / "Previous" / "Anterior",
  "viewer_next_page":                "Weiter" / "Next" / "Siguiente",
  "viewer_zoom_out":                 "Verkleinern" / "Zoom out" / "Alejar",
  "viewer_zoom_in":                  "Vergrößern" / "Zoom in" / "Acercar",
  "transcribe_mark_for_training":    "Für Training vormerken" / "Mark for training" / "Marcar para entrenamiento",
  "layout_menu_open":                "Menü öffnen" / "Open menu" / "Abrir menú",
  "layout_menu_close":               "Menü schließen" / "Close menu" / "Cerrar menú"
}

Translation suggestions — confirm with a native speaker before merge. Spanish especially.

3. Replace string literals with m.*() calls

  • frontend/src/lib/components/DocumentViewer.svelte (or its zoom/pager sub-components — verify the actual component names via the rg output)
  • frontend/src/lib/components/TranscribePanel.svelte (or its footer component)
  • frontend/src/routes/+layout.svelte (mobile drawer toggle button)

4. Regression guard

Add a Vitest unit test that asserts the three JSON files have identical key sets:

// frontend/src/lib/messages.spec.ts — extend if it exists; new file otherwise
test("de/en/es have identical message key sets", () => {
  const de = Object.keys(deMessages).sort();
  const en = Object.keys(enMessages).sort();
  const es = Object.keys(esMessages).sort();
  expect(en).toEqual(de);
  expect(es).toEqual(de);
});

Tests

  • Unit: key-parity test (above).
  • E2E: login, set locale to EN, navigate to /documents/<some-id>, open Transcribe panel, assert none of Zurück|Weiter|Verkleinern|Vergrößern|Für Training vormerken|Menü öffnen appear in the visible DOM.
  • E2E: same pass for ES locale.
  • axe-core: run WCAG 3.1.2 check in EN locale — no "language of parts" violations.

Verification

  1. Switch locale to EN in the app. Open any document, open Transcribe panel. Hover all viewer controls — labels must be English.
  2. Switch locale to ES. Same walk.
  3. Run the string-search heuristic once more across frontend/src/ for any residual hardcoded German. Zero hits expected.

Acceptance criteria

  • Every violating string moved through Paraglide (≥ 6 strings; expand if rg finds more)
  • de/en/es JSONs have matching key sets (enforced by unit test)
  • E2E verifies no German appears in EN-locale viewer + Transcribe panel
  • E2E verifies no German appears in ES-locale viewer + Transcribe panel
  • axe-core WCAG 3.1.2 passes in both EN and ES
  • No new hardcoded literals introduced in affected files

Critical files

frontend/src/lib/components/DocumentViewer.svelte           (verify name)
frontend/src/lib/components/TranscribePanel.svelte          (verify name)
frontend/src/routes/+layout.svelte
frontend/messages/de.json
frontend/messages/en.json
frontend/messages/es.json
frontend/src/lib/messages.spec.ts                           (new or extended)
frontend/e2e/i18n.spec.ts                                   (new or extended)
## Context With locale set to **EN** (and presumably ES — not yet sampled), several controls inside the document viewer and Transcribe panel render with German labels instead of being translated. Captured on 2026-04-24 during Phase B2 audit, structural snap at `/tmp/fa-audit/G2-snap.json`: - Viewer page navigation: `Zurück`, `Weiter` - Viewer zoom controls: `Verkleinern`, `Vergrößern` - Transcribe panel footer: `Für Training vormerken` - Mobile header drawer: `Menü öffnen` Other controls in the same surfaces (`Read`, `Edit`, `Done`, `Close panel`, `Draw regions on the document to start transcribing.`) are in English — so the surrounding i18n plumbing works; these specific strings just weren't wired up. Stakes: the product mission is "readable for a younger generation." That generation may prefer English (or Spanish, for family in Spain / Latin America). Mixed-locale UI undermines trust in the translation and fails WCAG 3.1.2 ("language of parts"). ## Non-goals - No new DE strings invented — existing German strings are authoritative; translate them, don't rewrite. - No full i18n audit of the whole app (this issue scopes viewer + Transcribe; others can follow). - No language-detection heuristics. ## Implementation plan ### 1. Discover every violation ```bash cd frontend/src rg --type svelte --type ts "Zurück|Weiter|Verkleinern|Vergrößern|Für Training vormerken|Menü öffnen" ``` Also grep broader for suspiciously hardcoded German in viewer/Transcribe files: ```bash rg --type svelte "[äöüÄÖÜß]" src/lib/components/TranscribePanel.svelte src/lib/components/DocumentViewer.svelte ``` Each hit becomes a Paraglide key. ### 2. Add message keys In `frontend/messages/{de,en,es}.json` — keys must exist in all three: ```json { "viewer_previous_page": "Zurück" / "Previous" / "Anterior", "viewer_next_page": "Weiter" / "Next" / "Siguiente", "viewer_zoom_out": "Verkleinern" / "Zoom out" / "Alejar", "viewer_zoom_in": "Vergrößern" / "Zoom in" / "Acercar", "transcribe_mark_for_training": "Für Training vormerken" / "Mark for training" / "Marcar para entrenamiento", "layout_menu_open": "Menü öffnen" / "Open menu" / "Abrir menú", "layout_menu_close": "Menü schließen" / "Close menu" / "Cerrar menú" } ``` Translation suggestions — confirm with a native speaker before merge. Spanish especially. ### 3. Replace string literals with `m.*()` calls - `frontend/src/lib/components/DocumentViewer.svelte` (or its zoom/pager sub-components — verify the actual component names via the rg output) - `frontend/src/lib/components/TranscribePanel.svelte` (or its footer component) - `frontend/src/routes/+layout.svelte` (mobile drawer toggle button) ### 4. Regression guard Add a Vitest unit test that asserts the three JSON files have identical key sets: ```ts // frontend/src/lib/messages.spec.ts — extend if it exists; new file otherwise test("de/en/es have identical message key sets", () => { const de = Object.keys(deMessages).sort(); const en = Object.keys(enMessages).sort(); const es = Object.keys(esMessages).sort(); expect(en).toEqual(de); expect(es).toEqual(de); }); ``` ## Tests - **Unit:** key-parity test (above). - **E2E:** login, set locale to EN, navigate to `/documents/<some-id>`, open Transcribe panel, assert none of `Zurück|Weiter|Verkleinern|Vergrößern|Für Training vormerken|Menü öffnen` appear in the visible DOM. - **E2E:** same pass for ES locale. - **axe-core:** run WCAG 3.1.2 check in EN locale — no "language of parts" violations. ## Verification 1. Switch locale to EN in the app. Open any document, open Transcribe panel. Hover all viewer controls — labels must be English. 2. Switch locale to ES. Same walk. 3. Run the string-search heuristic once more across `frontend/src/` for any residual hardcoded German. Zero hits expected. ## Acceptance criteria - [ ] Every violating string moved through Paraglide (≥ 6 strings; expand if rg finds more) - [ ] `de/en/es` JSONs have matching key sets (enforced by unit test) - [ ] E2E verifies no German appears in EN-locale viewer + Transcribe panel - [ ] E2E verifies no German appears in ES-locale viewer + Transcribe panel - [ ] axe-core WCAG 3.1.2 passes in both EN and ES - [ ] No new hardcoded literals introduced in affected files ## Critical files ``` frontend/src/lib/components/DocumentViewer.svelte (verify name) frontend/src/lib/components/TranscribePanel.svelte (verify name) frontend/src/routes/+layout.svelte frontend/messages/de.json frontend/messages/en.json frontend/messages/es.json frontend/src/lib/messages.spec.ts (new or extended) frontend/e2e/i18n.spec.ts (new or extended) ```
marcel added this to the Transcriber Experience v1 milestone 2026-04-24 13:22:31 +02:00
marcel added the P1-highbugui labels 2026-04-24 13:28:05 +02:00
marcel modified the milestone from Transcriber Experience v1 to Demo Day — family get-together 2026-04-24 13:35:13 +02:00
Author
Owner

👨‍💻 Felix Brandt — Senior Fullstack Developer

Observations

  • I grepped the repo. The violations live in PdfControls.svelte (not DocumentViewer.svelte as the critical files list says). The four aria-labels (Zurück, Weiter, Verkleinern, Vergrößern) are on lines with aria-label="..." on icon-only buttons. DocumentViewer.svelte exists but is not where these strings are. Fix the critical files list before anyone starts implementing.
  • AppNav.svelte:87 confirmed: aria-label={mobileNavOpen ? 'Menü schließen' : 'Menü öffnen'} — both states are hardcoded.
  • TranscriptionEditView.svelte:242 confirmed: <p class="mb-2 font-sans text-xs font-medium text-ink-2">Für Training vormerken</p> — this is visible text, not just an aria-label.
  • btn_back already exists in de.json with value "Zurück", but it carries navigation-back semantics. Do not reuse it for the PDF page-prev button — viewer_previous_page is the right distinct key.
  • The discovery command rg --type svelte will silently find nothing: ripgrep doesn't know the svelte filetype by default. Use rg -g '*.svelte' or pass file paths directly.
  • All three JSONs are currently at exactly 908 lines, so keys are already in sync — the parity test guards against future drift, not a current problem.

Recommendations

  • Fix the critical files list: replace DocumentViewer.svelte with PdfControls.svelte.
  • Fix the grep command: rg -g '*.svelte' -g '*.ts' "Zurück|Weiter|Verkleinern|Vergrößern|Für Training|Menü öffnen|Menü schließen" frontend/src/
  • TDD order: write the key parity test first (it should pass immediately — keys are in sync), then add all 7 keys to all three JSONs, then replace literals in the three component files.
  • layout_menu_close ("Menü schließen") is in AppNav.svelte but is absent from the proposed key list. Add it — that's violation #7 and it's already in the rg grep pattern in the issue body.
  • PdfControls.svelte already imports m from $lib/paraglide/messages.js and uses it for the annotation toggle (m.pdf_annotations_hide() / m.pdf_annotations_show()). The wiring pattern is already there — this is a mechanical substitution.
## 👨‍💻 Felix Brandt — Senior Fullstack Developer ### Observations - I grepped the repo. The violations live in **`PdfControls.svelte`** (not `DocumentViewer.svelte` as the critical files list says). The four aria-labels (`Zurück`, `Weiter`, `Verkleinern`, `Vergrößern`) are on lines with `aria-label="..."` on icon-only buttons. `DocumentViewer.svelte` exists but is not where these strings are. Fix the critical files list before anyone starts implementing. - `AppNav.svelte:87` confirmed: `aria-label={mobileNavOpen ? 'Menü schließen' : 'Menü öffnen'}` — both states are hardcoded. - `TranscriptionEditView.svelte:242` confirmed: `<p class="mb-2 font-sans text-xs font-medium text-ink-2">Für Training vormerken</p>` — this is visible text, not just an aria-label. - `btn_back` already exists in `de.json` with value "Zurück", but it carries navigation-back semantics. Do **not** reuse it for the PDF page-prev button — `viewer_previous_page` is the right distinct key. - The discovery command `rg --type svelte` will silently find nothing: ripgrep doesn't know the `svelte` filetype by default. Use `rg -g '*.svelte'` or pass file paths directly. - All three JSONs are currently at exactly 908 lines, so keys are already in sync — the parity test guards against future drift, not a current problem. ### Recommendations - **Fix the critical files list**: replace `DocumentViewer.svelte` with `PdfControls.svelte`. - Fix the grep command: `rg -g '*.svelte' -g '*.ts' "Zurück|Weiter|Verkleinern|Vergrößern|Für Training|Menü öffnen|Menü schließen" frontend/src/` - TDD order: write the key parity test first (it should pass immediately — keys are in sync), then add all 7 keys to all three JSONs, then replace literals in the three component files. - `layout_menu_close` ("Menü schließen") is in `AppNav.svelte` but is absent from the proposed key list. Add it — that's violation #7 and it's already in the rg grep pattern in the issue body. - `PdfControls.svelte` already imports `m` from `$lib/paraglide/messages.js` and uses it for the annotation toggle (`m.pdf_annotations_hide()` / `m.pdf_annotations_show()`). The wiring pattern is already there — this is a mechanical substitution.
Author
Owner

🏛️ Markus Keller — Senior Application Architect

Observations

  • Pure frontend change. No backend, no DB migrations, no API surface touched. Blast radius is a handful of .svelte files and three JSON message files — as contained as it gets.
  • Key namespace prefixes (viewer_*, layout_*) are consistent with the existing conventions (nav_*, btn_*, form_*, pdf_*). They scale correctly — a reader navigating the JSON file can group keys by prefix without a map.
  • All three message files are currently in sync (908 lines each). The parity test is a guard against future drift, not a fix for a current gap — that framing matters for prioritizing it.
  • PdfControls.svelte already has the Paraglide import and uses m.pdf_annotations_*() — this component knows how to use the message system. There is no architectural discovery needed; it's a mechanical wiring job.

Recommendations

  • Keep viewer_previous_page and viewer_next_page as distinct keys from btn_back. They carry different semantics (document paging vs. UI navigation). Reusing btn_back would make a future rename or translation change ripple into an unrelated surface.
  • The key parity Vitest test belongs in a dedicated src/lib/messages.spec.ts file, not piggybacked onto an unrelated spec. The file doesn't yet exist; creating it is the right call.
  • No architecture concerns beyond the wrong filename in the critical files list. Fix that before implementation starts.
## 🏛️ Markus Keller — Senior Application Architect ### Observations - Pure frontend change. No backend, no DB migrations, no API surface touched. Blast radius is a handful of `.svelte` files and three JSON message files — as contained as it gets. - Key namespace prefixes (`viewer_*`, `layout_*`) are consistent with the existing conventions (`nav_*`, `btn_*`, `form_*`, `pdf_*`). They scale correctly — a reader navigating the JSON file can group keys by prefix without a map. - All three message files are currently in sync (908 lines each). The parity test is a guard against future drift, not a fix for a current gap — that framing matters for prioritizing it. - `PdfControls.svelte` already has the Paraglide import and uses `m.pdf_annotations_*()` — this component knows how to use the message system. There is no architectural discovery needed; it's a mechanical wiring job. ### Recommendations - Keep `viewer_previous_page` and `viewer_next_page` as distinct keys from `btn_back`. They carry different semantics (document paging vs. UI navigation). Reusing `btn_back` would make a future rename or translation change ripple into an unrelated surface. - The key parity Vitest test belongs in a dedicated `src/lib/messages.spec.ts` file, not piggybacked onto an unrelated spec. The file doesn't yet exist; creating it is the right call. - No architecture concerns beyond the wrong filename in the critical files list. Fix that before implementation starts.
Author
Owner

🔒 Nora "NullX" Steiner — Application Security Engineer

Observations

  • WCAG 3.1.2 ("Language of Parts") requires that all text content — including aria-label attributes — matches the declared page language. When locale is EN, aria-label="Zurück" on a button is a conformance failure for AT users. The issue correctly identifies this.
  • All violations are in aria-label attributes (PdfControls.svelte, AppNav.svelte) or a visible <p> (TranscriptionEditView.svelte). There is no auth surface, no session state, no server-side data exposure involved. Risk is purely accessibility compliance.
  • The fix is fully additive: new translation keys, no logic changes. No regression in auth or permission behavior possible.
  • ES translations are suggestions — the issue acknowledges this. "Marcar para entrenamiento" is acceptable but the qualifier matters (see UI/UX comment on "OCR training" vs "training").

Recommendations

  • Before wiring axe-core into a new test, check e2e/accessibility.spec.ts — it already exists. If axe-core is already imported and configured there, extend that file rather than creating a new integration point.
  • The Spanish translations should be reviewed by a native speaker before merge, especially "Acercar"/"Alejar" (these are correct for zoom but can read as "approach/move away" in some dialects — "Ampliar"/"Reducir" may be clearer in a family archive context).
  • No security concerns beyond WCAG compliance. This is a correctness fix.
## 🔒 Nora "NullX" Steiner — Application Security Engineer ### Observations - WCAG 3.1.2 ("Language of Parts") requires that all text content — including `aria-label` attributes — matches the declared page language. When locale is EN, `aria-label="Zurück"` on a button is a conformance failure for AT users. The issue correctly identifies this. - All violations are in `aria-label` attributes (`PdfControls.svelte`, `AppNav.svelte`) or a visible `<p>` (`TranscriptionEditView.svelte`). There is no auth surface, no session state, no server-side data exposure involved. Risk is purely accessibility compliance. - The fix is fully additive: new translation keys, no logic changes. No regression in auth or permission behavior possible. - ES translations are suggestions — the issue acknowledges this. "Marcar para entrenamiento" is acceptable but the qualifier matters (see UI/UX comment on "OCR training" vs "training"). ### Recommendations - Before wiring axe-core into a new test, check `e2e/accessibility.spec.ts` — it already exists. If axe-core is already imported and configured there, extend that file rather than creating a new integration point. - The Spanish translations should be reviewed by a native speaker before merge, especially "Acercar"/"Alejar" (these are correct for zoom but can read as "approach/move away" in some dialects — "Ampliar"/"Reducir" may be clearer in a family archive context). - No security concerns beyond WCAG compliance. This is a correctness fix.
Author
Owner

🧪 Sara Holt — Senior QA Engineer

Observations

  • Acceptance criteria are specific and testable — named strings, key-parity enforcement, locale-specific E2E assertions. Solid spec.
  • e2e/lang.spec.ts already demonstrates the locale-switching E2E pattern: click the EN button in the banner, verify the nav updates, use page.context().clearCookies({ name: 'PARAGLIDE_LOCALE' }) to reset. The new i18n test should reuse exactly this setup.
  • The E2E test needs a document with an attached PDF to navigate to the viewer and open the Transcribe panel. Confirm e2e/fixtures/ has such a fixture or that the test setup creates one — otherwise the test will be blocked by "no file attached" state.
  • layout_menu_close ("Menü schließen") is confirmed in AppNav.svelte:87 but is absent from the acceptance criteria. It's violation #7. The "≥ 6 strings" criterion should cover it, but it should be named explicitly so the implementer doesn't miss it.
  • The mobile drawer is only rendered at narrow viewport — the E2E test for "Menü öffnen/schließen" needs to set a mobile viewport width to make that button visible.

Recommendations

  • Extend the acceptance criterion to name all 7 strings explicitly: Zurück, Weiter, Verkleinern, Vergrößern, Für Training vormerken, Menü öffnen, Menü schließen.
  • Use role-based assertions in the E2E test: getByRole('button', { name: 'Previous page' }) rather than text matchers — this directly verifies the accessibility label is translated, not just that German text is absent.
  • For the "no new hardcoded literals" criterion, re-run the grep discovery command as the final verification step in the E2E test notes or CI check — it's the most direct enforcement mechanism.
  • The axe-core WCAG 3.1.2 check in e2e/accessibility.spec.ts should cover the EN and ES locale paths after the fix; confirm that file already sets the locale before running the axe scan.
## 🧪 Sara Holt — Senior QA Engineer ### Observations - Acceptance criteria are specific and testable — named strings, key-parity enforcement, locale-specific E2E assertions. Solid spec. - `e2e/lang.spec.ts` already demonstrates the locale-switching E2E pattern: click the EN button in the banner, verify the nav updates, use `page.context().clearCookies({ name: 'PARAGLIDE_LOCALE' })` to reset. The new i18n test should reuse exactly this setup. - The E2E test needs a document with an attached PDF to navigate to the viewer and open the Transcribe panel. Confirm `e2e/fixtures/` has such a fixture or that the test setup creates one — otherwise the test will be blocked by "no file attached" state. - `layout_menu_close` ("Menü schließen") is confirmed in `AppNav.svelte:87` but is absent from the acceptance criteria. It's violation #7. The "≥ 6 strings" criterion should cover it, but it should be named explicitly so the implementer doesn't miss it. - The mobile drawer is only rendered at narrow viewport — the E2E test for "Menü öffnen/schließen" needs to set a mobile viewport width to make that button visible. ### Recommendations - Extend the acceptance criterion to name all 7 strings explicitly: `Zurück`, `Weiter`, `Verkleinern`, `Vergrößern`, `Für Training vormerken`, `Menü öffnen`, `Menü schließen`. - Use role-based assertions in the E2E test: `getByRole('button', { name: 'Previous page' })` rather than text matchers — this directly verifies the accessibility label is translated, not just that German text is absent. - For the "no new hardcoded literals" criterion, re-run the grep discovery command as the final verification step in the E2E test notes or CI check — it's the most direct enforcement mechanism. - The axe-core WCAG 3.1.2 check in `e2e/accessibility.spec.ts` should cover the EN and ES locale paths after the fix; confirm that file already sets the locale before running the axe scan.
Author
Owner

🎨 Leonie Voss — Senior UX Designer & Accessibility Strategist

Observations

  • The four viewer violations (Zurück, Weiter, Verkleinern, Vergrößern) are aria-label values on icon-only buttons — invisible to sighted users but read by screen readers and shown as hover tooltips in most browsers. Translating them correctly is both a screen-reader fix and a tooltip fix.
  • "Für Training vormerken" in TranscriptionEditView.svelte:242 is visible text rendered in a <p> tag — directly in the UI for all users. This one is the most user-visible of the violations.
  • "Menü öffnen/schließen" is the aria-label on the mobile hamburger button — AT users on mobile currently get German regardless of locale.

Recommendations

  • For the icon-button labels, the proposed translations are correct (Previous page, Next page, Zoom out, Zoom in). Keep them as aria-labels — do not add visible text to these icon buttons.
  • For "Für Training vormerken": the existing German keys in the codebase consistently use "OCR-Training" as a qualifier (e.g., training_ocr_heading, ocr_stat_training_blocks). The EN translation should follow suit — "Mark for OCR training" rather than just "Mark for training" — otherwise users unfamiliar with the context won't know what "training" refers to.
  • For "Open menu": consider "Open navigation" instead of "Open menu" — screen readers announcing "button, Open navigation" gives better context than "button, Open menu" for users who can't see the hamburger icon. This is a minor but meaningful AT improvement.

Open Decisions

  • "Mark for training" vs "Mark for OCR training" (EN) / "Marcar para entrenamiento" vs "Marcar para entrenamiento de OCR" (ES) — the broader codebase uses "OCR-Training" as the qualifier consistently. Should this label follow suit for discoverability, or stay shorter? Depends on target user's domain familiarity — the younger generation this targets may or may not know what OCR is.
## 🎨 Leonie Voss — Senior UX Designer & Accessibility Strategist ### Observations - The four viewer violations (`Zurück`, `Weiter`, `Verkleinern`, `Vergrößern`) are `aria-label` values on **icon-only buttons** — invisible to sighted users but read by screen readers and shown as hover tooltips in most browsers. Translating them correctly is both a screen-reader fix and a tooltip fix. - "Für Training vormerken" in `TranscriptionEditView.svelte:242` is **visible text** rendered in a `<p>` tag — directly in the UI for all users. This one is the most user-visible of the violations. - "Menü öffnen/schließen" is the aria-label on the mobile hamburger button — AT users on mobile currently get German regardless of locale. ### Recommendations - For the icon-button labels, the proposed translations are correct (`Previous page`, `Next page`, `Zoom out`, `Zoom in`). Keep them as aria-labels — do not add visible text to these icon buttons. - For "Für Training vormerken": the existing German keys in the codebase consistently use "OCR-Training" as a qualifier (e.g., `training_ocr_heading`, `ocr_stat_training_blocks`). The EN translation should follow suit — "Mark for OCR training" rather than just "Mark for training" — otherwise users unfamiliar with the context won't know what "training" refers to. - For "Open menu": consider "Open navigation" instead of "Open menu" — screen readers announcing "button, Open navigation" gives better context than "button, Open menu" for users who can't see the hamburger icon. This is a minor but meaningful AT improvement. ### Open Decisions - **"Mark for training" vs "Mark for OCR training" (EN) / "Marcar para entrenamiento" vs "Marcar para entrenamiento de OCR" (ES)** — the broader codebase uses "OCR-Training" as the qualifier consistently. Should this label follow suit for discoverability, or stay shorter? Depends on target user's domain familiarity — the younger generation this targets may or may not know what OCR is.
Author
Owner

🚀 Tobias Wendt — DevOps & Platform Engineer

Observations

  • No infrastructure changes, no new npm dependencies, no config changes. The change touches only *.svelte components and messages/*.json files. Fully self-contained in frontend/.
  • The key parity Vitest test runs under npm run test — already part of CI.
  • The new E2E locale tests run under Playwright — already part of CI. e2e/lang.spec.ts already exercises the locale-switching flow successfully, so the test infrastructure is proven.

Recommendations

  • No devops concerns. Nothing to block or change from the platform side.
  • One thing to confirm: the E2E test for the mobile drawer toggle (Menü öffnen) needs a narrow-viewport Playwright context. Check that the Playwright config or the test itself sets viewport: { width: 375, height: 812 } for that specific assertion — otherwise the mobile button is never rendered and the test passes vacuously.
## 🚀 Tobias Wendt — DevOps & Platform Engineer ### Observations - No infrastructure changes, no new npm dependencies, no config changes. The change touches only `*.svelte` components and `messages/*.json` files. Fully self-contained in `frontend/`. - The key parity Vitest test runs under `npm run test` — already part of CI. - The new E2E locale tests run under Playwright — already part of CI. `e2e/lang.spec.ts` already exercises the locale-switching flow successfully, so the test infrastructure is proven. ### Recommendations - No devops concerns. Nothing to block or change from the platform side. - One thing to confirm: the E2E test for the mobile drawer toggle (`Menü öffnen`) needs a narrow-viewport Playwright context. Check that the Playwright config or the test itself sets `viewport: { width: 375, height: 812 }` for that specific assertion — otherwise the mobile button is never rendered and the test passes vacuously.
Author
Owner

📋 Elicit — Requirements Engineer

Observations

  • The issue is well-scoped with explicit non-goals. The acceptance criteria are testable and traceable to specific violations. This is one of the tighter i18n bug specs I've seen.
  • Scope gap: layout_menu_close ("Menü schließen") appears in AppNav.svelte:87 alongside layout_menu_open, but is not listed in the proposed keys, not in the acceptance criteria, and not in the verification steps. The issue body's own grep pattern includes it (Menü öffnen), but the open/close pair must both be translated together — an untranslated "Menü schließen" after the fix would leave 1 of 2 states broken.
  • The acceptance criterion "≥ 6 strings" is intentionally open-ended ("expand if rg finds more"), which is good — but the floor should be raised to 7 once layout_menu_close is confirmed as violation #7.
  • The "critical files" list references DocumentViewer.svelte as the location for viewer violations, but the actual violations are in PdfControls.svelte. The implementer will grep first and discover this anyway, but correcting it removes ambiguity.

Recommendations

  • Add layout_menu_close / "Menü schließen" to: (a) the proposed key list, (b) the acceptance criteria, (c) the verification walk-through.
  • Update "≥ 6 strings" → "≥ 7 strings (Zurück, Weiter, Verkleinern, Vergrößern, Für Training vormerken, Menü öffnen, Menü schließen)".
  • Correct DocumentViewer.sveltePdfControls.svelte in the critical files list.
  • The Implementation Plan Step 1 grep command uses rg --type svelte which is not a valid ripgrep built-in type. Suggest replacing with rg -g '*.svelte' to avoid a silent no-match.
## 📋 Elicit — Requirements Engineer ### Observations - The issue is well-scoped with explicit non-goals. The acceptance criteria are testable and traceable to specific violations. This is one of the tighter i18n bug specs I've seen. - **Scope gap**: `layout_menu_close` ("Menü schließen") appears in `AppNav.svelte:87` alongside `layout_menu_open`, but is not listed in the proposed keys, not in the acceptance criteria, and not in the verification steps. The issue body's own grep pattern includes it (`Menü öffnen`), but the open/close pair must both be translated together — an untranslated "Menü schließen" after the fix would leave 1 of 2 states broken. - The acceptance criterion "≥ 6 strings" is intentionally open-ended ("expand if rg finds more"), which is good — but the floor should be raised to 7 once `layout_menu_close` is confirmed as violation #7. - The "critical files" list references `DocumentViewer.svelte` as the location for viewer violations, but the actual violations are in `PdfControls.svelte`. The implementer will grep first and discover this anyway, but correcting it removes ambiguity. ### Recommendations - Add `layout_menu_close` / "Menü schließen" to: (a) the proposed key list, (b) the acceptance criteria, (c) the verification walk-through. - Update "≥ 6 strings" → "≥ 7 strings (Zurück, Weiter, Verkleinern, Vergrößern, Für Training vormerken, Menü öffnen, Menü schließen)". - Correct `DocumentViewer.svelte` → `PdfControls.svelte` in the critical files list. - The Implementation Plan Step 1 grep command uses `rg --type svelte` which is not a valid ripgrep built-in type. Suggest replacing with `rg -g '*.svelte'` to avoid a silent no-match.
Author
Owner

🗳️ Decision Queue — Action Required

1 decision needs your input before implementation starts.

UX / Translations

  • "Mark for training" vs "Mark for OCR training" in EN (and ES equivalent) — The existing German codebase consistently qualifies this feature with "OCR-Training" (training_ocr_heading, ocr_stat_training_blocks, error_training_already_running). The issue proposes just "Mark for training" / "Mark for training" for EN — but without the "OCR" qualifier, younger non-technical users won't know what they're marking the document for. Option A: "Mark for OCR training" (consistent with the codebase, more context); Option B: "Mark for training" (shorter, matches the issue's suggestion, arguably cleaner in the UI). (Raised by: Leonie)
## 🗳️ Decision Queue — Action Required _1 decision needs your input before implementation starts._ ### UX / Translations - **"Mark for training" vs "Mark for OCR training" in EN (and ES equivalent)** — The existing German codebase consistently qualifies this feature with "OCR-Training" (`training_ocr_heading`, `ocr_stat_training_blocks`, `error_training_already_running`). The issue proposes just "Mark for training" / "Mark for training" for EN — but without the "OCR" qualifier, younger non-technical users won't know what they're marking the document for. Option A: "Mark for OCR training" (consistent with the codebase, more context); Option B: "Mark for training" (shorter, matches the issue's suggestion, arguably cleaner in the UI). _(Raised by: Leonie)_
Sign in to join this conversation.
No Label P1-high bug ui
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#319