Summary
An automated audit of the frontend (at af65d697) found ~203 hardcoded English UI strings across 40 components, despite the app maintaining 4 locales (en, de, it, es). 216 of 256 scanned components are clean.
The root cause of the invisibility: npm run check:locales (scripts/check-locale-parity.js) only enforces key parity between locale files — it never checks whether components actually use translation keys. So a component can ship fully hardcoded and CI stays green.
LogManagementTab.tsx is a good reference for the target state (flat keys under a component namespace, {{var}} interpolation).
Method
Heuristic regex scan over frontend/src/{components,pages}/**/*.tsx (excluding stories/tests) for:
- JSX text nodes
- user-facing string props (
label=, helperText=, placeholder=, title=, alt=, tooltip=, …)
window.confirm / alert messages
toast.* messages
- UI strings in ternaries (
{cond ? 'Copied!' : 'Copy to clipboard'})
with filtering for TS generics artifacts, Tailwind/sx values, CSS, type names, and non-UI values. Both wiring styles were recognized as "translated": useTranslation() and t: TFunction passed as a prop (e.g. SourceSelectionDialog.tsx is fully translated via the prop style).
Counts are a ranking tool, not an exact work order — expect ~10% noise in both directions (strings built in .ts helpers weren't scanned; a few counted strings like "Linux" or plan names may legitimately stay untranslated).
Findings, ranked
Tier 1 — Managed Agents surface (109 hits, 54% of the total)
| File |
Hits |
Notes |
pages/ManagedAgents.tsx |
81 (60 unique) |
imports useTranslation, but the managedAgents namespace contains exactly 1 key — the page was wired but never translated |
pages/managed-agents/AddAgentDialog.tsx |
22 |
no i18n wiring at all |
pages/managed-agents/AgentInstallCommand.tsx |
6 |
no i18n wiring at all |
Examples: "Add Agent", "Enrollment token created" (toast), "Deleting removes it from the fleet list…", "Run this on the Linux machine that owns the files…", the 4-step "Agent Setup Help", "TCP reachable"/"TCP failed".
Tier 2 — API tokens & account/auth (32 hits)
| File |
Hits |
components/ApiTokensSection.tsx |
19 — full token-reveal flow ("Copy this token now. You won't be able to see it again.", "Close without copying?") |
components/AccountPasswordDialog.tsx |
7 — all form labels |
pages/FirstLoginPasswordSetup.tsx |
4 — "Hide/Show password" |
pages/Login.tsx |
2 — same pair |
Tier 3 — shared wizard & layout components (~28 hits)
WizardStepReview (5, "Hide/Show passphrase", "Ready to Initialize"), MountSuccessToast (4, "Copied!"/"Copy command"), WizardStepLocation (3), WizardStepIndicator (3), RepositoryEncryptionFields, WizardStepScheduleReview, BackupPlanSelect ("Scheduled"/"Manual"), PlanScheduleCard + UnassignedReposSection ("Collapse"/"Expand"), NavGroup, RepoMenuItem, RepositoryWizard, and 5 more wizard steps with 1 hit each.
Tier 4 — long tail (~34 hits, ~17 files)
SystemKeyCard (5), CacheManagementTab (3), ScheduledChecksSection, ArchivesList, AuthLayout, AppHeader, AnnouncementModal, Scripts (2 each), plus 1-hit files — including two untranslated window.confirm sentences (pages/Repositories.tsx "Are you sure you want to delete repository…", and one in RunningBackupsSection).
Effort estimate
~180–195 new en keys (≈ 720–780 locale lines across the 4 files):
| Batch |
New keys |
managedAgents.* |
~80–85 |
apiTokens.* + account/auth |
~28 |
| shared wizard/layout |
~25 |
| long tail |
~35 |
common.* staples |
~10 |
A side finding: common has only 30 keys and is missing even Cancel and Close (actions, status, name exist). "Cancel" alone is hardcoded in 4 different files — repeated strings should route through common.* instead of being duplicated per namespace.
Suggested plan
Fix in 4 independent, reviewable PRs following the logManagement.* pattern, each keeping check:locales green:
- Managed Agents surface (~85 keys, 3 files) — biggest win, one coherent feature area; also adds the missing
common.* staples.
- API tokens + account security (~28 keys, 4 files).
- Shared wizard/layout components (~25 keys, ~15 files).
- Long-tail sweep (~35 keys, ~17 files), including the
window.confirm sentences (interpolated via {{name}}).
And to make this class of debt visible to CI permanently:
- Add
eslint-plugin-i18next (i18next/no-literal-string) scoped to src/components + src/pages. Without it, check:locales keeps passing while new hardcoded strings accrue — which is exactly how ManagedAgents.tsx reached 81 strings after being wired for i18n.
I'm happy to send these PRs one at a time as the review queue allows — filing the audit first so the scope is agreed before any code moves.
Summary
An automated audit of the frontend (at
af65d697) found ~203 hardcoded English UI strings across 40 components, despite the app maintaining 4 locales (en,de,it,es). 216 of 256 scanned components are clean.The root cause of the invisibility:
npm run check:locales(scripts/check-locale-parity.js) only enforces key parity between locale files — it never checks whether components actually use translation keys. So a component can ship fully hardcoded and CI stays green.LogManagementTab.tsxis a good reference for the target state (flat keys under a component namespace,{{var}}interpolation).Method
Heuristic regex scan over
frontend/src/{components,pages}/**/*.tsx(excluding stories/tests) for:label=,helperText=,placeholder=,title=,alt=,tooltip=, …)window.confirm/alertmessagestoast.*messages{cond ? 'Copied!' : 'Copy to clipboard'})with filtering for TS generics artifacts, Tailwind/
sxvalues, CSS, type names, and non-UI values. Both wiring styles were recognized as "translated":useTranslation()andt: TFunctionpassed as a prop (e.g.SourceSelectionDialog.tsxis fully translated via the prop style).Counts are a ranking tool, not an exact work order — expect ~10% noise in both directions (strings built in
.tshelpers weren't scanned; a few counted strings like"Linux"or plan names may legitimately stay untranslated).Findings, ranked
Tier 1 — Managed Agents surface (109 hits, 54% of the total)
pages/ManagedAgents.tsxuseTranslation, but themanagedAgentsnamespace contains exactly 1 key — the page was wired but never translatedpages/managed-agents/AddAgentDialog.tsxpages/managed-agents/AgentInstallCommand.tsxExamples: "Add Agent", "Enrollment token created" (toast), "Deleting removes it from the fleet list…", "Run this on the Linux machine that owns the files…", the 4-step "Agent Setup Help", "TCP reachable"/"TCP failed".
Tier 2 — API tokens & account/auth (32 hits)
components/ApiTokensSection.tsxcomponents/AccountPasswordDialog.tsxpages/FirstLoginPasswordSetup.tsxpages/Login.tsxTier 3 — shared wizard & layout components (~28 hits)
WizardStepReview(5, "Hide/Show passphrase", "Ready to Initialize"),MountSuccessToast(4, "Copied!"/"Copy command"),WizardStepLocation(3),WizardStepIndicator(3),RepositoryEncryptionFields,WizardStepScheduleReview,BackupPlanSelect("Scheduled"/"Manual"),PlanScheduleCard+UnassignedReposSection("Collapse"/"Expand"),NavGroup,RepoMenuItem,RepositoryWizard, and 5 more wizard steps with 1 hit each.Tier 4 — long tail (~34 hits, ~17 files)
SystemKeyCard(5),CacheManagementTab(3),ScheduledChecksSection,ArchivesList,AuthLayout,AppHeader,AnnouncementModal,Scripts(2 each), plus 1-hit files — including two untranslatedwindow.confirmsentences (pages/Repositories.tsx"Are you sure you want to delete repository…", and one inRunningBackupsSection).Effort estimate
~180–195 new
enkeys (≈ 720–780 locale lines across the 4 files):managedAgents.*apiTokens.*+ account/authcommon.*staplesA side finding:
commonhas only 30 keys and is missing evenCancelandClose(actions,status,nameexist). "Cancel" alone is hardcoded in 4 different files — repeated strings should route throughcommon.*instead of being duplicated per namespace.Suggested plan
Fix in 4 independent, reviewable PRs following the
logManagement.*pattern, each keepingcheck:localesgreen:common.*staples.window.confirmsentences (interpolated via{{name}}).And to make this class of debt visible to CI permanently:
eslint-plugin-i18next(i18next/no-literal-string) scoped tosrc/components+src/pages. Without it,check:localeskeeps passing while new hardcoded strings accrue — which is exactly howManagedAgents.tsxreached 81 strings after being wired for i18n.I'm happy to send these PRs one at a time as the review queue allows — filing the audit first so the scope is agreed before any code moves.