Problem/Motivation

As described in #2773329: Field token values are not fetched in the correct language if field translation is disabled, tokens for translated entities referred by an entity reference field are not in the correct language. For example, token [node:term_reference_field:entity:name] will be provided in the default language, even if the node provided is in a different (active) language and if a translation exists.

Proposed resolution

We need test coverage for translatable fields and entities. For instance, the :name token of an entity should be in the correct language. Once the bug is exposed, we can fix it.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Bambell created an issue. See original summary.

Bambell’s picture

Status: Needs review » Needs work

The last submitted patch, 2: test_coverage_for-2772673-2.patch, failed testing.

The last submitted patch, 2: test_coverage_for-2772673-2.patch, failed testing.

Bambell’s picture

Title: Test coverage for multilingual fields and entities. » Provide tokens for referenced entities in the active language.
Issue summary: View changes
Status: Needs work » Needs review
FileSize
1.08 KB
3.79 KB

Patch with test and suggested fix. Not entirely sure about $entity_language_id !== 'und'.

Bambell’s picture

Simplifying code and extending test coverage to fields.

Berdir’s picture

Status: Needs review » Needs work
+++ b/tests/src/Kernel/FieldTest.php
@@ -547,10 +557,12 @@ class FieldTest extends KernelTestBase {
       'title' => 'english-node-title',
       'test_term_reference:entity:name' => 'english-test-term',
+      'test_term_reference:entity:term_field:value' => 'english-term-field-value',
+      'test_term_reference:entity:term_field' => 'english-term-field-value',

that's certainly also good to have but I actually meant to test fields on the node, both translatable and untranslatable.

So, for untranslatable, you can use just [test_term_reference] (which should show the label in the correct language).

And then add/use a translatable text field, e.g. test_translatable and then verify that you get the correct translation for both [test_translatable] and [test_translatable:value]

Berdir’s picture

+++ b/tests/src/Kernel/FieldTest.php
@@ -509,4 +517,62 @@ class FieldTest extends KernelTestBase {
+    $node->addTranslation('de', [
+      'title' => 'deutsch-node-title',
+      'test_term_reference' => [
+        'target_id' => $deutsch_term->id(),
+      ],

+++ b/token.tokens.inc
@@ -1450,7 +1450,11 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
+        $active_langcode = $field_item->getEntity()->language()->getId();

I think this field is translatable, that's why it currently works.

As discussed, lets use $langcode instead and use getTranslationFromContext() both on the referenced entity and also further up on the main entity that is passed in when doing other entity tokens.

node_tokens() does the same, for specific tokens.

Bambell’s picture

Assigned: Unassigned » Bambell
Status: Needs work » Needs review
FileSize
6.78 KB
2.71 KB

Uploading a failing patch to expose the problem in #8. When explicitly making untranslatable the taxonomy term reference field (test_term_reference), tests fail with :

[node:test_term_reference:entity:name] was 'english-test-term', expected value 'deutsch-test-term'

Status: Needs review » Needs work

The last submitted patch, 9: provide_tokens_for-2772673-9.patch, failed testing.

The last submitted patch, 9: provide_tokens_for-2772673-9.patch, failed testing.

Bambell’s picture

This field token replacement function is frying my brain ;-P. I got this to work by passing $langcode to the recursed calls.

Bambell’s picture

Adding a test with "normal" (not entity reference) fields.

// Token shouldn't exist if the langcode is specified and incorrect.
$this->assertNoTokens('node', ['node' => $node], ['test_field' => 'foo-english'], ['langcode' => 'de']);
$this->assertNoTokens('node', ['node' => $node], ['test_field_2' => 'foo-deustch'], ['langcode' => 'en']);

The big question now is whether we should try to fallback here or not ?

Berdir’s picture

Status: Needs review » Needs work
+++ b/tests/src/Kernel/FieldTest.php
@@ -612,6 +627,32 @@ class FieldTest extends KernelTestBase {
+      'test_field' => [
+        'value' => 'foo-english',
+        'format' => $this->testFormat->id(),
+      ],
+      'test_field_2' => [
+        'value' => 'foo-deustch',
+        'format' => $this->testFormat->id(),
+      ],

this is not what I meant :)

A single field, with a translation. Not two fields :) Just fill out test_field in the existing node that you create and add a translation and then add assertions to testing that for the existing token asserts in each langugae and then also a case where you pass in $node but use langcode => 'de'. That's all.

Also, typo (a very consistent one ;)): deustch.

Bambell’s picture

Here we go, hopefully I got it right now. Adding a text field to a node, translating that node and field and asserting that the field token is correct for the given node. If the langcode is specified during token replacement, then it has priority over the node's active langcode (had to make few additional changes for this).

Berdir’s picture

Status: Needs review » Needs work

Really close now :)

  1. +++ b/tests/src/Kernel/FieldTest.php
    @@ -617,42 +610,22 @@ class FieldTest extends KernelTestBase {
    +      'test_field' => 'test-german-field',
    

    lets also add test_field:value, here and above.

  2. +++ b/tests/src/Kernel/FieldTest.php
    @@ -617,42 +610,22 @@ class FieldTest extends KernelTestBase {
    +    // If the langcode is specified, it should have priority over the node's
    +    // active language.
    +    $this->assertTokens('node', ['node' => $node], ['test_field' => 'test-german-field'], ['langcode' => 'de']);
    

    can we add one more assert for test_term_reference:entity:term_field, just to make sure this works as well?

  3. +++ b/token.tokens.inc
    @@ -1338,8 +1338,10 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
         $active_langcode = $entity->language()->getId();
         if (!isset($options['langcode'])) {
           // Set the active language in $options, so that it is passed along.
    -      $options['langcode'] = $active_langcode;
    +      $langcode = $options['langcode'] = $active_langcode;
         }
    

    $active_langcode is only used if you go inside the if. so you can move that inside and assign $langcode directly in that case.

Bambell’s picture

Here we go. It wasn't working as intended with :value tokens (specified langcode was ignored).

  • Berdir committed 35ebfcd on 8.x-1.x authored by Bambell
    Issue #2772673 by Bambell, Berdir: Provide tokens for referenced...
Berdir’s picture

Status: Needs review » Fixed
diff --git a/token.tokens.inc b/token.tokens.inc
index 2967f10..7685b63 100644
--- a/token.tokens.inc
+++ b/token.tokens.inc
@@ -1388,7 +1388,7 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
       $langcode = $options['langcode'] = $entity->language()->getId();
     }
     // Obtain the entity with the correct language.
-    $entity = \Drupal::entityManager()->getTranslationFromContext($entity, $langcode);
+    $entity = \Drupal::service('entity.repository')->getTranslationFromContext($entity, $langcode);
 
     // Reset the prepared view flag in case token generation is called from
     // inside field_attach_view().
@@ -1509,7 +1509,7 @@ function field_tokens($type, $tokens, array $data = array(), array $options = ar
         // Entity reference field.
         $entity = $field_item->$property_name;
         // Obtain the referenced entity with the correct language.
-        $entity = \Drupal::entityManager()->getTranslationFromContext($entity, $langcode);
+        $entity = \Drupal::service('entity.repository')->getTranslationFromContext($entity, $langcode);
 
         if (count($parts) > 1) {
           $field_tokens = \Drupal::token()->findWithPrefix($filtered_tokens, $property_name);

Committed.

Berdir’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.