fix(admin): replace hardcoded German strings with i18n keys (#7735)#7736
Conversation
…r#7735) PR ether#7716 ("chore: fixed admin design rework") rebuilt admin/src/pages with literal German copy inline — "Update verfügbar", "Aktualisieren", "Keine Pads gefunden", "Hook-Bindings", "de-DE" date formatters, etc. Non-DE users see a French/English/German salad: <Trans i18nKey="…"/> calls resolve correctly via translatewiki, but every literal stays German regardless of browser locale. This change: - Adds 90+ keys to src/locales/en.json under admin.*, admin_login.*, admin_pads.*, admin_plugins.*, admin_plugins_info.*, admin_settings.*, admin_shout.*, and the previously-orphaned update.page.{disabled, unauthorized,error}. - Replaces every hardcoded literal in admin/src/{App,LoginScreen, HomePage,HelpPage,PadPage,SettingsPage,ShoutPage,UpdatePage}.tsx with t() or <Trans>. - Threads i18n.language into PadPage so relativeTime() and toLocale*() honour the user's locale instead of forcing de-DE. Test coverage: - src/tests/backend-new/specs/admin-i18n-source-lint.test.ts (vitest): scans admin/src/pages/*.tsx + App.tsx for a denylist of German literals introduced by ether#7716, asserts PadPage no longer hardcodes 'de-DE', and pins the set of new en.json keys. - src/tests/frontend-new/admin-spec/admini18n.spec.ts (Playwright): extended to assert rendered English text on every page (Home, Pads, Help, Login) and verify no German leakage on the English path. Non-EN locales pick up translations from translatewiki on its normal cadence; until then i18next falls back to en.json. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one. |
Review Summary by QodoReplace hardcoded German strings with i18n keys in admin UI
WalkthroughsDescription• Replaces ~50 hardcoded German strings with i18n keys across admin pages • Adds 90+ translation keys to en.json for admin UI components • Threads i18n.language into PadPage for locale-aware date/time formatting • Adds source-level lint test to prevent German literal regressions • Extends Playwright admin spec with rendered text assertions Diagramflowchart LR
A["Admin Pages<br/>App, LoginScreen, HomePage,<br/>HelpPage, PadPage, etc."] -->|"Replace hardcoded<br/>German literals"| B["t() and Trans<br/>i18n calls"]
B -->|"Reference"| C["en.json<br/>90+ new keys"]
D["PadPage"] -->|"Thread i18n.language"| E["Locale-aware<br/>formatters"]
F["Source Lint Test<br/>admin-i18n-source-lint.test.ts"] -->|"Catch regressions"| G["CI validation"]
H["Playwright Spec<br/>admini18n.spec.ts"] -->|"Assert rendered<br/>English text"| I["Rendered output<br/>verification"]
File Changes1. src/tests/backend-new/specs/admin-i18n-source-lint.test.ts
|
Code Review by Qodo
Context used 1. Unsafe locale into Intl
|
Pre-rework admin already had:
ep_admin_pads:ep_adminpads2_action ("Action")
ep_admin_pads:ep_adminpads2_last-edited ("Last edited")
ep_admin_pads:ep_adminpads2_no-results ("No results")
Initial pass added admin_pads.{col.action, col.last_edited,
sort.last_edited, empty_state} duplicating those — drop the duplicates
from en.json and point PadPage.tsx at the existing translatewiki-fed
keys. Stats/column heads that genuinely didn't exist before
(admin_pads.col.{pad,users,revisions}, the filter chips, relative-time,
pagination, etc.) stay as new keys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Qodo flagged a reliability bug in PadPage on PR ether#7736: i18n.language flows from user-controlled ?lng= straight into Intl.* formatters, which throw RangeError on malformed tags (e.g. 'en_US', '💥'). Crashing the pads page on a crafted URL. Wrap the locale in a sanitizeLocale() helper that normalises '_' → '-' and validates via Intl.DateTimeFormat.supportedLocalesOf(), falling back to 'en' so dates render in a sane locale rather than the user's browser default fighting page copy. Same audit surfaced four additional regressions from ether#7716 still on develop, fixed here on-theme: - HomePage dropped <a href="https://npmjs.com/..."> wrappers on both installed and available plugin rows. Restored with .pm-plugin-link. - "Downloads" column / "Most popular" default sort / "Popular" tag were dead UI — src/static/js/pluginfw/installer.ts::search() never populates `downloads`. Removed the column, default sort, and tag; dropped `downloads` from PluginDef + SearchParams.sortBy. - PadPage sort dropdown hardcoded `ascending: e.target.value === 'padName'`, leaving no way to invert direction. Replaced with a paired ↑/↓ button (.pm-sort-dir) for both HomePage and PadPage. - "1 Core" stat hint hardcoded count=1. Derived from installedPlugins.filter(p => p.name === 'ep_etherpad-lite').length. - Deleted orphan modules SearchField.tsx and sorting.ts (no longer imported anywhere after ether#7716). Tests: - admin-i18n-source-lint.test.ts: +3 assertions (sanitizeLocale pattern, dead-downloads check, orphan-module deletion, sort-dir toggle) → 14 passing. - admini18n.spec.ts: +2 assertions (npmjs link on ep_etherpad-lite row, sort-direction toggle visible). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR ether#7716 ("admin design rework") shipped ~50 hardcoded German literals, dropped npmjs.com link affordances, removed the sort-direction control on PadPage, and forced `de-DE` into Intl formatters — none of which the AGENTS.MD guide explicitly forbade. Document the rules so the next UI refresh cannot regress these in the same way: - i18n section spells out which slots must be localised (JSX text, placeholders, titles, aria-labels, alts, toasts, options, alerts), which API to use per surface (<Trans>/t() in React, data-l10n-id in the legacy pad UI, never window._ rebound), where keys live (src/locales/en.json — never hand-edit non-EN locales), to reuse existing keys before duplicating, pluralisation via _one/_other, defaultValue is safety not a substitute, and points at the source-lint test that enforces the denylist. - a11y section spells out the lessons surfaced by the audit: icon-only buttons need aria-label AND title (both localised), sort controls must be focusable + reversible, semantic HTML over div soup, external navigation is <a>, "don't drop affordances when restyling" is a hard rule, Playwright specs must assert rendered strings + at least one structural affordance for UI changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#7666) Takes over ether#7666 / closes ether#7603. Squashed rebase of 32 commits onto current develop (which has since absorbed admin design rework ether#7716 and admin i18n fixes ether#7736 — granular history preserved on takeover/7666-admin-settings-editor before this squash, see PR description for the original commit log). Highlights: - New parsed JSONC settings editor under admin/src/components/settings/ — FormView, ModeToggle, ParseErrorBanner, JsoncNode dispatcher, leaf widgets (string, number, bool, null, env pill), and pure helpers (comments, envPill, jsoncEdit, labels, templateComments). - ${VAR:default} env placeholders render as editable inline inputs that round-trip through the raw textarea (env-pill spec asserts this; docker-template spec protects against form-view degradation on env-heavy configs). - Schema-driven help text sourced from settings.json.template, inlined at build time via vite (drops the runtime fs.allow widening that earlier iterations needed). - ModeToggle switches between FormView and raw textarea on /admin/settings; parse errors surface in a non-blocking banner. - jsonc-parser dep added; pure helpers wrap modify() for stable edits that preserve key order and trailing comments (stops at end-of-line so trailing-comment trains don't bleed into the next property). - i18n keys added for form mode, parse error, env pill, default_label, and input aria. - Playwright specs cover form view, env pill, parse error banner, raw round-trip, and form-mode regressions called out in ether#7666 review (stable React keys from AST offsets, save-toast on server ack only, NumberInput draft sync, parse-error flash during initial load, .settings CSS conflict resolution, focus retention via rAF, IconButton type defaulting to 'button'). Co-authored-by: Ayushi Gupta <ayushigupta36881@gmail.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rPage imports them PR ether#7736 added a lint assertion that admin/src/components/SearchField.tsx and admin/src/utils/sorting.ts must NOT exist, on the assumption they were dead code after ether#7716. They aren't — the GDPR AuthorPage (ether#7667) imports both. The previous commit restored the files; this commit flips the assertion so the test: - verifies both files exist - verifies admin/src/pages/AuthorPage.tsx still imports them If a future cleanup wants to delete these modules, the test now forces the author to also delete or refactor the AuthorPage consumption first, preventing a repeat of the merge-order accident that produced this bug. Test plan: - cd src && pnpm exec vitest run tests/backend-new/specs/admin-i18n-source-lint.test.ts → 14 passed (14) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…7746) * fix(admin): restore SearchField + sorting modules used by AuthorPage PR #7736 ("replace hardcoded German strings with i18n keys") deleted admin/src/components/SearchField.tsx and admin/src/utils/sorting.ts as "orphan modules (no longer imported anywhere after #7716)". At that point, the GDPR admin AuthorPage (PR #7667) had not yet landed on develop. When #7667 merged afterwards, the new admin/src/pages/AuthorPage.tsx brought back imports of SearchField and determineSorting — but the files were already gone, leaving develop with broken admin imports. This breaks `pnpm --filter admin run build-copy` on every CI job that builds the admin UI: src/pages/AuthorPage.tsx(6,27): error TS2307: Cannot find module '../components/SearchField.tsx' or its corresponding type declarations. src/pages/AuthorPage.tsx(9,32): error TS2307: Cannot find module '../utils/sorting.ts' or its corresponding type declarations. src/pages/AuthorPage.tsx(207,29): error TS7006: Parameter 'v' implicitly has an 'any' type. Backend tests, Frontend admin tests, Docker (build-test, build-test-local-plugin, build-test-db-drivers), rate limit, and Upgrade from latest release all fail at the admin build step on develop. Restore both files verbatim from before the deletion (commit ff7c4d5^). With them back in place AuthorPage.tsx's existing imports resolve, the implicit-any on the SearchField onChange callback goes away (typed via SearchFieldProps), and `pnpm --filter admin run build-copy` succeeds. This is the minimal, surgical fix; whether SearchField / determineSorting should ultimately be inlined into AuthorPage is a separate cleanup. Test plan: - cd admin && pnpm exec tsc --noEmit → no errors - cd admin && pnpm exec tsc && pnpm exec vite build → built in 545ms Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(admin-i18n-lint): invert orphan-modules assertion now that AuthorPage imports them PR #7736 added a lint assertion that admin/src/components/SearchField.tsx and admin/src/utils/sorting.ts must NOT exist, on the assumption they were dead code after #7716. They aren't — the GDPR AuthorPage (#7667) imports both. The previous commit restored the files; this commit flips the assertion so the test: - verifies both files exist - verifies admin/src/pages/AuthorPage.tsx still imports them If a future cleanup wants to delete these modules, the test now forces the author to also delete or refactor the AuthorPage consumption first, preventing a repeat of the merge-order accident that produced this bug. Test plan: - cd src && pnpm exec vitest run tests/backend-new/specs/admin-i18n-source-lint.test.ts → 14 passed (14) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#7666) Takes over ether#7666 / closes ether#7603. Squashed rebase of 32 commits onto current develop (which has since absorbed admin design rework ether#7716 and admin i18n fixes ether#7736 — granular history preserved on takeover/7666-admin-settings-editor before this squash, see PR description for the original commit log). Highlights: - New parsed JSONC settings editor under admin/src/components/settings/ — FormView, ModeToggle, ParseErrorBanner, JsoncNode dispatcher, leaf widgets (string, number, bool, null, env pill), and pure helpers (comments, envPill, jsoncEdit, labels, templateComments). - ${VAR:default} env placeholders render as editable inline inputs that round-trip through the raw textarea (env-pill spec asserts this; docker-template spec protects against form-view degradation on env-heavy configs). - Schema-driven help text sourced from settings.json.template, inlined at build time via vite (drops the runtime fs.allow widening that earlier iterations needed). - ModeToggle switches between FormView and raw textarea on /admin/settings; parse errors surface in a non-blocking banner. - jsonc-parser dep added; pure helpers wrap modify() for stable edits that preserve key order and trailing comments (stops at end-of-line so trailing-comment trains don't bleed into the next property). - i18n keys added for form mode, parse error, env pill, default_label, and input aria. - Playwright specs cover form view, env pill, parse error banner, raw round-trip, and form-mode regressions called out in ether#7666 review (stable React keys from AST offsets, save-toast on server ack only, NumberInput draft sync, parse-error flash during initial load, .settings CSS conflict resolution, focus retention via rAF, IconButton type defaulting to 'button'). Co-authored-by: Ayushi Gupta <ayushigupta36881@gmail.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#7666) Takes over ether#7666 / closes ether#7603. Squashed rebase of 32 commits onto current develop (which has since absorbed admin design rework ether#7716 and admin i18n fixes ether#7736 — granular history preserved on takeover/7666-admin-settings-editor before this squash, see PR description for the original commit log). Highlights: - New parsed JSONC settings editor under admin/src/components/settings/ — FormView, ModeToggle, ParseErrorBanner, JsoncNode dispatcher, leaf widgets (string, number, bool, null, env pill), and pure helpers (comments, envPill, jsoncEdit, labels, templateComments). - ${VAR:default} env placeholders render as editable inline inputs that round-trip through the raw textarea (env-pill spec asserts this; docker-template spec protects against form-view degradation on env-heavy configs). - Schema-driven help text sourced from settings.json.template, inlined at build time via vite (drops the runtime fs.allow widening that earlier iterations needed). - ModeToggle switches between FormView and raw textarea on /admin/settings; parse errors surface in a non-blocking banner. - jsonc-parser dep added; pure helpers wrap modify() for stable edits that preserve key order and trailing comments (stops at end-of-line so trailing-comment trains don't bleed into the next property). - i18n keys added for form mode, parse error, env pill, default_label, and input aria. - Playwright specs cover form view, env pill, parse error banner, raw round-trip, and form-mode regressions called out in ether#7666 review (stable React keys from AST offsets, save-toast on server ack only, NumberInput draft sync, parse-error flash during initial load, .settings CSS conflict resolution, focus retention via rAF, IconButton type defaulting to 'button'). Co-authored-by: Ayushi Gupta <ayushigupta36881@gmail.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#7666) Takes over ether#7666 / closes ether#7603. Squashed rebase of 32 commits onto current develop (which has since absorbed admin design rework ether#7716 and admin i18n fixes ether#7736 — granular history preserved on takeover/7666-admin-settings-editor before this squash, see PR description for the original commit log). Highlights: - New parsed JSONC settings editor under admin/src/components/settings/ — FormView, ModeToggle, ParseErrorBanner, JsoncNode dispatcher, leaf widgets (string, number, bool, null, env pill), and pure helpers (comments, envPill, jsoncEdit, labels, templateComments). - ${VAR:default} env placeholders render as editable inline inputs that round-trip through the raw textarea (env-pill spec asserts this; docker-template spec protects against form-view degradation on env-heavy configs). - Schema-driven help text sourced from settings.json.template, inlined at build time via vite (drops the runtime fs.allow widening that earlier iterations needed). - ModeToggle switches between FormView and raw textarea on /admin/settings; parse errors surface in a non-blocking banner. - jsonc-parser dep added; pure helpers wrap modify() for stable edits that preserve key order and trailing comments (stops at end-of-line so trailing-comment trains don't bleed into the next property). - i18n keys added for form mode, parse error, env pill, default_label, and input aria. - Playwright specs cover form view, env pill, parse error banner, raw round-trip, and form-mode regressions called out in ether#7666 review (stable React keys from AST offsets, save-toast on server ack only, NumberInput draft sync, parse-error flash during initial load, .settings CSS conflict resolution, focus retention via rAF, IconButton type defaulting to 'button'). Co-authored-by: Ayushi Gupta <ayushigupta36881@gmail.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…7709) * admin: parsed JSONC settings editor with form view (#7603, #7666) Takes over #7666 / closes #7603. Squashed rebase of 32 commits onto current develop (which has since absorbed admin design rework #7716 and admin i18n fixes #7736 — granular history preserved on takeover/7666-admin-settings-editor before this squash, see PR description for the original commit log). Highlights: - New parsed JSONC settings editor under admin/src/components/settings/ — FormView, ModeToggle, ParseErrorBanner, JsoncNode dispatcher, leaf widgets (string, number, bool, null, env pill), and pure helpers (comments, envPill, jsoncEdit, labels, templateComments). - ${VAR:default} env placeholders render as editable inline inputs that round-trip through the raw textarea (env-pill spec asserts this; docker-template spec protects against form-view degradation on env-heavy configs). - Schema-driven help text sourced from settings.json.template, inlined at build time via vite (drops the runtime fs.allow widening that earlier iterations needed). - ModeToggle switches between FormView and raw textarea on /admin/settings; parse errors surface in a non-blocking banner. - jsonc-parser dep added; pure helpers wrap modify() for stable edits that preserve key order and trailing comments (stops at end-of-line so trailing-comment trains don't bleed into the next property). - i18n keys added for form mode, parse error, env pill, default_label, and input aria. - Playwright specs cover form view, env pill, parse error banner, raw round-trip, and form-mode regressions called out in #7666 review (stable React keys from AST offsets, save-toast on server ack only, NumberInput draft sync, parse-error flash during initial load, .settings CSS conflict resolution, focus retention via rAF, IconButton type defaulting to 'button'). Co-authored-by: Ayushi Gupta <ayushigupta36881@gmail.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(admin): stabilise React keys to prevent focus loss in settings editor Switch React keys in JsoncNode and FormView from byte offsets to stable JSON paths (`getNodePath(...).join('.')`). Byte offsets shift on every keystroke because the edit changes the surrounding character count, which forces React to remount inputs and lose focus mid-typing. - Object children key on the property path. - Array elements key on their JSON path index. - Add a Playwright regression test pinning focus stability for array element edits. Co-authored-by: John McLear <john@mclear.co.uk> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(admin): stop trailing /* */ comments from bleeding into next key's label (#7740) In the parsed settings form view, each key's row was rendering its label as the previous keys' source lines concatenated together. Root cause: findLeading() in admin/src/components/settings/comments.ts treated any line ending in `*/` as a comment continuation, so a JSON line like "altF9": true, /* focus on the File Menu and/or editbar */ was absorbed into the next sibling's leading comment block, and then each subsequent key picked up an even longer accumulation. - Tighten findLeading's isComment check to only match structural comment lines (`//`, `/*`, or a `*`-prefixed continuation/close), so JSON code with a trailing block comment no longer matches. - Surface leading and trailing comments separately from the template map. Leaf rows with only a trailing same-line comment now render the humanized key as the row label and the comment as the help text below the control, matching settings.json.template's convention (and #7740's recommendation that "helper text should be below"). - Add unit tests pinning the regression and the JSDoc/`//` leading styles, plus a Playwright spec that asserts altC's row carries a clean label and the "focus on the Chat window" help text. Closes #7740. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ayushi Gupta <ayushigupta36881@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Closes #7735.
PR #7716 (admin design rework) shipped the new admin UI with ~50+ literal German strings baked into JSX. Existing
<Trans i18nKey="…"/>calls resolve to whatever language i18next detects (French, English, etc.), but the literals stay German — producing the "mix of French, English and German-ish" salad #7735 reports.This PR:
src/locales/en.json(admin.*,admin_login.*,admin_pads.*,admin_plugins.*,admin_plugins_info.*,admin_settings.*,admin_shout.*, and the previously-orphanedupdate.page.{disabled,unauthorized,error}).admin/src/{App,LoginScreen,HomePage,HelpPage,PadPage,SettingsPage,ShoutPage}.tsxwitht()or<Trans>.i18n.languageinto PadPage sorelativeTime()andtoLocale*()honour the user's locale instead of forcingde-DE.Non-EN locales pick up translations from translatewiki on its normal round-trip; until then i18next falls back to
en.json.Test plan
pnpm --filter ep_etherpad-lite vitest run tests/backend-new/specs/admin-i18n-source-lint.test.ts— 10 source-lint assertions (10 pass).pnpm --filter admin tsc --noEmit— clean.pnpm --filter admin vite build— builds.tests/frontend-new/admin-spec/admini18n.spec.ts) extended with rendered-text assertions for Home, Pads, Help, Login in EN; CI runs./admin/?lng=enand/admin/?lng=deafter merge.🤖 Generated with Claude Code