diff --git a/core/modules/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install
index a8d4c49..c253bbe 100644
--- a/core/modules/content_translation/content_translation.install
+++ b/core/modules/content_translation/content_translation.install
@@ -5,9 +5,6 @@
* Installation functions for Content Translation module.
*/
-use Drupal\Core\Language\LanguageInterface;
-use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
-
/**
* Implements hook_install().
*/
@@ -15,6 +12,11 @@ function content_translation_install() {
// Assign a fairly low weight to ensure our implementation of
// hook_module_implements_alter() is run among the last ones.
module_set_weight('content_translation', 10);
+
+ // Initialize the Content Translation form language type configuration.
+ /** @var \Drupal\language\LanguageNegotiatorInterface $negotiator */
+ $negotiator = \Drupal::service('language_negotiator');
+ $negotiator->updateConfiguration([]);
}
/**
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 3b29ecc..a6a2a5e 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -13,6 +13,7 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\content_translation\Plugin\LanguageNegotiation\ContentTranslationFormLanguage;
/**
* Implements hook_help().
@@ -52,16 +53,37 @@ function content_translation_help($route_name, RouteMatchInterface $route_match)
*/
function content_translation_module_implements_alter(&$implementations, $hook) {
switch ($hook) {
- // Move some of our hook implementations to the end of the list.
+ // Move our entity_type_alter hook implementation to the end of the list.
case 'entity_type_alter':
$group = $implementations['content_translation'];
unset($implementations['content_translation']);
$implementations['content_translation'] = $group;
break;
+ // Move our entity_prepare_form hook implementation to the beginning of the
+ // list, so that the entity in the form object is exchanged with its
+ // requested translation before any other hook implementations have had
+ // access on it.
+ case 'entity_prepare_form':
+ $group = $implementations['content_translation'];
+ unset($implementations['content_translation']);
+ $implementations = array_merge(array('content_translation' => $group), $implementations);
+ break;
}
}
/**
+ * Implements hook_language_types_info().
+ */
+function content_translation_language_types_info() {
+ return [
+ ContentTranslationFormLanguage::TYPE => array(
+ 'fixed' => [ContentTranslationFormLanguage::METHOD_ID],
+ 'locked' => TRUE,
+ ),
+ ];
+}
+
+/**
* Implements hook_language_type_info_alter().
*/
function content_translation_language_types_info_alter(array &$language_types) {
@@ -307,6 +329,32 @@ function content_translation_form_alter(array &$form, FormStateInterface $form_s
}
/**
+ * Implements hook_entity_prepare_form().
+ */
+function content_translation_entity_prepare_form(EntityInterface $entity, $operation, FormStateInterface $form_state) {
+ // If the Content Translation form language differs from the current form
+ // language and we are editing an entity translation, we need to update the
+ // form language to match the specified value.
+ $langcode = \Drupal::languageManager()->getCurrentLanguage(ContentTranslationFormLanguage::TYPE)->getId();
+ $prepare_translation =
+ $entity instanceof ContentEntityInterface &&
+ in_array($operation, ['default', 'edit']) &&
+ count($entity->getTranslationLanguages()) > 1 &&
+ !$form_state->get(['content_translation', 'source']) &&
+ !$form_state->get(['content_translation', 'translation_form']) &&
+ $entity->language()->getId() != $langcode &&
+ $entity->hasTranslation($langcode);
+
+ if ($prepare_translation) {
+ /** @var \Drupal\Core\Entity\EntityFormInterface $form_object*/
+ $form_object = $form_state->getFormObject();
+ $translation = $entity->getTranslation($langcode);
+ $form_object->setEntity($translation);
+ $form_state->set('langcode', $langcode);
+ }
+}
+
+/**
* Implements hook_language_fallback_candidates_OPERATION_alter().
*
* Performs language fallback for inaccessible translations.
diff --git a/core/modules/content_translation/src/Controller/ContentTranslationController.php b/core/modules/content_translation/src/Controller/ContentTranslationController.php
index 9c6879a..83432f3 100644
--- a/core/modules/content_translation/src/Controller/ContentTranslationController.php
+++ b/core/modules/content_translation/src/Controller/ContentTranslationController.php
@@ -8,6 +8,7 @@
namespace Drupal\content_translation\Controller;
use Drupal\content_translation\ContentTranslationManagerInterface;
+use Drupal\content_translation\Plugin\LanguageNegotiation\ContentTranslationFormLanguage;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\ContentEntityInterface;
@@ -82,8 +83,9 @@ public function prepareTranslation(ContentEntityInterface $entity, LanguageInter
* The route match.
* @param string $entity_type_id
* (optional) The entity type ID.
+ *
* @return array Array of page elements to render.
- * Array of page elements to render.
+ * Array of page elements to render.
*/
public function overview(RouteMatchInterface $route_match, $entity_type_id = NULL) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
@@ -92,6 +94,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
$handler = $this->entityManager()->getHandler($entity_type_id, 'translation');
$manager = $this->manager;
$entity_type = $entity->getEntityType();
+ $destination = $this->getDestinationArray();
// Start collecting the cacheability metadata, starting with the entity and
// later merge in the access result cacheability metadata.
@@ -191,16 +194,17 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
->merge(CacheableMetadata::createFromObject($update_access))
->merge(CacheableMetadata::createFromObject($translation_access));
if ($update_access->isAllowed() && $entity_type->hasLinkTemplate('edit-form')) {
+ $url = $entity->urlInfo('edit-form');
+ $url->setOption('query', [ContentTranslationFormLanguage::QUERY_PARAMETER => $langcode] + $destination);
$links['edit']['url'] = $entity->urlInfo('edit-form');
- $links['edit']['language'] = $language;
}
elseif (!$is_original && $translation_access->isAllowed()) {
$links['edit']['url'] = $edit_url;
}
-
if (isset($links['edit'])) {
$links['edit']['title'] = $this->t('Edit');
}
+
$status = array('data' => array(
'#type' => 'inline_template',
'#template' => '{% if status %}{{ "Published"|t }}{% else %}{{ "Not published"|t }}{% endif %}{% if outdated %} {{ "outdated"|t }}{% endif %}',
@@ -221,18 +225,17 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
$cacheability = $cacheability
->merge(CacheableMetadata::createFromObject($delete_access))
->merge(CacheableMetadata::createFromObject($translation_access));
+
if ($entity->access('delete') && $entity_type->hasLinkTemplate('delete-form')) {
- $links['delete'] = array(
- 'title' => $this->t('Delete'),
- 'url' => $entity->urlInfo('delete-form'),
- 'language' => $language,
- );
+ $url = $entity->urlInfo('delete-form');
+ $url->setOption('query', [ContentTranslationFormLanguage::QUERY_PARAMETER => $langcode] + $destination);
+ $links['delete']['url'] = $url;
}
elseif ($translation_access->isAllowed()) {
- $links['delete'] = array(
- 'title' => $this->t('Delete'),
- 'url' => $delete_url,
- );
+ $links['delete']['url'] = $delete_url;
+ }
+ if (isset($links['delete'])) {
+ $links['delete']['title'] = $this->t('Delete');
}
}
}
diff --git a/core/modules/content_translation/src/Plugin/LanguageNegotiation/ContentTranslationFormLanguage.php b/core/modules/content_translation/src/Plugin/LanguageNegotiation/ContentTranslationFormLanguage.php
new file mode 100644
index 0000000..4e77d96
--- /dev/null
+++ b/core/modules/content_translation/src/Plugin/LanguageNegotiation/ContentTranslationFormLanguage.php
@@ -0,0 +1,50 @@
+get(static::QUERY_PARAMETER);
+ return $langcode ?: $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
+ }
+
+}
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
index 5faa85c..82bb1a0 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
@@ -7,6 +7,7 @@
namespace Drupal\content_translation\Tests;
+use Drupal\content_translation\Plugin\LanguageNegotiation\ContentTranslationFormLanguage;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Language\Language;
@@ -56,6 +57,7 @@ function testTranslationUI() {
$this->doTestPublishedStatus();
$this->doTestAuthoringInfo();
$this->doTestTranslationEdit();
+ $this->doTestFormLanguageSwitch();
$this->doTestTranslationChanged();
$this->doTestTranslationDeletion();
}
@@ -191,16 +193,19 @@ protected function doTestBasicTranslation() {
*/
protected function doTestTranslationOverview() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
- $this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
+ $translate_url = $entity->urlInfo('drupal:content-translation-overview');
+ $this->drupalGet($translate_url);
+ $translate_url->setAbsolute(FALSE);
foreach ($this->langcodes as $langcode) {
if ($entity->hasTranslation($langcode)) {
$language = new Language(array('id' => $langcode));
- $view_path = $entity->url('canonical', array('language' => $language));
- $elements = $this->xpath('//table//a[@href=:href]', array(':href' => $view_path));
+ $view_url = $entity->url('canonical', ['language' => $language]);
+ $elements = $this->xpath('//table//a[@href=:href]', array(':href' => $view_url));
$this->assertEqual((string) $elements[0], $entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation.', array('%language' => $langcode)));
- $edit_path = $entity->url('edit-form', array('language' => $language));
- $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', array(':href' => $edit_path));
+ $query = [ContentTranslationFormLanguage::QUERY_PARAMETER => $langcode, 'destination' => $translate_url->toString()];
+ $edit_url = $entity->url('edit-form', ['query' => $query]);
+ $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', array(':href' => $edit_url));
$this->assertEqual((string) $elements[0], t('Edit'), format_string('Edit link correct for %language translation.', array('%language' => $langcode)));
}
}
@@ -308,6 +313,20 @@ protected function doTestAuthoringInfo() {
}
/**
+ * Tests the form language switch functionality.
+ */
+ protected function doTestFormLanguageSwitch() {
+ $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
+
+ $message = 'The form language can be switched to @langcode through a query string parameter';
+ foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+ $url = $entity->urlInfo('edit-form', ['query' => ['content_translation_target' => $langcode]]);
+ $this->drupalGet($url);
+ $this->assertRaw($entity->getTranslation($langcode)->{$this->fieldName}->value, format_string($message, array('@langcode' => $langcode)));
+ }
+ }
+
+ /**
* Tests translation deletion.
*/
protected function doTestTranslationDeletion() {
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
index 223bb81..f07f2ae 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
@@ -7,6 +7,7 @@
namespace Drupal\content_translation\Tests;
+use Drupal\content_translation\Plugin\LanguageNegotiation\ContentTranslationFormLanguage;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
@@ -192,8 +193,10 @@ protected function doTestWorkflows(UserInterface $user, $expected_status) {
$this->clickLink('Edit', 2);
// An editor should be pointed to the entity form in multilingual mode.
// We need a new expected edit path with a new language.
- $expected_edit_path = $this->entity->url('edit-form', $options);
- $this->assertUrl($expected_edit_path, [], 'The translation overview points to the edit form for editors when editing translations.');
+ $translate_url = $this->entity->url('drupal:content-translation-overview', ['language' => $languages[$this->langcodes[1]], 'absolute' => FALSE]);
+ $options = ['query' => [ContentTranslationFormLanguage::QUERY_PARAMETER => $langcode, 'destination' => $translate_url]];
+ $expected_edit_url = $this->entity->url('edit-form', $options);
+ $this->assertUrl($expected_edit_url, [], 'The translation overview points to the edit form for editors when editing translations.');
}
else {
$this->clickLink('Edit');