Canvas editor loads and saves the site default translation instead of the entity's source translation

Overview

Since #3592033 (closed) a Canvas page's source language can differ from the site default language. The editor still assumes they are the same:

  • Editor requests carry no language, so core upcasts the page to the site default translation. When the page has a translation in that language, the editor loads it instead of the source translation, and edits are saved to it. Discarding changes also targets that translation.
  • handleLanguageChange() in LanguageSelect.tsx checks isDefault, so selecting the source language opens a read-only preview.

Steps to reproduce

  1. Site default en, nl enabled.
  2. Create a new Canvas page and switch its language to nl before publishing (#3592033 (closed)). Add a component and publish.
  3. Add an en translation with different component inputs.
  4. Open /canvas/editor/canvas_page/N. The editor loads the en translation instead of the nl source, and edits are saved to the en translation. /nl/canvas/editor/... redirects back to the prefix-free URL.

Proposed resolution

  • Add a route enhancer for the routes through which the editor reads or writes the page: layout GET/POST/PATCH, the component instance and page data forms, auto-save discard and auto-save status. When the request selects no language, it replaces the upcast page with its source translation. A language selected by URL prefix, domain, session or canvas_preview_langcode is still used. Route enhancers run before access checks, so access is checked on the edited translation.
  • Build the entity forms for the translation passed in. ContentEntityForm otherwise picks the translation in the negotiated language.
  • LanguageSelect.tsx: compare against translations.available[0] instead of isDefault.

The content API and JSON:API are unchanged: the URL still selects the language. Rendering the editor in the source language's context is #3546597.

Split out of #3591976 (closed). Supersedes !1590 (closed), which redirected only the layout GET; writes then targeted a different translation (409).

Edited by Fons Vandamme