fix(web-ui): replace hardcoded German UI strings with next-intl translations - #447
Merged
Merged
Conversation
…lations Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Conflicts in web-ui/messages/{en,de}.json: both sides appended keys to
admin.builder — kept our apiError key and upstream's persona block (byte5ai#411).
Verified: i18n:check OK (2535 keys), tsc clean, eslint 0 errors, 184/184 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codifies what PR byte5ai#447 fixed so it stays fixed: no user-facing literals in components, German only in de.json, error messages via catalog keys, no fixed-locale formatting, a pre-commit grep self-check, and the list of deliberate exceptions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Weegy
enabled auto-merge (squash)
July 6, 2026 08:33
Weegy
added a commit
that referenced
this pull request
Aug 13, 2026
…surfaced (#471) * feat(deploy): one-click Render blueprint + one-command Fly.io deploy render.yaml backs a 'Deploy to Render' button: middleware + admin UI from the published GHCR images, managed Postgres 17 (pgvector), a persistent /data disk, and a generated VAULT_KEY — the /setup wizard collects the LLM key at runtime, so a deploy needs zero pasted secrets. MIDDLEWARE_URL reaches the web-ui via fromService/envVarKey RENDER_EXTERNAL_URL. Fly has no blueprint-style button, so fly/deploy.sh is the one-command equivalent: three apps (pgvector/pgvector:pg17 private-only, middleware with /data volume, web-ui), volumes, and generated secrets. Fly's own Postgres offerings don't fit (postgres-flex ships without pgvector, Managed Postgres gates the vector extension behind a dashboard toggle), hence the same pgvector image the compose stack uses. Smoke-tested end to end on a live Fly org. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web-ui): resolve MIDDLEWARE_URL at request time — route handlers replace baked rewrites Next evaluates rewrites() at build time and freezes the destinations into routes-manifest.json. The published web-ui image was built with ARG MIDDLEWARE_URL=http://middleware:8080, so every browser call through /bot-api proxied to a hostname that only exists on the compose network — on Fly/Render/any other host the proxy died with ENOTFOUND and every operator page showed 'GET … failed: 500'. Runtime MIDDLEWARE_URL was silently ignored (only server-side fetches honored it, which is why page shells still rendered). /bot-api/* and /p/* are now catch-all route handlers backed by app/_lib/middlewareProxy.ts, resolving MIDDLEWARE_URL per request: streaming both directions (SSE, plugin-ZIP uploads), multiple Set-Cookie preserved, redirects passed through, hop-by-hop headers stripped. One image now runs anywhere; the compose default still works because compose sets the same value as runtime env. Consequences swept: Dockerfile build ARG removed (with a warning against reintroducing it), CI workflows no longer pass the inert build-arg, the compose build overlay drops it, and desktop-apps.yml stops pretending to bake the kernel URL — the desktop supervisor already injects it at spawn (this also fixes the latent wrong-port proxy when the kernel doesn't get its preferred port). Verified: 6 regression tests (core assertion: same handler, changed env, changed target), plus a live Fly deployment where /bot-api/v1/auth/providers went 500 → 200 and login started working. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(web-ui): first-run readiness banner; fix 17 broken t.rich placeholders; unbreak next dev Three first-run findings from smoke-testing the one-click deploy: 1. Readiness banner. A fresh install has no LLM key, so the orchestrator never publishes chatAgent@1/orchestratorRegistry@1 and every operator surface answers 503 multi_orchestrator_unavailable — the UI showed raw 'GET … failed: 503' strings with no hint at the cause. RuntimeReadinessBanner (mounted next to SessionWatcher) probes one operator route, and on the structured 503 shows a card naming the cause with a link to /admin/settings. Re-probes on focus + heartbeat while visible, so it clears itself the moment the key is saved. 2. t.rich placeholder syntax (regression from #447). 17 placeholders across 12 message keys used ICU argument syntax ({toolName}) while the component passes chunk functions — next-intl splices the raw function into the render output and React throws ('Functions cannot be passed to Client Components'), hard-crashing the routines page, login no-providers state, onboarding modal, builder preview, and four admin pages whenever those paths render. The locale-parity check couldn't catch it because both locales were consistently wrong. All converted to tag syntax (<toolName></toolName>) in en+de; audited to zero via a t.rich/message cross-check sweep. 3. next dev was 500ing on every page: Tailwind v4 scans comments as class candidates, and two test-file comments containing a literal bracket-class with var(...) compiled into invalid CSS (dev-only; prod builds tolerate it). Reworded the comments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(web-ui): use the canonical Lume Button in RuntimeReadinessBanner main's no-restricted-syntax rule (the Lume button ratchet, #616) landed after this branch was written and rejects the raw <button> the banner used for its dismiss action. Migrated to <Button variant="secondary">, matching ConfirmDialog's precedent for a cancel-style affordance; the uppercase/tracking classes are kept so it still reads as a pair with the adjacent settings link. web-ui lint: 0 errors (was 1). --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Marcel Wege <mwege@byte5.de>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
English is the default locale, but large parts of the web UI showed hardcoded German regardless of the selected language. This PR converts all user-visible hardcoded German strings to next-intl translations, per the i18n conventions in
web-ui/CLAUDE.md.messages/en.json+messages/de.json): natural English written foren, original German preserved/polished inde. Catalog now totals 2457 keys.t.richwhere text embeds JSX.labelDe/descriptionDefields (moved to catalog keys); persona conflict messages return amessageKeytranslated at render; German validation errors inzodSchemaForToolSpec.ts/openapiToTools.tsnow use the documented TFn pattern.Deliberately left untouched
app/global-error.tsx— intentionally bilingual; renders when the intl provider itself failedMOCK_KG_WALKdev fixture (behind?kgmock=1)personaTemplates.ts,toolTemplates.ts,composeFixPrompt.ts) — not UI chromeVerification
npm run i18n:check✅ (catalog parity, ICU placeholder consistency)tsc --noEmit✅eslint .✅ 0 errors (22 warnings, all pre-existing — verified against pre-change baseline)vitest run✅ 173/173Follow-up (out of scope)
Several pages hardcode
toLocaleString('de-DE')/Europe/Berlinfor number/date formatting; these should move to next-intl's locale-aware formatters in a separate PR.🤖 Generated with Claude Code