Skip to main content
Petanque Life

Multi-Language Sites

F18.09 5 features Planned

At a glance

Sites can publish parallel content in 43+ FIPJP languages, with per-page translation, a language switcher in navigation, a configurable default language per site, a translation workflow that flags pages needing work with source diffs, and hreflang tags plus xhtml:link sitemap alternates emitted automatically for SEO. Editors translate page-by-page, partial coverage falls back politely, and visitors land in their language via URL prefix, Accept-Language header or remembered preference.

How it works

Multi-language support is built into the CmsPage model. Each page has a base language (`source_language`) and a translations map keyed by ISO locale (`sv`, `fr`, `de`, …) containing localized title, slug, meta and a translated copy of the block array. The CmsSite declares supported_languages and default_language; pages can be translated into any subset of the site's supported list, and partial coverage is allowed — pages without a target translation fall back to default with a polite notice for visitors who chose another language.

Localized URLs follow a `/<locale>/...` pattern except the default language, which uses clean root paths to keep canonical URLs stable. The renderer detects visitor language via the URL prefix first, then Accept-Language headers, then a sessionStorage preference; the language switcher in the navigation lets visitors change explicitly and remembers the choice across pages. The translation workflow surfaces a queue per site: when a source page is updated, every translation is automatically flagged 'needs review' with a diff against the previous source version, so translators see exactly what changed.

Translators can be different OrgNodeMembers from editors and have a `cms:translate` capability that lets them edit translations without unlocking the source. SEO hreflang tags are emitted for every available translation plus an `x-default` pointing to the site default; the sitemap includes `xhtml:link` hreflang alternates so Google indexes each language correctly. CMS-search respects the active locale and only returns results from the matching language version, with a setting to fall back to default-language matches when no localized hit exists.

Key capabilities

  • Parallel page versions per ISO locale with localized title, slug, meta and blocks
  • Per-site default and supported language configuration
  • Localized `/<locale>/...` routing with clean root paths for default language
  • Language switcher with sessionStorage memory and Accept-Language fallback
  • Translation queue with source-diff to show what changed since last review
  • Separate `cms:translate` capability for translator roles
  • hreflang tags and sitemap alternates for SEO across all languages

In practice

Anneli at the Finnish Federation maintains the federation site in Finnish, Swedish and English. She updates the membership-fees page in Finnish to reflect new prices. The translation queue immediately flags the Swedish and English copies as 'needs review' with a side-by-side diff showing exactly the three numbers and one paragraph that changed.

She forwards the queue link to the federation's volunteer translator who edits the Swedish version that evening; the English version gets done two days later. Until then, visitors who land on the English page see the Finnish content with a 'translation in progress' badge, and Google indexes only the Finnish version because the English hreflang is suppressed for unfinished pages.

Features in this subsystem

5
ID Status Features
F18.09.01 Shipped Multi-language page content (parallel versions per language) PL-F1809 | ✅ byggd, annan modell — innehållet är {lang→text} i sidans title och i varje block­fält blockkatalogen deklarerar som i18nField (aldrig parallella sidrader). cms_page.translations fick sin FORM i 00216: en post per språk med status, translator_ref, reviewed_by/reviewed_at, source_version, source_fingerprint, notes. Båda identitetsreferenserna står i båda identitetsmanifesten (erasure: actor · merge: actor-repoint)
F18.09.02 Shipped Language switcher in navigation PL-F1809 | ✅ byggd, FLYTTAD nyckel — legacys boolean header_layout.show_language_switcher är ersatt av language_config.switcher_placement med FYRA lägen (header|footer|header_footer|hidden). Headern avvisar den gamla nyckeln med ett pekande 422 (D-3), migrationens datasteg bevarade varje sajts placering. Växlarens data kommer ur den publika projektionen GET /v1/public/cms/sites/{id}/languages — renderarens ENDA språkkälla
F18.09.03 Shipped Default language per site PL-F1809 | ✅ byggd med EN skrivare — PUT /v1/cms/sites/{id}/languages är enda vägen till default_language, supported_languages och language_config; sajt-PATCH:ens språkfält ⇒ 422 som pekar hit (D-2). Varje tagg kommer ur plattformens EGET register (GET /v1/cms/language-catalog, cms:view, stark ETag) — D-CMS listar inga språk själv. Att ta bort ett språk raderar ingenting: innehållet ligger kvar och svaret bokför content_languages_dropped[]
F18.09.04 Shipped Translation workflow (flag pages needing translation) PL-F1809 | ✅ byggd, utan flaggan — fyra lagrade ord (not_started|in_progress|needs_review|approved) med sluten kantlista och mandat per kant (cms:edit · cms:approve · cms:publish:immediate), If-Match på SIDANS ETag och EN audit-rad per kant. Föråldrat är HÄRLETT ur källavtrycket, aldrig lagrat — legacys flag-outdated och DELETE …/translations/{lang} är konsoliderade bort (Ö-2). Täckningen är MÄTT av EN funktion som matar matrisen, …/translations/needed och sidlistans languages_with_content
F18.09.05 Shipped hreflang tags for SEO PL-F1809 | ✅ byggd som EN härledning — GET /v1/public/cms/sites/{id}/pages/{slug}/hreflang ger en post per språk sidan faktiskt är TILLGÄNGLIG i (policyn respekterad) plus x-default. URL-formen är default-språket på ren väg och övriga under /{lang}, absolut över sajtens publika värd inklusive monteringen. Saknar sajten publik värd är mängden tom och base_url null — en påhittad adress vore fabricerad data i ett SEO-huvud. m-11:s seo-head/canonical/sitemap KONSUMERAR mängden