Files
RenewCMS/docs/design/screens/localization-editor.md
T

9.6 KiB

Localization: editor header, permissions, publishing

Board: RenewCMS Screens Localization. Frame: LocaleFrame. Data: locale-fixtures.js (single fixture source), tables in locale-spec.js. UI copy says "article", never "story". No revision numbers, IDs or HTTP status names in user-facing text.

L5 Editor: header, actions and title

Route: /article/{id}/edit (one id per version) API: proposed: GET /v1/article/{id} → translations[], capabilities[]

Header priority and the action matrix are in the tables below. One filled primary button at most: Save and publish when allowed, dirty and offline; otherwise Save when dirty; otherwise Publish when clean and offline. Save stays separate and secondary. Desktop collapse order as width shrinks (1440 → 1024): reference button loses its label, Unpublish moves to the overflow menu, the freshness badge becomes icon-only. Mobile: title, language, publication and dirty badges stay at the top; Save, primary and an overflow button sit in a fixed bottom bar; reference, Unpublish, Edit slug, Move and Delete go to the overflow sheet. The language control is a menu button with a menu (not a listbox).

ID State Trigger Selector Sizes Fixture
loc.l5.default Clean draft Open a draft you can publish; title pristine LocaleFrame{s:{v:"ed",x:"default"}} 1440, 1024, 390 Journal
loc.l5.dirty Dirty draft Can edit, can't publish: Save is primary LocaleFrame{s:{v:"ed",x:"dirty"}} 1440, 1024, 390 Journal
loc.l5.dirtyPublish Dirty draft, publish allowed Save and publish French is primary; Save secondary LocaleFrame{s:{v:"ed",x:"dirtyPublish"}} 1440, 1024, 390 Journal
loc.l5.cleanOnline Clean online Unpublish only; no primary LocaleFrame{s:{v:"ed",x:"cleanOnline"}} 1440, 1024, 390 Journal
loc.l5.onlineDirty Dirty online Save updates the live page; Unpublish stays LocaleFrame{s:{v:"ed",x:"onlineDirty"}} 1440, 1024, 390 Journal
loc.l5.saving Saving Save in flight LocaleFrame{s:{v:"ed",x:"saving"}} 1440, 390 Journal
loc.l5.saved Saved Toast LocaleFrame{s:{v:"ed",x:"saved"}} 1440, 390 Journal
loc.l5.saveFail Save failed Stays unsaved; Try saving again; nothing published LocaleFrame{s:{v:"ed",x:"saveFail"}} 1440, 1024, 390 Journal
loc.l5.pubFail Publish failed after save Saved as a draft; Retry publish; edits shown as saved LocaleFrame{s:{v:"ed",x:"pubFail"}} 1440, 1024, 390 Journal
loc.l5.ovf Overflow menu More actions (sheet on mobile) LocaleFrame{s:{v:"ed",x:"ovf"}} 1440, 390 Journal
loc.l5.menu Menu: four languages Open menu LocaleFrame{s:{v:"ed",x:"menu"}} 1440, 390 Journal
loc.l5.two Menu: two languages Two-language site LocaleFrame{s:{v:"ed",x:"two"}} 1440, 390 Journal
loc.l5.many Menu: many languages Scrolls inside the viewport, with filter LocaleFrame{s:{v:"ed",x:"many"}} 1440, 390 Journal
loc.l5.unsaved Switch with unsaved changes Choose English while dirty LocaleFrame{s:{v:"ed",x:"unsaved"}} 1440, 390 Journal
loc.l5.loading Loading version Slow switch LocaleFrame{s:{v:"ed",x:"loading"}} 1440, 390 Journal
loc.l5.navFail Switch failed Stays on current version LocaleFrame{s:{v:"ed",x:"navFail"}} 1440, 390 Journal
loc.l5.gone Version unavailable Deleted or no access (neutral) LocaleFrame{s:{v:"ed",x:"gone"}} 1440, 390 Journal
loc.l5.focus Title: focus Field focused, not changed LocaleFrame{s:{v:"ed",x:"focus"}} 1440, 390 Journal
loc.l5.titleEdit Title: editing Typing; counter shown LocaleFrame{s:{v:"ed",x:"titleEdit"}} 1440, 390 Journal
loc.l5.titleUnsaved Title: unsaved Changed, focus left LocaleFrame{s:{v:"ed",x:"titleUnsaved"}} 1440, 390 Journal
loc.l5.invalid Title: invalid Empty: "Enter a title" LocaleFrame{s:{v:"ed",x:"invalid"}} 1440, 390 Journal
loc.l5.titleShort Title: too short Under 3 characters LocaleFrame{s:{v:"ed",x:"titleShort"}} 1440, 390 Journal
loc.l5.titleLong Title: too long Over 200 characters LocaleFrame{s:{v:"ed",x:"titleLong"}} 1440, 390 Journal
loc.l5.titleSaving Title: saving Save in flight LocaleFrame{s:{v:"ed",x:"titleSaving"}} 1440, 390 Journal
loc.l5.failed Title: save failed Text kept, still unsaved, Try again LocaleFrame{s:{v:"ed",x:"failed"}} 1440, 390 Journal
loc.l5.menuDisabled Language menu with a disabled language Spanish is disabled: Disabled tag, version stays readable LocaleFrame{s:{v:"ed",x:"menuDisabled"}} 1440, 390 Journal

L5b Permission states (per version)

Route: /article/{id}/edit API: capabilities[] on GET /v1/article/{id}; every PATCH/DELETE rechecked on the server

Every action renders from its own capability (read, edit, publish, delete, create translation, review). See the capability matrix. Missing-translation rows appear only for users who can create; unreadable siblings are absent with no placeholder and no count. The formatting toolbar is hidden whenever edit is missing.

ID State Trigger Selector Sizes Fixture
loc.perm.readonly P1 Read-only version Banner, read-only fields, no Save, Publish, Delete, toolbar; reference stays LocaleFrame{s:{v:"perm",x:"ro"}} 1440, 1024, 390 Journal
loc.l5b.roMenu P1 menu No Add translation row LocaleFrame{s:{v:"perm",x:"roMenu"}} 1440, 390 Journal
loc.perm.editNoPublish P2 Edit, no publish Save only; hint about the publish permission LocaleFrame{s:{v:"perm",x:"p2"}} 1440, 1024, 390 Journal
loc.perm.publishNoEdit P3 Publish, no edit Publish only; fields read-only LocaleFrame{s:{v:"perm",x:"p3"}} 1440, 1024, 390 Journal
loc.perm.siblingReadOnly P4 Sibling readable, not editable Arabic row tagged Read only LocaleFrame{s:{v:"perm",x:"p4"}} 1440, 390 Journal
loc.perm.siblingHidden P5 Sibling not readable German absent, no count, no placeholder LocaleFrame{s:{v:"perm",x:"p5"}} 1440, 390 Journal
loc.perm.createNotAll P6 Can create, not modify all Delete article and Move disabled with the reason; Delete French version available LocaleFrame{s:{v:"perm",x:"p6"}} 1440, 390 Journal
loc.l5b.noAdd Menu without Add translation No create permission; Arabic is missing but offers nothing LocaleFrame{s:{v:"perm",x:"noAdd"}} 1440, 390 Journal
loc.l5b.withAdd Menu with Add translation Same article, user may create LocaleFrame{s:{v:"perm",x:"withAdd"}} 1440, 390 Journal

L8 Publishing and deletion

Route: same as the editor API: existing PATCH /v1/article/{id}/publish and /unpublish and DELETE per article id

Every action names its language. No bulk publish. With unsaved edits the primary reads Save and publish and saves first; if the save fails nothing is published and the edits stay unsaved; if the save works and publishing fails the edits show as saved. Unpublish never saves: the saved version goes offline and local edits stay in the editor. Saving an online version updates the live page immediately, and the editor says so. Deletion confirmations name only readable versions.

ID State Trigger Selector Sizes Fixture
loc.l8.publish Publish French Confirm, English untouched LocaleFrame{s:{v:"pd",x:"publish"}} 1440, 390 Journal
loc.lang.publishDisabled Publishing blocked: language disabled French disabled on the site LocaleFrame{s:{v:"pd",x:"publishDisabled"}} 1440, 390 Journal
loc.l8.saveAndPublish Save and publish French Unsaved edits: saves first, then publishes LocaleFrame{s:{v:"pd",x:"saveAndPublish"}} 1440, 390 Journal
loc.l8.saveFailPublish Save failed, nothing published Error banner above the editor and an error toast; Save failed badge, primary Try saving again, edits kept, nothing published LocaleFrame{s:{v:"pd",x:"saveFailPublish"}} 1440, 390 Journal
loc.l8.unpublish Unpublish French Confirm LocaleFrame{s:{v:"pd",x:"unpublish"}} 1440, 390 Journal
loc.l8.unpublishDirty Unpublish with unsaved edits Saved version goes offline; edits kept; nothing saved LocaleFrame{s:{v:"pd",x:"unpublishDirty"}} 1440, 390 Journal
loc.l8.delete Delete French version Confirm LocaleFrame{s:{v:"pd",x:"delete"}} 1440, 390 Journal
loc.l8.deleteSource Delete a source version Names the readable versions that track the source; warns review tracking pauses LocaleFrame{s:{v:"pd",x:"deleteSource"}} 1440, 390 Journal
loc.l8.deleteLast Delete the only version Deletes the article LocaleFrame{s:{v:"pd",x:"deleteLast"}} 1440, 390 Journal
loc.l8.deleteStoryOk Delete article (all readable versions) Names readable versions only LocaleFrame{s:{v:"pd",x:"deleteStoryOk"}} 1440, 390 Journal
loc.l8.deleteStory Delete article blocked Neutral, no version named LocaleFrame{s:{v:"pd",x:"deleteStory"}} 1440, 390 Journal
loc.l8.done Published Toast "French published" and the online banner; badge Published, status Saved LocaleFrame{s:{v:"pd",x:"done"}} 1440, 390 Journal
loc.l8.fail Publish failed Toast LocaleFrame{s:{v:"pd",x:"fail"}} 1440, 390 Journal
loc.l8.deleteDone French version deleted Toast; editor opens English LocaleFrame{s:{v:"pd",x:"deleteDone"}} 1440, 390 Journal
loc.l8.deleteStoryFail Delete article failed Dialog error; nothing deleted LocaleFrame{s:{v:"pd",x:"deleteStoryFail"}} 1440, 390 Journal
loc.l8.deleteStoryDone Article deleted Toast on the Articles list LocaleFrame{s:{v:"li",x:"deleteStoryDone"}} 1440, 390 Journal