Multilingual editor (2/3): Columns view of a translation, Translate element, element form with source values #199
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#199
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
Second of three editor slices of the multilingual work (contract: the PRD in #192, section 8; visual reference:
docs/mockups/multilingual/, artifact https://claude.ai/artifact/6RYbRG7y4yMyeGxYbFsh1n). This slice is editing inside a translation: the Columns view of a connected translation (translated cards, placeholder rows, badges, banners, no structural controls), Translate element / Translate all, the free-translation grid, the element form with the default-language value under every translatable field, and the delete confirmation that names affected translations.Builds on #192 (language switch, tree state, untranslated-page screen, Translate page dialog). Follow-up: #200 (page properties, page menu, pending changes, paste confirmation, Languages view, module lists).
Current behavior
After #192 a translated page opens in the ordinary grid: its elements are cards, insert rows and element operations are hidden when the page answer says
translation.mode: "connected", nothing marks untranslated or changed elements, and the element form is the default-language form. The API (README "Translations on the wire") already answers everything below; nothing in the engine changes.Desired behavior
Every rule is phrased in terms of the default language (
languages[0]ofGET /api/site), never "English". Mockup: Services in Deutsch, the Cards element, Home in Deutsch (free) and Français (connected), About us in Deutsch.Columns view of a connected translation (
GET /api/pagein a non-default language answerstranslation: {mode: "connected", source: {path, lang}, changed: [uid], untranslated: [uid], synced: {<element name>: [param]}}; the server repairs the structure to the source's on open):untranslatedis a placeholder row at its source position (source order within the slot: fetch the source page's elements withGET /api/page/elements?path=&lang=<default>and interleave), showing the element label, uid, the Disabled badge when the source element is disabled, and "Not translated · :POST /api/element/translate {page, lang, uid}, then open the new element's form. An element whose parent is untranslated answers 409 "translate the parent first": show the server's message.changedcarries the " changed" badge (amber) on its card.POST /api/page/enabled) — this one replaces today's disabled-page banner in a non-default language; (3) "N of M elements are not translated yet…" with Translate all (POST /api/element/translate-all {page, lang}, then re-render; the answer'stranslatedcount goes into the toast). The Content tab count readstranslated/totalin a translation.translation.mode: "free"): the ordinary grid with insert rows and every element operation, exactly as a source page, plus a "Free translation" badge in the page docheader. No change tracking.Delete on a source page names the translations deleted with it: the element delete confirmation calls
GET /api/element/delete-plan?page=&lang=&uid=and, whentranslationsis non-empty, says "Its translation(s) are deleted with it: connected translations follow the structure." The delete button then reads "Delete in N languages". (The page-level delete plan is in #200.)Element form in a translation (route
/edit/<lang>/<path>/<uid>):GET /api/page/elements?path=&lang=<default>or the element fetch the form already uses, withlang= default). A docheader toggle Hide/Show hides all boxes (a per-session preference).changed: an amber note at the top of General ("The element changed after this translation was saved. Compare the values under each field. Saving this form is the review: it clears the mark.") and every source box is amber with the eyebrow " — changed since this translation". Change tracking is per element (thel10nSourcehash), not per field; the UI must not pretend to know which field changed.translation.synced[<element name>]. Free: editable as today.#c-<uid>— verifyPreviewPane/previewmap.tsmatch permalinks with the language prefix (an "assumption to check" of the original brief).l10nSource; after save the badge disappears without a reload (re-read the page answer the save returns, which carriestranslation).Skills and AGENTS.md (CLAUDE.md rule): no skill walks through editor screens; confirm with
grep -ril "translat\|language" scaffold/site/.claude/skills/and leave them untouched unless a step describes the editor's behaviour on a translation. AGENTS.md "Translations" already describes the file-level rules; add one sentence under "Editor modules" only if this slice changes what it states.Decisions
Settled in the grilling session and the mockup review; implement, do not reopen.
web/src/styles.csstokens and the existingElementCard,InsertRow,LayoutGrid,ElementForm,FormScreen,PageBanner,Docheaderare extended, not duplicated.Key interfaces
web/src/stores/pageEditor.ts— the page answer'stranslationblock;web/src/api/pageedit.ts/elements.ts— source elements per language;api/elementops.ts—translate,translate-all,delete-plan.components/LayoutGrid.tsx,ElementCard.tsx,InsertRow.tsx,PageContent.tsx,PageBanner.tsx,ElementDeleteDialog.tsx— the Columns view; newTranslationPlaceholder(or similar) for the placeholder row.components/ElementForm.tsx,FormScreen.tsx,FormLayout.tsx, the items collection component — source-value boxes, synced read-only fields, the items rules.previewmap.ts,PreviewPane.tsx— language-prefixed permalinks.docs/mockups/multilingual/m/m.js—TrSlotColumn,trCard,Banners,trElementGroups,decorateItems,formDocheader, the patchedelementMenu.Acceptance criteria
servicesindeontestdata/multilingual-siteshows translated cards with "↔ #uid", placeholder rows at source positions with Translate, the changed badge where the fixture marks it, no insert rows or drag handles, the per-slot note, and the applicable banners; Translate creates the element (POST /api/element/translate) and opens its form; Translate all creates every missing one.delete-plan).descrolls to the element the form's Preview names.webunit tests cover: placeholder interleaving fromuntranslatedand source order, the structural-control guard, banner selection, the source-value box and Copy, the items rules, the changed-mark lifecycle through a save;go test ./...and the skills drift test pass.How to verify
make build,harness serveontestdata/multilingual-site, walk Services/About/Home indeagainst the mockup's Columns view and forms; check the URL at each step.cd web && npm test;go test ./....Out of scope
Blocked by