There are a great many issues that already documented this, including one that supposedly fixed the issue. However, I am still seeing the issue. Here is my current setup:
Drupal 8.3.7
Ctools 8.x-3.0
Pathauto 8.x-1.0
Token 8.x-1.0

All of the above are up to date.

I have the default language (English) and one additional language (Spanish) enabled.
I have a Book page that references a custom content type called Product via a field with machine name field_product. Both the Book page content type and the Product content type are translatable.
I have two Pathauto patterns, one for each language on my site, that look like literaltext/[node:field_product]/[node:title] (for the default site language) and translatedliteraltext/[node:field_product]/[node:title] (for the translated language).
Assume the Book page has a node with title "BookPage" and translated title "BookPageTranslated".
Assume that the Product has a node with title "Product" and translated title "ProductTranslated".

If admin/structure/types/manage/book/fields/node.book.field_product (field_product in the Book page) has "Users may translate this field" unchecked then the alias I get for my translated Book page looks like translatedliteraltext/product/bookpagetranslated.
If admin/structure/types/manage/book/fields/node.book.field_product (field_product in the Book page) has "Users may translate this field" checked then the alias I get for my translated Book page looks like tranlsatedliteraltext/producttranslated/bookpagetranslated.

The expected outcome is that I will get the translated title for the referenced entity token (in this case [node:field_product]) regardless of whether the entity reference field (in this case field_product) in the entity that does the referencing (in this case the Book page) indicates that its entity reference field allows users to translate it.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

vegantriathlete created an issue. See original summary.

vegantriathlete’s picture

Title: Token for entity reference returns default language » Token for entity reference returns default language instead of translation
Issue summary: View changes
supertony’s picture

I'm having a similar issue while using the Workbench Email module. I'm wondering, has this been fixed? I'm using the latest version and I'm still seeing only the token values from the original language source.

Anybody’s picture

I can also confirm that tokens don't seem to receive the current site language as parameter and are always returned in default language. In my case it's custom tokens.

vidorado’s picture

It seems to be a problem with book tokens. Here is a patch. Tested with Token 1.5.0

joseph.olstad’s picture

wondering if it would be possible to allow the tokens to process a language suffix in the token to get the specific language value otherwise grab the current interface language rather than default language (as this patch purportedly does).

with that said, the advertised patch above is an improvement.

Berdir’s picture

Status: Active » Needs work
+++ b/token.module
@@ -655,7 +658,11 @@ function token_book_load_all_parents(array $book) {
     $i = 1;
     while ($book["p$i"] != $nid) {
-      $cache[$nid][] = Node::load($book["p$i"])->getTitle();
+      $node = Node::load($book["p$i"]);
+      if($langcode && $node->hasTranslation($langcode)) {
+        $node = $node->getTranslation($langcode);
+      }
+      $cache[$nid][] = $node->getTitle();
       $i++;

Should use $entity = \Drupal::service('entity.repository')->getTranslationFromContext($entity, $langcode);

joseph.olstad’s picture

what if you want to say write an email with two languages in the same email, one after the other, how to specify the other language for the token? it would be good if the token it'self could process a langcode in a syntax like :en OR :es OR :de OR :fr ?

Berdir’s picture

That's not in scope for this issue. The language is passed in as an option, token syntax simply isn't flexible enough to allow arguments like that. Each token has one type and has to pass things to its children. It would also explode the number of tokens which is already a massive concern.

ankithashetty’s picture

Status: Needs work » Needs review
FileSize
4.36 KB
3.49 KB

Updated the patch in #5 with the changes specified by @Berdir in #7... Moving status back to "Needs review".

Thank you!

Berdir’s picture

  1. +++ b/token.module
    @@ -651,7 +654,11 @@ function token_book_load_all_parents(array $book) {
         while ($book["p$i"] != $nid) {
    -      $cache[$nid][] = Node::load($book["p$i"])->getTitle();
    +      $node = Node::load($book["p$i"]);
    +      if($langcode && $node->hasTranslation($langcode)) {
    +        $node = \Drupal::service('entity.repository')->getTranslationFromContext($node, $langcode);
    +      }
    +      $cache[$nid][] = $node->getTitle();
    

    Some coding standard issues like missing space between if and (. With getTranslationFromContext(), we do not need to check for hasTranslation() anymore, that method will do it for us and use a fallback which is useful if you have regional languages like en vs en-gb.

    Tests would also be good to have.

  2. +++ b/token.tokens.inc
    @@ -1148,10 +1156,13 @@ function book_tokens($type, $tokens, array $data = [], array $options = [], Bubb
           }
           if ($book_tokens = \Drupal::token()->findWithPrefix($tokens, 'parents')) {
    -        $parents = token_book_load_all_parents($book);
    +        $parents = token_book_load_all_parents($book, !empty($options['langcode']) ? $options['langcode'] : null);
             $replacements += \Drupal::token()->generate('array', $book_tokens, ['array' => $parents], $options, $bubbleable_metadata);
    

    We require Drupal 8.8 now, so this can be simplified to $options['langcode'] ?? NULL.

    We could even do the same in the other calls too, then we'd respect the current site default language if the langcode is not explicitly provided?

Tests would be nice, but it would be a bit work to create a multilingual version of \Drupal\Tests\token\Kernel\BookTest. If someone is willing to write those then great, but open to committing without it.

Berdir’s picture

Status: Needs review » Needs work
joseph.olstad’s picture

Assigned: Unassigned » joseph.olstad
Status: Needs work » Active

new patch comming shortly

joseph.olstad’s picture

joseph.olstad’s picture

Assigned: joseph.olstad » Unassigned
Status: Active » Needs review
joseph.olstad’s picture

Assigned: Unassigned » joseph.olstad
Status: Needs review » Active

oops

joseph.olstad’s picture

Assigned: joseph.olstad » Unassigned
Status: Active » Needs review
FileSize
4.22 KB
2.21 KB

ignore patch 14

Berdir’s picture

Status: Needs review » Needs work
  1. +++ b/token.module
    @@ -653,7 +656,11 @@ function token_book_load_all_parents(array $book) {
    -      $cache[$nid][] = Node::load($book["p$i"])->getTitle();
    +      $node = Node::load($book["p$i"]);
    +      if ($langcode) {
    +        $node = \Drupal::service('entity.repository')->getTranslationFromContext($node, $langcode);
    +      }
    +      $cache[$nid][] = $node->getTitle();
    

    I think we can skip the langcode check and just pass NULL through and it will pick the current content language which is fine.

  2. +++ b/token.tokens.inc
    @@ -1127,6 +1127,9 @@ function book_tokens($type, $tokens, array $data, array $options, BubbleableMeta
             $child_node = Node::load($book['nid']);
    +        if (!empty($options['langcode']) && $child_node->hasTranslation($options['langcode'])) {
    +          $child_node = \Drupal::service('entity.repository')->getTranslationFromContext($child_node, $options['langcode']);
    +        }
    

    Mentioned in previous review but a bit hiding between conding standard comments, the hasTranslation() call for this and 3 more below can be removed and we could I think always use the ?? NULL and remove the condition completely.

dagmar’s picture

Status: Needs work » Needs review
FileSize
4.06 KB
2.33 KB

I only addressed comment 2. I think it still valid to do a check before execute \Drupal::service('entity.repository')->getTranslationFromContext($node, $langcode); which will not slow down the token module if no language is needed.

Rajab Natshah’s picture

Thanks for the #19
It fixes the following error

Error: Call to a member function getTitle() on null in token_book_load_all_parents() (line 635 of modules/contrib/token/token.module).
token_book_load_all_parents(Array) (Line: 1274)
book_tokens('book', Array, Array, Array, Object)
call_user_func_array(Object, Array) (Line: 426)
Drupal\Core\Extension\ModuleHandler->Drupal\Core\Extension\{closure}(Object, 'book') (Line: 405)
Drupal\Core\Extension\ModuleHandler->invokeAllWith('tokens', Object) (Line: 433)
Drupal\Core\Extension\ModuleHandler->invokeAll('tokens', Array) (Line: 357)
Drupal\Core\Utility\Token->generate('book', Array, Array, Array, Object) (Line: 1230)
book_tokens('node', Array, Array, Array, Object)
call_user_func_array(Object, Array) (Line: 426)
Drupal\Core\Extension\ModuleHandler->Drupal\Core\Extension\{closure}(Object, 'book') (Line: 405)
Drupal\Core\Extension\ModuleHandler->invokeAllWith('tokens', Object) (Line: 433)
Drupal\Core\Extension\ModuleHandler->invokeAll('tokens', Array) (Line: 357)

Only having a new error of Argument #1 ($entity) must be of type Drupal\Core\Entity\EntityInterface

The website encountered an unexpected error. Please try again later.
TypeError: Drupal\Core\Entity\EntityRepository::getTranslationFromContext(): Argument #1 ($entity) must be of type Drupal\Core\Entity\EntityInterface, null given, called in /app/docroot/modules/contrib/token/token.tokens.inc on line 1244 in Drupal\Core\Entity\EntityRepository->getTranslationFromContext() (line 93 of core/lib/Drupal/Core/Entity/EntityRepository.php).
Drupal\Core\Entity\EntityRepository->getTranslationFromContext(NULL, 'en') (Line: 1244)
book_tokens('book', Array, Array, Array, Object)
call_user_func_array(Object, Array) (Line: 426)
Drupal\Core\Extension\ModuleHandler->Drupal\Core\Extension\{closure}(Object, 'book') (Line: 405)
Drupal\Core\Extension\ModuleHandler->invokeAllWith('tokens', Object) (Line: 433)
Drupal\Core\Extension\ModuleHandler->invokeAll('tokens', Array) (Line: 357)
Rajab Natshah’s picture

joseph.olstad’s picture

Status: Needs review » Reviewed & tested by the community

RTBC

We've been using this patch for years, the patch 21 is the best one yet and I approve! Fixes hasTranslation() on null issue that was present in a previous version of the patch. I recommend going forward with patch #21 asap.

The interdiff is a mess though, but either way, this patch is needed and I tested it extensively.

Berdir’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/token.tokens.inc
@@ -1227,6 +1227,9 @@ function book_tokens($type, $tokens, array $data, array $options, BubbleableMeta
         $child_node = Node::load($book['nid']);
+        if (!empty($options['langcode']) && $child_node->hasTranslation($options['langcode'])) {
+          $child_node = \Drupal::service('entity.repository')->getTranslationFromContext($child_node, $options['langcode']);
+        }

@@ -1237,42 +1240,52 @@ function book_tokens($type, $tokens, array $data, array $options, BubbleableMeta
+      if (isset($book_node)) {
+        if (!empty($options['langcode']) && $book_node->hasTranslation($options['langcode'])) {
+          $book_node = \Drupal::service('entity.repository')->getTranslationFromContext($book_node, $options['langcode']);
+        }
+        foreach ($tokens as $name => $original) {
+          switch ($name) {
...
+              if (!empty($book['pid'])) {
+                $parent_node = Node::load($book['pid']);
+                if (!empty($options['langcode']) && $parent_node->hasTranslation($options['langcode'])) {
+                  $parent_node = \Drupal::service('entity.repository')->getTranslationFromContext($parent_node, $options['langcode']);
+                }

As said in #18, these 3 case check the if the translation exists, which is different from whether there is a langcode.

I agree that the langcode options make sense, but not the hasTranslationCheck(), because there are use cases where language fallbacks are explicitly desired and this won't work with that.

I'm also a bit confused about the issue title and summary and the patch. The patch is solely about book tokens, but the issue title and description is at least partially about entity reference tokens. I'm fine with making this specifically about book tokens, there's at least one other issue about entity reference fields, but then at least the issue title should be updated to reflect that.

HeikkiY’s picture

We seem to be encountering a similar issue where we are using a taxonomy term token to generate a path for a content type.

This works fine when we save the node but if we bulk generate paths it seems to replace the term with the default language term. Bulk saving the content fixes the issue again.

In our case we are using the following token to generate a path:
/instructions/[node:field_instruction_main_category]/[node:field_instruction_category]/[node:title]

I agree with @berdir in #23 that the issue title and summary and the patch seem to be doing a different thing. I am also a bit worried that #2648252: entity reference field token does not get translated is marked as being a duplicate of this issue and this issue patch seems to solve it only for a certain use case.

We can still test this patch and see if it fixes our issue.

HeikkiY’s picture

We found another similar issue which is not linked as a relationship here.