Problem/Motivation

Placeholder to plan translation related work.

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Comments

mogtofu33 created an issue.

ipumpkin’s picture

My opinion for Display Builder Multilingual Strategy

Core Principle
Translation happens at the layer where the data lives — never at the structure or workbench layer.

Each of the three layers uses its own native mechanism for multilingual support. Layout structure stays shared where it must (config layers), and translation is stored where content actually lives.

Layer 1: Page Layout (config entity)

Decision: Layout is shared across all languages or use language condition. No translation happens at this layer. All variable content inside the layout tree must be bound to translatable data source.

Rationale:

  1. display_builder_page_layout.* is a configuration entity — There is no simply way to translate a config entity per language.
  2. The page layout defines the site shell (header/footer/global regions). The shell should be consistent across languages — a shared shell with translated content is what users expect from a multilingual site.
  3. Content that differs per language is fetched at render time via data source bindings
  4. Constraint (the one rule): No hardcoded multilingual text (textfield/wysiwyg with literal strings) in shared layout trees. Config-layer text is "dead text" — identical in every language. If a string is truly language-agnostic (brand slogan), accept it as-is or use component with twig interface translation.
  5. Large source tree and page layout item in config layer has performance risk

Layer 2: Entity Type Display / Bundle Default Layout (entity type)

Decision: Identical to Layer 1 — layout sources live in core.entity_view_display.* third_party_settings (config), shared across languages, with content pulled from translatable field bindings.

Rationale:

  1. Same config-layer constraint as Layer 1: third_party_settings of a view display is configuration, not content. No translation mechanism exists there.
  2. This layer defines the default layout template for all nodes of a bundle. A template is by definition shared — translating it would mean duplicating the template per language, and any design change would need to be applied N times.
  3. All nodes of the bundle inherit this shared layout; per-node differences are handled at Layer 3.
  4. The bundle default layout must not contain bundle-specific hardcoded copy — it renders the same fields for every node, and those fields carry the translation.

Layer 3: Node Override (entity_override)

Decision: The field_display_* override field on the node is the translation carrier. It stays translatable (translatable = TRUE), giving each language its own independent source tree. display_builder_instance is NOT translated — it remains a single edit-workbench concept, with langcode used only as editor context, never as translation storage.

Rationale:

  1. Fields are content; instances are workbench state. The source tree stored in field_display_* is content (what the page shows). Translating content belongs in the standard content-translation pipeline. An instance holds the editor's draft/work state — translating a draft has no product meaning and no rendering effect.
  2. Native access to the full content_translation pipeline: with a translatable field, translation happens through the standard node translation form and workflow — translation status tracking, sync/async field handling, translation permissions, and the familiar editor UX. Content editors never need to enter the builder workbench to translate.
  3. Hardcoded text in the tree becomes translatable. Because each language has its own tree, literal strings inside components (titles, helper text) can be translated per language. This is exactly the freedom Layer 1/2 cannot offer — the field-level translation model removes the "bind everything to fields" constraint.
  4. Instance stays single and untranslated because of the publish/sync mechanism: Instance::publish() → saveSources() mirrors the tree back to the render path (PageLayout sources / node field). Per-language instances would make "publish which language?" ambiguous and break this bidirectional sync. The instance is a workbench; langcode merely selects which language's tree the editor is working on.
mogtofu33’s picture

display_builder_page_layout.* is a configuration entity — There is simply no way (and no reason) to translate a config entity per language.

Wrong baseline, we have Translating configuration for that, and [##3555110] is to have a better process than actual painful config translation interface in Drupal.

Then for entity_override we have the field storage problem, on my side I would not mind to have a common display for all languages, but it is by design a field. I am curious on how Layout Builder override manage the multi-language then?

ipumpkin’s picture

display_builder_page_layout.* is a configuration entity — There is simply no way (and no reason) to translate a config entity per language.

Wrong baseline, we have Translating configuration for that, and [##3555110] is to have a better process than actual painful config translation interface in Drupal.

Yes, there was an error in my description here, and I have corrected my mistake.

Then for entity_override we have the field storage problem, on my side I would not mind to have a common display for all languages, but it is by design a field. I am curious on how Layout Builder override manage the multi-language then?

I can do some research in this direction.