diff --git a/paragraphs.module b/paragraphs.module
index a6a75af..0148fc8 100644
--- a/paragraphs.module
+++ b/paragraphs.module
@@ -125,7 +125,7 @@ function paragraphs_form_field_storage_config_edit_form_alter(&$form, \Drupal\Co
/**
* Implements hook_form_FORM_ID_alter().
*
- * Indicate unsupported multilingual paragraphs field configuration.
+ * Indicates not recommended multilingual paragraphs field configuration.
*/
function paragraphs_form_field_config_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
$field = $form_state->getFormObject()->getEntity();
@@ -144,24 +144,18 @@ function paragraphs_form_field_config_edit_form_alter(&$form, \Drupal\Core\Form
}
// This is a translatable ERR field pointing to a paragraph.
- $message_display = 'warning';
- $message_text = t('Paragraphs fields do not support translation. See the online documentation.', [
+ $message_text = t('The recommended multilingual configuration is to not enable translation on Paragraph fields. See the online documentation.', [
':documentation' => Url::fromUri('https://www.drupal.org/node/2735121')
->toString()
]);
- if ($form['translatable']['#default_value'] == TRUE) {
- $message_display = 'error';
- }
-
- $form['paragraphs_message'] = array(
- '#type' => 'container',
- '#markup' => $message_text,
- '#attributes' => array(
- 'class' => array('messages messages--' . $message_display),
- ),
+ $form['paragraphs_message'] = [
+ '#theme' => 'status_messages',
'#weight' => 0,
- );
+ '#message_list' => [
+ 'warning' => [$message_text],
+ ],
+ ];
}
/**
@@ -185,10 +179,7 @@ function paragraphs_module_implements_alter(&$implementations, $hook) {
/**
* Implements hook_form_FORM_ID_alter().
*
- * Indicate unsupported multilingual paragraphs field configuration.
- *
- * Add a warning that paragraph fields can not be translated.
- * Switch to error if a paragraph field is marked as translatable.
+ * Indicates not recommended multilingual paragraphs field configuration.
*/
function paragraphs_form_language_content_settings_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
// Without it Paragraphs message are meaningless.
@@ -197,15 +188,11 @@ function paragraphs_form_language_content_settings_form_alter(&$form, \Drupal\Co
}
$content_translation_manager = \Drupal::service('content_translation.manager');
- $message_display = 'warning';
- $message_text = t('(* unsupported) Paragraphs fields do not support translation. See the online documentation.', [
+ $message_text = t('(* not recommended) The recommended multilingual configuration is to not enable translation on Paragraph fields. See the online documentation.', [
':documentation' => Url::fromUri('https://www.drupal.org/node/2735121')
->toString()]);
$map = \Drupal::service('entity_field.manager')->getFieldMapByFieldType('entity_reference_revisions');
foreach ($map as $entity_type_id => $info) {
- if (!$content_translation_manager->isEnabled($entity_type_id)) {
- continue;
- }
$field_storage_definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type_id);
/** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition */
@@ -223,30 +210,23 @@ function paragraphs_form_language_content_settings_form_alter(&$form, \Drupal\Co
$bundles = Element::children($form['settings'][$entity_type_id]);
}
foreach($bundles as $bundle) {
- if (!$content_translation_manager->isEnabled($entity_type_id, $bundle)) {
- continue;
- }
// Update the label and if the paragraph field is translatable,
// display an error message instead of just a warning.
if (isset($form['settings'][$entity_type_id][$bundle]['fields'][$name]['#label'])) {
- $form['settings'][$entity_type_id][$bundle]['fields'][$name]['#label'] = t('@field_label (* unsupported)', ['@field_label' => $form['settings'][$entity_type_id][$bundle]['fields'][$name]['#label']]);
- }
- if (!empty($form['settings'][$entity_type_id][$bundle]['fields'][$name]['#default_value'])) {
- $message_display = 'error';
+ $form['settings'][$entity_type_id][$bundle]['fields'][$name]['#label'] = t('@field_label (* not recommended)', ['@field_label' => $form['settings'][$entity_type_id][$bundle]['fields'][$name]['#label']]);
}
}
}
}
}
- $form['settings']['paragraphs_message'] = array(
- '#type' => 'container',
- '#markup' => $message_text,
- '#attributes' => array(
- 'class' => array('messages messages--' . $message_display),
- ),
+ $form['settings']['paragraphs_message'] = [
+ '#theme' => 'status_messages',
'#weight' => 0,
- );
+ '#message_list' => [
+ 'warning' => [$message_text],
+ ],
+ ];
}
/**
diff --git a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
index 45f33cc..0800d29 100644
--- a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
+++ b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php
@@ -16,6 +16,7 @@ use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Render\Element;
use Drupal\paragraphs\ParagraphInterface;
+use Drupal\paragraphs\Entity\Paragraph;
use Drupal\paragraphs\Plugin\EntityReferenceSelection\ParagraphSelection;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
@@ -385,54 +386,7 @@ class ParagraphsWidget extends WidgetBase {
}
if ($paragraphs_entity) {
- // Detect if we are translating.
- $this->initIsTranslating($form_state, $host);
- $langcode = $form_state->get('langcode');
-
- if (!$this->isTranslating) {
- // Set the langcode if we are not translating.
- $langcode_key = $paragraphs_entity->getEntityType()->getKey('langcode');
- if ($paragraphs_entity->get($langcode_key)->value != $langcode) {
- // If a translation in the given language already exists, switch to
- // that. If there is none yet, update the language.
- if ($paragraphs_entity->hasTranslation($langcode)) {
- $paragraphs_entity = $paragraphs_entity->getTranslation($langcode);
- }
- else {
- $paragraphs_entity->set($langcode_key, $langcode);
- }
- }
- }
- else {
- // Add translation if missing for the target language.
- if (!$paragraphs_entity->hasTranslation($langcode)) {
- // Get the selected translation of the paragraph entity.
- $entity_langcode = $paragraphs_entity->language()->getId();
- $source = $form_state->get(['content_translation', 'source']);
- $source_langcode = $source ? $source->getId() : $entity_langcode;
- // Make sure the source language version is used if available. It is a
- // the host and fetching the translation without this check could lead
- // valid scenario to have no paragraphs items in the source version of
- // to an exception.
- if ($paragraphs_entity->hasTranslation($source_langcode)) {
- $paragraphs_entity = $paragraphs_entity->getTranslation($source_langcode);
- }
- // The paragraphs entity has no content translation source field if
- // no paragraph entity field is translatable, even if the host is.
- if ($paragraphs_entity->hasField('content_translation_source')) {
- // Initialise the translation with source language values.
- $paragraphs_entity->addTranslation($langcode, $paragraphs_entity->toArray());
- $translation = $paragraphs_entity->getTranslation($langcode);
- $manager = \Drupal::service('content_translation.manager');
- $manager->getTranslationMetadata($translation)->setSource($paragraphs_entity->language()->getId());
- }
- }
- // If any paragraphs type is translatable do not switch.
- if ($paragraphs_entity->hasField('content_translation_source')) {
- // Switch the paragraph to the translation.
- $paragraphs_entity = $paragraphs_entity->getTranslation($langcode);
- }
- }
+ $paragraphs_entity = $this->prepareEntity($paragraphs_entity, $items, $form_state);
$element_parents = $parents;
$element_parents[] = $field_name;
@@ -2161,6 +2115,89 @@ class ParagraphsWidget extends WidgetBase {
return $values;
}
+
+/**
+ * Prepares the paragraph entity for translation.
+ *
+ * @param \Drupal\paragraphs\Entity\Paragraph $entity
+ * The paragraph entity.
+ * @param \Drupal\Core\Field\FieldItemListInterface $items
+ * The field items list that hosts this paragraph.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The form state.
+ *
+ * @return \Drupal\paragraphs\Entity\Paragraph
+ * The prepared paragraph.
+ *
+ * @see \Drupal\Core\Entity\ContentEntityForm::initFormLangcodes().
+ */
+ protected function prepareEntity(Paragraph $entity, FieldItemListInterface $items, FormStateInterface $form_state) {
+ // Detect if we are translating.
+ $this->initIsTranslating($form_state, $items->getEntity());
+ $langcode = $form_state->get('langcode');
+
+ if (!$this->isTranslating) {
+ // Set the langcode if we are not translating.
+ $langcode_key = $entity->getEntityType()->getKey('langcode');
+ if ($entity->get($langcode_key)->value != $langcode) {
+ // If a translation in the given language already exists, switch to
+ // that. If there is none yet, update the language.
+ if ($entity->hasTranslation($langcode)) {
+ $entity = $entity->getTranslation($langcode);
+ }
+ else {
+ $entity->set($langcode_key, $langcode);
+ }
+ }
+ }
+
+ // Localised Paragraphs.
+ // If the parent field is marked as translatable, assume paragraphs
+ // to be localized (host entity expects different paragraphs for
+ // different languages)
+ elseif ($items->getFieldDefinition()->isTranslatable()) {
+ if (!empty($form_state->get('content_translation'))) {
+ $entity = $this->createDuplicateWithSingleLanguage($entity, $langcode);
+ }
+ if ($entity->hasTranslation($langcode)) {
+ $entity = $entity->getTranslation($langcode);
+ }
+ }
+
+ // Translated Paragraphs
+ // If the parent field is not translatable, assume the paragraph
+ // entity itself (rather the fields within it) are marked as
+ // translatable. (host entity expects same paragraphs in different
+ // languages).
+ else {
+ // Add translation if missing for the target language.
+ if (!$entity->hasTranslation($langcode)) {
+ // Get the selected translation of the paragraph entity.
+ $entity_langcode = $entity->language()->getId();
+ $source = $form_state->get(['content_translation', 'source']);
+ $source_langcode = $source ? $source->getId() : $entity_langcode;
+ $entity = $entity->getTranslation($source_langcode);
+ // The paragraphs entity has no content translation source field if
+ // no paragraph entity field is translatable, even if the host is.
+ if ($entity->hasField('content_translation_source')) {
+ // Initialise the translation with source language values.
+ $entity->addTranslation($langcode, $entity->toArray());
+ $translation = $entity->getTranslation($langcode);
+ $manager = \Drupal::service('content_translation.manager');
+ $manager->getTranslationMetadata($translation)
+ ->setSource($entity->language()->getId());
+ }
+ }
+ // If any paragraphs type is translatable do not switch.
+ if ($entity->hasField('content_translation_source')) {
+ // Switch the paragraph to the translation.
+ $entity = $entity->getTranslation($langcode);
+ }
+ }
+
+ return $entity;
+ }
+
/**
* {@inheritdoc}
*/
@@ -2221,6 +2258,51 @@ class ParagraphsWidget extends WidgetBase {
}
}
+
+ /**
+ * Clones a paragraph recursively.
+ *
+ * Also, in case of a translatable paragraph, updates its original language
+ * and removes all other translations.
+ *
+ * @param \Drupal\paragraphs\ParagraphInterface $paragraph
+ * The paragraph entity to clone.
+ * @param string $langcode
+ * Language code for all the clone entities created.
+ *
+ * @return \Drupal\paragraphs\ParagraphInterface
+ * New paragraph object with the data from the original paragraph. Not
+ * saved. All sub-paragraphs are clones as well.
+ */
+ protected function createDuplicateWithSingleLanguage(ParagraphInterface $paragraph, $langcode) {
+ $duplicate = $paragraph->createDuplicate();
+
+ // Clone all sub-paragraphs recursively.
+ foreach ($duplicate->getFields(FALSE) as $field) {
+ // @todo: should we support field collections as well?
+ if ($field->getFieldDefinition()->getType() == 'entity_reference_revisions' && $field->getFieldDefinition()->getTargetEntityTypeId() == 'paragraph') {
+ foreach ($field as $item) {
+ $item->entity = $this->createDuplicateWithSingleLanguage($item->entity, $langcode);
+ }
+ }
+ }
+
+ // Change the original language and remove possible translations.
+ if ($duplicate->isTranslatable()) {
+ $duplicate->set('langcode', $langcode);
+ foreach ($duplicate->getTranslationLanguages(FALSE) as $language) {
+ try {
+ $duplicate->removeTranslation($language->getId());
+ }
+ catch (\InvalidArgumentException $e) {
+ // Should never happen.
+ }
+ }
+ }
+
+ return $duplicate;
+ }
+
/**
* After-build callback for removing the translatability clue from the widget.
*
@@ -2507,7 +2589,7 @@ class ParagraphsWidget extends WidgetBase {
* TRUE if we can allow reference changes, otherwise FALSE.
*/
protected function allowReferenceChanges() {
- return !$this->isTranslating;
+ return !$this->isTranslating || $this->fieldDefinition->isTranslatable();
}
/**