[GH-ISSUE #1040] [Feature]: Audit and fix hardcoded user-visible strings across the frontend (i18n parity) #735

Closed
opened 2026-05-06 12:32:24 +02:00 by BreizhHardware · 2 comments

Originally created by @Minidoracat on GitHub (Apr 19, 2026).
Original GitHub issue: https://github.com/maziggy/bambuddy/issues/1040

Originally assigned to: @maziggy on GitHub.

Problem or Use Case

CONTRIBUTING.md states: "Never hardcode user-visible strings — always use translation keys." A scan of the current frontend/src/ tree shows that rule is being violated in a lot of places — users on non-English locales see English text bleeding through even with their locale fully populated.

Two independent scans:

  • AST scan (ESLint via eslint-plugin-i18next, mode: 'jsx-text-only') → 465 hardcoded JSX text nodes across 76 files.
  • ripgrep multi-pattern scan for placeholder / title / aria-label / alt / throw new Error(...)~100 additional attribute + error-message hits across ~40 files.

Lower-bound total: ~560 occurrences across 80+ files. Actual number is likely higher (toast/alert callees and template literals not counted). Highest-density files:

  • pages/PrintersPage.tsx (63)
  • pages/SettingsPage.tsx (39)
  • pages/ArchivesPage.tsx (37)
  • components/PrintModal/PrinterSelector.tsx (26)
  • components/FileManagerModal.tsx (16)
  • components/APIBrowser.tsx (14)
  • components/TimelapseEditorModal.tsx (14)
  • components/CompareArchivesModal.tsx (12)
  • components/ProjectPageModal.tsx (12)
  • components/LogViewer.tsx (11)

Concrete examples:

  • CompareArchivesModal.tsx:53<p>Failed to load comparison</p>
  • APIBrowser.tsx:337<h4>Path Parameters</h4>
  • APIBrowser.tsx:585<Button>Expand All</Button>
  • AddExternalLinkModal.tsx:197<label>Name *</label>
  • PendingUploadsPanel.tsx:130-150<label>Tags</label> / Notes / Project
  • FileManagerModal.tsx:152<p>All Plates</p>
  • CalendarView.tsx:192<div>Prints this month</div>

Scan methodology is reproducible — happy to share the exact ripgrep patterns and ESLint flat-config in the first PR description if useful.

Proposed Solution

A series of scoped PRs (not one mega-PR) grouped by feature area. Each PR:

  1. Extracts user-visible strings into keyed namespaces (printers.*, archives.*, settings.*, …), wraps call sites with useTranslation() + t('...').
  2. Adds matching keys to all 8 locale files (en, de, fr, it, ja, pt-BR, zh-CN, zh-TW) so the parity gate (check:i18n, already on dev) stays green.
  3. English is source of truth. zh-CN / zh-TW native-speaker quality from me.
  4. Includes before/after screenshots per batch (per CONTRIBUTING.md).

Rough batching I have in mind (open to reshaping — happy to collapse, split, or re-order based on what you prefer to review first):

  • B1 — Printers & Print Modal (PrintersPage, PrintModal/*, PrinterSelector, SwitchbarPopover, SmartPlugCard)
  • B2 — Archives (ArchivesPage, CompareArchivesModal, BatchProjectModal, ProjectPageModal, ProjectDetailPage)
  • B3 — Settings surface (SettingsPage, LDAPSettings, EmailSettings, OIDCProviderSettings, ExternalLinksSettings, SpoolmanSettings, TwoFactorSettings)
  • B4 — File manager + calendar + dashboard
  • B5 — SpoolBuddy
  • B6 — Misc modals & overlays (LogViewer, TimelapseEditorModal, EmbeddedCameraViewer, PhotoGalleryModal, TagManagementModal, QRCodeModal, KeyboardShortcutsModal, …)
  • B7 — Profiles + inventory + stats + maintenance
  • B8 (follow-up) — add eslint-plugin-i18next's no-literal-string rule (warn-level) to frontend/eslint.config.js so this cannot regress

Happy to start with B1 as a proof-of-shape and let you decide whether to keep going before I commit to the full cleanup.

Open questions I'd appreciate your call on before I start:

  1. Scope of dev-facing pages — should APIBrowser and LogViewer be included, or stay English-only since they're developer/diagnostic surfaces?
  2. Translation policy for de / fr / it / ja / pt-BR — would you prefer (a) best-effort translations from me now, or (b) placeholder entries tagged TODO-translate so native speakers can fill them in follow-up PRs?
  3. Batch granularity — does 7-8 PRs feel right, or would you rather I collapse into 3-4 larger ones, or start smaller (just B1 + feedback)?

Alternatives Considered

  • Leave as-is — violates CONTRIBUTING.md's explicit rule and degrades the UX for non-English users even when their locale is complete.
  • Runtime machine translation — lossy, doesn't address the underlying hardcoded-string debt.
  • Lint rule only (no cleanup) — prevents new regressions but doesn't fix the existing 500+ hits, and the rule would flag so many existing violations it'd have to start at off.
  • One giant PR — ~500+ lines of churn across 80+ files, effectively unreviewable.

Feature Category

UI/UX

Priority

Would improve my workflow

Contribution

  • I would be willing to help implement this feature

Checklist

  • I have searched existing issues to ensure this feature hasn't already been requested
Originally created by @Minidoracat on GitHub (Apr 19, 2026). Original GitHub issue: https://github.com/maziggy/bambuddy/issues/1040 Originally assigned to: @maziggy on GitHub. ### Problem or Use Case `CONTRIBUTING.md` states: *"Never hardcode user-visible strings — always use translation keys."* A scan of the current `frontend/src/` tree shows that rule is being violated in a lot of places — users on non-English locales see English text bleeding through even with their locale fully populated. Two independent scans: - **AST scan** (ESLint via `eslint-plugin-i18next`, `mode: 'jsx-text-only'`) → **465 hardcoded JSX text nodes across 76 files**. - **ripgrep multi-pattern scan** for `placeholder` / `title` / `aria-label` / `alt` / `throw new Error(...)` → **~100 additional attribute + error-message hits across ~40 files**. Lower-bound total: ~560 occurrences across 80+ files. Actual number is likely higher (toast/alert callees and template literals not counted). Highest-density files: - `pages/PrintersPage.tsx` (63) - `pages/SettingsPage.tsx` (39) - `pages/ArchivesPage.tsx` (37) - `components/PrintModal/PrinterSelector.tsx` (26) - `components/FileManagerModal.tsx` (16) - `components/APIBrowser.tsx` (14) - `components/TimelapseEditorModal.tsx` (14) - `components/CompareArchivesModal.tsx` (12) - `components/ProjectPageModal.tsx` (12) - `components/LogViewer.tsx` (11) Concrete examples: - `CompareArchivesModal.tsx:53` — `<p>Failed to load comparison</p>` - `APIBrowser.tsx:337` — `<h4>Path Parameters</h4>` - `APIBrowser.tsx:585` — `<Button>Expand All</Button>` - `AddExternalLinkModal.tsx:197` — `<label>Name *</label>` - `PendingUploadsPanel.tsx:130-150` — `<label>Tags</label>` / `Notes` / `Project` - `FileManagerModal.tsx:152` — `<p>All Plates</p>` - `CalendarView.tsx:192` — `<div>Prints this month</div>` Scan methodology is reproducible — happy to share the exact ripgrep patterns and ESLint flat-config in the first PR description if useful. ### Proposed Solution A **series of scoped PRs** (not one mega-PR) grouped by feature area. Each PR: 1. Extracts user-visible strings into keyed namespaces (`printers.*`, `archives.*`, `settings.*`, …), wraps call sites with `useTranslation()` + `t('...')`. 2. Adds matching keys to **all 8 locale files** (`en`, `de`, `fr`, `it`, `ja`, `pt-BR`, `zh-CN`, `zh-TW`) so the parity gate (`check:i18n`, already on `dev`) stays green. 3. English is source of truth. `zh-CN` / `zh-TW` native-speaker quality from me. 4. Includes before/after screenshots per batch (per CONTRIBUTING.md). Rough batching I have in mind (open to reshaping — happy to collapse, split, or re-order based on what you prefer to review first): - B1 — Printers & Print Modal (`PrintersPage`, `PrintModal/*`, `PrinterSelector`, `SwitchbarPopover`, `SmartPlugCard`) - B2 — Archives (`ArchivesPage`, `CompareArchivesModal`, `BatchProjectModal`, `ProjectPageModal`, `ProjectDetailPage`) - B3 — Settings surface (`SettingsPage`, `LDAPSettings`, `EmailSettings`, `OIDCProviderSettings`, `ExternalLinksSettings`, `SpoolmanSettings`, `TwoFactorSettings`) - B4 — File manager + calendar + dashboard - B5 — SpoolBuddy - B6 — Misc modals & overlays (`LogViewer`, `TimelapseEditorModal`, `EmbeddedCameraViewer`, `PhotoGalleryModal`, `TagManagementModal`, `QRCodeModal`, `KeyboardShortcutsModal`, …) - B7 — Profiles + inventory + stats + maintenance - B8 (follow-up) — add `eslint-plugin-i18next`'s `no-literal-string` rule (warn-level) to `frontend/eslint.config.js` so this cannot regress Happy to start with B1 as a proof-of-shape and let you decide whether to keep going before I commit to the full cleanup. **Open questions I'd appreciate your call on before I start:** 1. **Scope of dev-facing pages** — should `APIBrowser` and `LogViewer` be included, or stay English-only since they're developer/diagnostic surfaces? 2. **Translation policy for `de / fr / it / ja / pt-BR`** — would you prefer (a) best-effort translations from me now, or (b) placeholder entries tagged `TODO-translate` so native speakers can fill them in follow-up PRs? 3. **Batch granularity** — does 7-8 PRs feel right, or would you rather I collapse into 3-4 larger ones, or start smaller (just B1 + feedback)? ### Alternatives Considered - **Leave as-is** — violates CONTRIBUTING.md's explicit rule and degrades the UX for non-English users even when their locale is complete. - **Runtime machine translation** — lossy, doesn't address the underlying hardcoded-string debt. - **Lint rule only (no cleanup)** — prevents new regressions but doesn't fix the existing 500+ hits, and the rule would flag so many existing violations it'd have to start at `off`. - **One giant PR** — ~500+ lines of churn across 80+ files, effectively unreviewable. ### Feature Category UI/UX ### Priority Would improve my workflow ### Contribution - [x] I would be willing to help implement this feature ### Checklist - [x] I have searched existing issues to ensure this feature hasn't already been requested
BreizhHardware 2026-05-06 12:32:24 +02:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@maziggy commented on GitHub (Apr 20, 2026):

Oh this is awesome! Have it on my list for a long time but simly don't found the time to fix it.

Please go ahead. Thanks!!!

<!-- gh-comment-id:4278323780 --> @maziggy commented on GitHub (Apr 20, 2026): Oh this is awesome! Have it on my list for a long time but simly don't found the time to fix it. Please go ahead. Thanks!!!
Author
Owner

@maziggy commented on GitHub (Apr 29, 2026):

Funny, but I already fixed it by error :) Sorry for that.

Locale leaf counts:
en 4492 [ref]
de 4492 [locale]
fr 4492 [locale]
it 4492 [locale]
ja 4492 [locale]
pt-BR 4492 [locale]
zh-CN 4492 [locale]
zh-TW 4492 [locale]

✓ All locales in parity with en (de / fr / it / ja / pt-BR / zh-CN / zh-TW).

<!-- gh-comment-id:4342143329 --> @maziggy commented on GitHub (Apr 29, 2026): Funny, but I already fixed it by error :) Sorry for that. Locale leaf counts: en 4492 [ref] de 4492 [locale] fr 4492 [locale] it 4492 [locale] ja 4492 [locale] pt-BR 4492 [locale] zh-CN 4492 [locale] zh-TW 4492 [locale] ✓ All locales in parity with en (de / fr / it / ja / pt-BR / zh-CN / zh-TW).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/bambuddy#735
No description provided.