Multilingual editor (1/3): language switch, tree translation state, untranslated page, Translate page dialog #192
Labels
No labels
bug
enhancement
needs-triage
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#192
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
First of three editor slices of the multilingual work. Give the editor its language axis: the language list from
GET /api/site, a language switch in every module docheader with a status dot per language, translation state in the page tree, the untranslated-page screen, the Translate page dialog, and the guard that hides structural controls on a connected translation. The visual reference is round 2 of the mockup indocs/mockups/multilingual/(artifact: https://claude.ai/artifact/6RYbRG7y4yMyeGxYbFsh1n), reviewed and accepted by the maintainer on 2026-10-10 and committed in #198.The engine side landed in #193 (API documented in README "Translations on the wire"; every endpoint below exists), the theme in #196. The remaining editor screens are split into #199 (editing inside a translation) and #200 (page properties, page menu, pending changes, paste confirmation, Languages view, module lists). The PRD at the bottom is the contract for all three.
Current behavior
The editor carries the language in its URL (
/:module/:lang/*path,web/src/routing.ts), in the stores (PageId = {path, lang}) and in every API call, but it takes the one language from the tree response (ModuleContext.treeLang,web/src/modules.tsx) and never readsGET /api/site's language list. There is no switch, no translation state in the tree, and a non-default language's URL shows the page as if it were the default language's.GET /api/tree?lang=andGET /api/badges?lang=already take the language (web/src/api/tree.ts).Desired behavior
Every rule is phrased in terms of the default language (
languages[0]ofGET /api/site), never "English". Mockup: the EN · DE · FR switch, the tree in DE, Web design in DE, Translate page….Language list.
GET /api/siteanswers{title, languages: [{code, label}]}with at least one entry, the first being the default. The client reads it once at start (stores/modules.ts/ModuleContext) and exposeslanguages,defaultLang, andisDefault(lang).treeLangstops being the source of truth for the language; the URL segment is, validated against the list (an unknown code in the URL repairs to the default language,routerepair.ts).Language switch (
components/Docheader.tsx, every module): a segmented control labelled "Language", one segment per language inlanguagesorder showing the code, with a status dot for the current page: default (grey) · translated (green) · changed since translated (amber, treetranslation: "outdated") · unpublished changes (blue, the page's badge in that language) · not translated (hollow). Tooltip: the label and the state. Switching callsbuildRoutewith the new language and the same module and page: the URL's language segment changes, the page stays selected, the tree reloads for that language, the form's unsaved-changes guard runs first. Modules keep the language on switch as they do today. On a module screen without a selected page the segments show no dots. A one-language site shows the switch with one segment (or hides it; pick one and test it).Per-language state of the current page in the other languages comes from one-shot
fetchTree(lang)andfetchBadgesBody(lang)per configured language, cached in a store and refreshed on the same signal that refreshes the current tree (mutations, git sync, the tree poll's change detection); the tree node'stranslation("translated" | "missing" | "outdated") andl10n("connected" | "free") plus the badge state give the dot. Keep it simple: a site has a handful of languages. If this proves too heavy in practice, a per-page endpoint is an engine follow-up, not this issue.Page tree in a non-default language (
components/TreePane.tsx,TreeList.tsx,stores/pageTree.ts): the server already sends translated titles (title),enabledfrom the translation andaddress. Untranslated pages (translation: "missing") show their default-language title in italics with the default language's code as a small tag, muted, and stay selectable. A page disabled in this language uses the existing struck-through style. A legend at the bottom of the tree explains the dots (translated · changed since · unpublished · not translated). "+ New page" is disabled outside the default language with the hint "New pages are created in first; a translation cannot exist without its source"; drag reorder and move in the tree are disabled outside the default language too (the server answers 400 for create, create-root, move and reorder). Inline rename stays enabled: it renames the title of the current language's file. The tree header shows the language badge.Untranslated page screen (
GET /api/pageof a page without a translation answers 200{page, missing: true, type, title, file, source: {path, lang}};components/PageScreen.tsx): instead of the grid, two cards. Left: "“Multilingual sites: editor language switch, translation screens and Translate dialogto Multilingual editor (1/3): language switch, tree translation state, untranslated page, Translate page dialogRewritten on 2026-10-10 after the mockup review: round 2 of the mockup (PR #198) was reviewed and accepted, the engine (#193) and theme (#196) have landed, and the editor work is split into three landable slices. This issue is slice 1 (language switch, tree state, untranslated page, Translate page dialog) and is ready for an AFK agent; #199 and #200 follow, each blocked by the previous one. The PRD stays in this issue's body as the contract for all three.