Problem/Motivation
When a translatable paragraph bundle is referenced through an Entity Reference Revisions field marked as non-translatable (translatable: false at the FieldConfig level) on a parent paragraph bundle, the ParagraphsWidget always shows — and overwrites — the original-language values of the child paragraph. The child paragraph's own translatable fields become impossible to translate.
The intent of a non-translatable ERR field is: all language versions of the host share the same structural reference (same child paragraph entity), but the child paragraph itself can still hold per-language translations of its own content fields. This is sometimes called the "shared structure, translated content" pattern.
Steps to reproduce
-
Create two paragraph bundles:
- wrapper with an ERR field field_items set to non-translatable (translatable: false), referencing bundle card.
- card with content translation enabled and at least one translatable text field (e.g. field_title).
- Add a node type with an ERR field referencing wrapper. Enable content translation for both the node type and the card bundle.
- Create a node in the default language (e.g. English), add a wrapper with a card child. Save
- Translate the node to a second language (e.g. Italian). Open the translation form.
Expected behavior: The widget for field_items loads the existing translation of card in the current language, or — when none exists yet — pre-populates the subform with the source-language values so the user can translate them. On save, a translation of card in the current language is created or updated, while the source-language version is left untouched.
Actual behavior: The translatable fields of card cannot be translated at all. ParagraphsWidget::formElement() enters the !$this->isTranslating branch because the immediate host (wrapper) carries no translation (it was never required to have one, since its parent ERR field is non-translatable). Inside that branch, when no translation of card exists for the target language, the code falls through to:
<?php $paragraphs_entity->set($langcode_key, $langcode);
This renames the original paragraph entity to the target language instead of creating a separate translation. The source-language version is destroyed, and no per-language translation is ever created. Editing the content in either language subsequently loads the same (now orphaned) entity.
Proposed resolution
In the !$this->isTranslating block, add an intermediate elseif before the destructive else:
<?php if ($paragraphs_entity->hasTranslation($langcode)) { $paragraphs_entity = $paragraphs_entity->getTranslation($langcode); } elseif ($paragraphs_entity->hasField('content_translation_source')) { // The paragraph bundle supports content translation. Even when the // referencing ERR field is non-translatable (same entity shared across // all language versions of the host), the child needs a per-language // translation so its own translatable fields can be edited independently. // Create an in-memory translation here; ERR::preSave() will persist it. $paragraphs_entity->addTranslation($langcode, $paragraphs_entity->toArray()); $paragraphs_translation = $paragraphs_entity->getTranslation($langcode); /** @var \Drupal\content_translation\ContentTranslationManagerInterface $manager */ $manager = \Drupal::service('content_translation.manager'); $manager->getTranslationMetadata($paragraphs_translation) ->setSource($paragraphs_entity->language()->getId()); $paragraphs_entity = $paragraphs_translation; } else { $paragraphs_entity->set($langcode_key, $langcode); }
A patch implementing this change is attached.
| Comment | File | Size | Author |
|---|---|---|---|
| untranslatable-field-nested-paragraph-translation.patch | 1.58 KB | guizzard |
Comments