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>
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