Files
RenewCMS/docs/design/screens/localization-site-languages.md
T

8.2 KiB

Localization: site languages, public URL and setup

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.

L1 Site languages

Route: /settings/sites/:key/languages API: proposed: GET/PUT /v1/site/{key}/languages

Navigation: Settings → Sites → [site] → Languages only. The site name is in the heading and breadcrumb. Site formatting locale, default content language, article language and social locale are four separate values. At least one language stays enabled and the default must be enabled: to retire the default, first Make default on another language, then Disable. Disable and Remove never delete or unpublish content. Disabled languages show a Disabled tag and a Re-enable action; their versions stay readable and editable per permission. Drafts can be published only after Re-enable ("French is disabled on this site. Re-enable it to publish."). The language filter lists them under Disabled. Social locale per language has three states: Derived (from the language code, e.g. pt-BR gives pt_BR), Set (an owner typed it, e.g. fr_FR) and Not set (og:locale left out). Only owners edit it.

ID State Trigger Selector Sizes Fixture
loc.l1.single Single language Owner opens Languages on Archive (one language, pt-BR kept) LocaleFrame{s:{v:"sl",x:"single"}} 1440, 390 Archive
loc.l1.multi Multilingual Journal: four enabled, two disabled (Spanish, Italian) LocaleFrame{s:{v:"sl",x:"multi"}} 1440, 1024, 390 Journal
loc.l1.loading Loading Request in flight LocaleFrame{s:{v:"sl",x:"loading"}} 1440, 390 Journal
loc.l1.error Load failed Network or server error LocaleFrame{s:{v:"sl",x:"error"}} 1440, 390 Journal
loc.l1.add Add a language Add language LocaleFrame{s:{v:"sl",x:"add"}} 1440, 390 Journal
loc.l1.saveFail Add failed Server error, selection kept LocaleFrame{s:{v:"sl",x:"saveFail"}} 1440, 390 Journal
loc.l1.adddup Duplicate language Already enabled LocaleFrame{s:{v:"sl",x:"adddup"}} 1440, 390 Journal
loc.l1.broader Only a broader tag supported Add Portuguese (Portugal): substitution explained, confirmation required LocaleFrame{s:{v:"sl",x:"broader"}} 1440, 390 Journal
loc.l1.default Make default Row menu LocaleFrame{s:{v:"sl",x:"default"}} 1440, 390 Journal
loc.l1.defaultFail Set default failed Server error, nothing changed LocaleFrame{s:{v:"sl",x:"defaultFail"}} 1440, 390 Journal
loc.l1.disable Disable with content Row menu → Disable German LocaleFrame{s:{v:"sl",x:"disable"}} 1440, 390 Journal
loc.l1.stale Languages changed Another owner changed them: "Languages changed. Reload." LocaleFrame{s:{v:"sl",x:"stale"}} 1440, 390 Journal
loc.lang.disableDefault Disable default blocked Disable English LocaleFrame{s:{v:"sl",x:"disableDefault"}} 1440, 390 Journal
loc.lang.disableLast Disable last blocked Only one enabled LocaleFrame{s:{v:"sl",x:"disableLast"}} 1440, 390 Journal
loc.l1.reenable Re-enable Spanish row → Re-enable LocaleFrame{s:{v:"sl",x:"reenable"}} 1440, 390 Journal
loc.l1.reenableDone Re-enabled Toast after success LocaleFrame{s:{v:"sl",x:"reenableDone"}} 1440, 390 Journal
loc.l1.reenableFail Re-enable failed Dialog error, still disabled LocaleFrame{s:{v:"sl",x:"reenableFail"}} 1440, 390 Journal
loc.l1.remove Remove (no content) Italian (0 versions) LocaleFrame{s:{v:"sl",x:"remove"}} 1440, 390 Journal
loc.l1.removeFail Remove failed Dialog error, still listed LocaleFrame{s:{v:"sl",x:"removeFail"}} 1440, 390 Journal
loc.l1.removeBlocked Remove blocked Spanish has versions LocaleFrame{s:{v:"sl",x:"removeBlocked"}} 1440, 390 Journal
loc.l1.readonly Read-only view Non-owner: list and social locale readable; no controls LocaleFrame{s:{v:"sl",x:"readonly"}} 1440, 390 Journal
loc.l1.saved Saved After Add language LocaleFrame{s:{v:"sl",x:"saved"}} 1440, 390 Journal
loc.l1.social Social locale: three states Atelier: Derived en_GB and pt_BR, Set fr_FR, Not set LocaleFrame{s:{v:"sl",x:"social"}} 1440, 1024, 390 Atelier
loc.l1.socialEdit Social locale: edit Edit on French (Set: fr_FR) LocaleFrame{s:{v:"sl",x:"socialEdit"}} 1440, 390 Atelier
loc.l1.socialInvalid Social locale: invalid fr-FR typed with a hyphen LocaleFrame{s:{v:"sl",x:"socialInvalid"}} 1440, 390 Atelier

L1b Public URL pattern (owner setting)

Route: /settings/sites/:key/public-url API: proposed: GET/PUT /v1/site/{key} { public_url_pattern }

Owner-only. One pattern per site, for example https://example.com/{lang}/{slug}. Supported placeholders: {lang} (language code) and {slug} (the article address in that language). {slug} is required: without it every article would share one address. The preview uses a real article of the site. When unset, article panels show "Public URL not configured" and link here. No URL is invented.

ID State Trigger Selector Sizes Fixture
loc.l1b.set Set Pattern saved, preview shown LocaleFrame{s:{v:"pu",x:"set"}} 1440, 390 Journal
loc.l1b.editing Editing Live preview follows the field LocaleFrame{s:{v:"pu",x:"editing"}} 1440, 390 Journal
loc.l1b.unknown Unknown placeholder {language} typed LocaleFrame{s:{v:"pu",x:"unknown"}} 1440, 390 Journal
loc.l1b.scheme Bad scheme ftp:// typed LocaleFrame{s:{v:"pu",x:"scheme"}} 1440, 390 Journal
loc.l1b.encoded Not encoded A space in the path LocaleFrame{s:{v:"pu",x:"encoded"}} 1440, 390 Journal
loc.l1b.noSlug Missing {slug} Pattern without {slug} LocaleFrame{s:{v:"pu",x:"noSlug"}} 1440, 390 Journal
loc.l1b.saved Saved Toast LocaleFrame{s:{v:"pu",x:"saved"}} 1440, 390 Journal
loc.l1b.unset Unset No pattern; opened from "Public URL not configured" LocaleFrame{s:{v:"pu",x:"unset"}} 1440, 390 Journal
loc.l1b.viewOnly Read-only view Non-owner: pattern readable, no controls LocaleFrame{s:{v:"pu",x:"viewOnly"}} 1440, 390 Journal
loc.l1b.invalidHost Invalid host Pattern with an empty host label LocaleFrame{s:{v:"pu",x:"invalidHost"}} 1440, 390 Journal

L2 First-time setup for existing articles

Route: /settings/sites/:key/languages/setup API: proposed: GET /v1/language/setup, POST /v1/language/setup { sites:[{ key, lang }] } (lang is the full tag, e.g. pt-BR)

Opened from the Languages banner of any site; :key is the site it was opened from, and the page lists every site that still has unlabelled articles. Setup assigns a language per site and applies in one transaction. It never translates, links or moves articles. Each site is seeded from its full Site.Locale when a matching language entry exists: pt-BR becomes Portuguese (Brazil). A site with no locale has no seed and the owner must choose. Nothing applies until the owner confirms every row, including a site with no articles. Retrying is safe.

ID State Trigger Selector Sizes Fixture
loc.l2.ready Ready to assign Journal (en), Notes (no locale, must choose), Archive (pt-BR kept) LocaleFrame{s:{v:"su",x:"ready"}} 1440, 390 Journal, Notes, Archive
loc.l2.noLocale No-locale site Notes only: the owner must choose LocaleFrame{s:{v:"su",x:"noLocale"}} 1440, 390 Notes
loc.l2.empty Site with no articles Gallery has no articles; en-GB kept, confirm and Save LocaleFrame{s:{v:"su",x:"empty"}} 1440, 390 Gallery
loc.l2.applying Applying Request in flight LocaleFrame{s:{v:"su",x:"applying"}} 1440, 390 Journal, Notes, Archive
loc.l2.inProgress Setup already running Another owner started it; starting again is safe LocaleFrame{s:{v:"su",x:"inProgress"}} 1440, 390 Journal, Notes, Archive
loc.l2.done Done 16 articles labelled LocaleFrame{s:{v:"su",x:"done"}} 1440, 390 Journal, Notes, Archive
loc.l2.fail Failed Nothing changed; choices kept; retry is safe LocaleFrame{s:{v:"su",x:"fail"}} 1440, 390 Journal, Notes, Archive