Problem/Motivation

The ::toUrl() method of any content entity seems to incorrectly fall back to the default language of the entity instead of falling back to the language provided by the Language Negotiation API.

In a default scenario (aka when enabling multiple languages on a vanilla Drupal installation) the Language Negotiation API would fall back to the user's current language, which would be reflected by the URL of the page the user is visiting (language domain or prefix).

Currently however, the Entity API is falling back to the default language of the entity, without consulting the Language Negotiation API, resulting in unexpected (and faulty IMHO) behaviour that any URL provided by ::toUrl() will have the language prefix removed (in the default scenario). Clicking on any link generated by ::toUrl() would undesirably change the user's current language. This problem was reported in the following issues:

Proposed resolution

I think this should be solved by changing the way the language of the entity is resolved in the ContentEntityBase::toUrl() method. Instead of defaulting to the entities' default language, it should use Language Negotiation to see what it needs to do.

Remaining tasks

Write tests for all 'child' issues?

Issue fork drupal-3061761

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

nuez created an issue. See original summary.

nuez’s picture

Title: The language of content entities incorrectly falls back to the default language instead of the current language. » The language of content entities incorrectly falls back to the default language of the content whilst ignoring the current language of the user
nuez’s picture

StatusFileSize
new4.01 KB
new2.68 KB

I'm adding 2 patches:

One patch to prove this is failing on the ImageFormatter and another one with a potential fix.

nuez’s picture

Issue summary: View changes
nuez’s picture

Issue summary: View changes
phenaproxima’s picture

Issue summary: View changes

Fixing issue links in the IS.

nuez’s picture

Issue summary: View changes
StatusFileSize
new8.37 KB
new8.37 KB

After discussing this with @alexpott @berdir and @phenaproxima I'm trying to get closer to the right approach here:

According to Berdir (correct me if I'm wrong) it's a no-go to change the EntityBase::language() which would be API changing. Possibly we can discuss the option to change the toUrl method on the ContentEntity.

We can either try to do that, or resolve the language in each formatter before $entity->toUrl() is called.

Personally I think $entity->toUrl();should just resolve to the right language. When there's no language context whatsoever, then it should resolve to the current language of the user. But I realise cannot oversee all the implications.

I'm uploading two patches

  1. A fail patch to prove that it's currently failing on the ImageFormatter and on the MediaThumbnailFormatter
  2. A patch to propose to override the toUrl() method in the content entity - mainly to see what other tests this might break.
nuez’s picture

StatusFileSize
new8.46 KB
new8.46 KB

Tests are failing because of the testbot subdirectory.

Uploading them again, and if everything goes right, from a testbot perspective, we only have to review 4 failing tests.

nuez’s picture

Accidentally misnamed the patches, but the failtest actually contains the fix and vice versa.

nuez’s picture

Status: Active » Needs review
StatusFileSize
new3.74 KB
new2.87 KB

Accidentally misnamed the patches, but the failtest actually contains the fix and vice versa.

This is not even true (it must have been late). Both patches in #8 in seem to be exactly the same, and are probably failing for other reasons.

Sending two new patches for the testbot to report back.

Since all the 'child issues' are caused by the same problem, writing tests for all different issues would result in practically the same tests for all different image formatters. I'm not sure if this is necessary. Love to hear feedback about this.

Instead of testing this at the level of an image formatter, I'm adding a test to the LanguageNegotiationContentEntityTest to test the correct 'fall back' behaviour when ::toUrl is called without passing any language context but the language prefix in the URL.

nuez’s picture

Issue summary: View changes

The last submitted patch, 10: 3061761-10.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 10: 3061761-10-fail.patch, failed testing. View results

charginghawk’s picture

This also resolves an issue where, under specific circumstances, {{ url }} in twig templates returning the "canonical" URL use the content's original langcode prefix instead of the active langcode prefix. See:

https://www.drupal.org/project/drupal/issues/2689459#comment-13129366

I noticed this issue with programmatically generated content, specifically from a migrate job where langcode had been set to "en".

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new6.23 KB
new3.68 KB

The tests are failing in #10 basically, as far as I can understand, there is no way to know for sure what the intention is when calling to ::toUrl(). Either:

  1. When no language context is given, fall back to the default language of the entity (currently the case and cause of many issues reported) - or
  2. When no language context given, fall back to the current language of the user (using language negotiation) - currently not the way it works, but how I think it should work.

However in some cases, you want to target the translation (and change the language prefix). This is why my patch in #10 breaks FieldEntityLinkBaseTest, because the intention there is not to fall back to the user's language, but to the default language of the entity, since we're listing translations of the same node.

Not troubled by a very in dept knowledge of the Entity API, nor whether or not it would be possible to add such a change, I would say that it needs to be possible to mark the entity as 'called in a translation context', so that the URL resolves correctly when necessary. But that in all other cases, the current user language is assumed.

I'm also not sure about the naming of the methods added.

Anyway, as a strating point and proof of concept I'm uploading this new patch.

Status: Needs review » Needs work

The last submitted patch, 15: 3061761-15.patch, failed testing. View results

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new6.01 KB

Status: Needs review » Needs work

The last submitted patch, 17: 3061761-16.patch, failed testing. View results

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new6.06 KB

Status: Needs review » Needs work

The last submitted patch, 19: 3061761-17.patch, failed testing. View results

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new6.12 KB

Status: Needs review » Needs work

The last submitted patch, 21: 3061761-21.patch, failed testing. View results

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new6.06 KB

Status: Needs review » Needs work

The last submitted patch, 23: 3061761-23.patch, failed testing. View results

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new6.71 KB

Status: Needs review » Needs work

The last submitted patch, 25: 3061761-25.patch, failed testing. View results

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new9.06 KB

Status: Needs review » Needs work

The last submitted patch, 27: 3061761-27.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new9.05 KB
nuez’s picture

After a couple of iterations I finally have a patch that doesn't break things.

Some (perhaps some superfluous) observations:

  1. The patch changes the default language fallback of content entities to the current user language (using language negotiation) instead of the default language (x-default) of the entity.
  2. The patch allows entities to keep on resolving to the right language when they are indeed language specific. That's the case when the entity is loaded from EntityRepository::getTranslationFromContext() or from ContentEntityBase::getTranslation.
  3. To flag an entity as 'LanguageSpecific' a property was added to the ContentEntityBase with corresponding getter and setter.
  4. A test was added to prove that things are currently not working (https://www.drupal.org/files/issues/2019-07-11/3061761-10-fail.patch)

Questions:

  1. Should we change the naming of the 'translation' property, maybe to 'languageSpecific' and the corresponding methods, eg ::markAsTranslation to markAsLanguageSpecific?
  2. Should we add further tests to prove that this solves the linked issues or is the test added to this issue enough?

I don't know if this kind of API change/addition is acceptable, but I think all the linked issues prove that this is something that needs to be addressed in a more generic way. I think I can further iterate on this but it's probably best for some of the core maintainers to say something about this. I would love to receive some feedback.

nuez’s picture

StatusFileSize
new8.64 KB
new751 bytes

I deleted a line in an existing test that should have been deleted. On my localhost the test at issue continues to pass.

charginghawk’s picture

@nuez In your recent patches, not sure why this code is commented out, or what the if condition is doing in that case:

+  /**
+   * {@inheritdoc}
+   */
+  public function toUrl($rel = 'canonical', array $options = []) {
+    if ($rel == 'canonical' && !isset($options['language']) && !$this->isTranslation()) {
+   //   $currentLanguage = $this->languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT);
+   //   $options['language'] = $currentLanguage;
+    }
+    return parent::toUrl($rel, $options);
+  }
nuez’s picture

Status: Needs review » Needs work

@charginghawk

DOH! I'm doing bits and pieces on my holiday destination, lot's of distractions...Obviously not focussed enough. Let me revise the whole thing once again.

Setting this to 'needs work'

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new10.11 KB
new10.11 KB

Uploading two new patches:

- One fail patch with the working code commented out.
- One patch that does work.

The patch takes into account that the testbot runs in a subdirectory, which in previous patches meant that the patches failed, even though they were meant to pass. I've tried this with a local d8 installation in a subdirectory. Hope it works the same with the testbot.

I've also run the patch locally with the tests that failed in #25 and #23, and locally they seem to pass.

I'm also toying around with the naming of things: in this patch I'm proposing to call content entities 'Language Aware' and 'Language unaware', the difference being that when the entity is language unaware, it should inherit the language from the current user language, and not from it's own default language.

Let's see what the test bot returns.

nuez’s picture

The last submitted patch, 34: 3061761-34.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 34: 3061761-34-fail.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

nuez’s picture

Status: Needs work » Needs review
StatusFileSize
new10.08 KB
new2.16 KB
  • The testbot doesn't specify why TaxonomyFieldTidTest::testViewsHandlerTidField fails in #34, but it's because of a mismatch in the hreflang attribute in the link, which is added by LinkGenerator::generate (110). (Why a hreflang is added in a non multilingual installation is food for another issue).

    The hreflang attribute is added based on the #options['language'] of the Url object, which defaults to the entities default language. See EntityBase::toUrl.

    In this patch I'm also marking entities whose requested translation is LANGUAGE_NOT_SPECIFIED as 'language unaware' . This stops TaxonomyFieldTidTest::testViewsHandlerTidField from failing. However, it would break TermIndexTest::testTaxonomyTermHierarchyBreadcrumbs, for similar reasons.

  • The test in TermIndexTest::testTaxonomyTermHierarchyBreadcrumbs would not add any hreflang attributes currently. However with the patch, since we're explicitly adding the language to the #options array in ContentEntityBase, so the 'hreflang' attribute gets added to the expected result by EntityBase::toUrl, and not to the actual result, where ::toUrl() is not used, but Link::createFromRoute().

    That's why I'm also making sure that the 'hreflang' attribute is not added on calling ::toUrl on a language unaware entity.

  • Finally, I've added a condition that the working code only applies on mutlilingual sites by checking ::isMultilingual().

I realise that this fix is far from a solution. There are just so many things that tie in together, that it's hard to know what we could potentially break, and whether or not it's compliant with the way things were meant to work.

Things to take into account:

  1. I haven't had the chance to look into language cache context. Not sure if something else needs to be done.
  2. The EntityRepository::getTranslationFromContext actually seems to do what I'm trying to do in this issue: It's resolving language based on both the users' language and the entity language.
  3. After speaking to people about this issue, I believe there are more issues out there regarding this specific issue. We should try and find them.

If this patch passes the tests, I think a core maintainer should have a look at this. Hope this helps though.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

firfin’s picture

Issue summary: View changes

Alphabetized the issue list and removed a duplicate. It was hard to see if the issues I found were in there

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

ben_a’s picture

StatusFileSize
new12.54 KB

I've re-rolled the last patch for 9.2.x as it didn't apply properly. I hope this helps someone.

gauravvvv’s picture

StatusFileSize
new4.69 KB
new11.96 KB

Re-rolled patch #44, tried fixing cs errors.

gauravvvv’s picture

StatusFileSize
new11.96 KB

removed white space from empty line.

Status: Needs review » Needs work

The last submitted patch, 46: 3061761-46.patch, failed testing. View results

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

akalam made their first commit to this issue’s fork.

akalam’s picture

Status: Needs work » Needs review

There was a mistake with patch on #46, the followingt method was duplicated \Drupal\Tests\language\Functional\LanguageNegotiationContentEntityTest::testContentEntityLanguageFallback

I created a new MR with a commit authored by @nuez with the re-rolled patch on #46 + the fix of the duplicated method.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

geek-merlin’s picture

Dug through quite some link translation issues and think this is the most relevant.

I doubt this is on the right track though. We must strictly separate interface language detection and content language detection (by query string, see Enable language detection).

What did the trick for me is to enable content language detection and borrow code from ContentLanguageNegotiation of Language Negotiator Content Entity (All Routes).

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new3.54 KB

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

jonasanne’s picture

StatusFileSize
new10.97 KB

Rerolled patch to 10.2.x

greg boggs’s picture

This patch has a couple of issues, one is mentioned, but just adding more explanation.

1. Currently to use this patch, you must set your content language the same as your Interface language. So, you have to translate both the admin interface and the content. If your users have a different language for the admin interface, this patch doesn't work for them.

2. Content types have to have translation enabled even though it's untranslated. Even if a node is not translatable, it still has a valid path in each language and that path should be used to prevent an untranslated node from switching a user's chosen language.

greg boggs’s picture

Here's a patch of the latest work in the MR

fernly’s picture

StatusFileSize
new478 bytes

Patch based on latest MR version against Drupal 11.1.7. The MR still needs to be targeted to 11.x.

shabana.navas’s picture

The patch in #57 had a copy/paste error. Re-rolling it with the fix.

fernly’s picture

StatusFileSize
new11.54 KB

Apparently the patch in #60 was practically empty. Created new patch to be applicable to D11.2.3.

I moved entity_test_entity_prepare_view() hook to entity_test_entity_view_alter() as it added $build array keys, but $build is not existing in this entity_test_entity_prepare_view() hook in the first place, so this never worked.

fernly’s picture

StatusFileSize
new10.98 KB

Removed entity_test_entity_view_alter()in favor of the also added entity_test_entity_display_build_alter().

fernly’s picture

StatusFileSize
new10.79 KB

Removed unused use statements in entity_test.module.

jelle_s’s picture

StatusFileSize
new10.79 KB

Rerolled patch for 11.2.10

akalam’s picture

StatusFileSize
new9.75 KB

Rerolled against 11.3

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

tuwebo’s picture

Taking a look at this one while working on Drupal 11.3.x, I think there is one scenario that the patch would fail:

  • Enable 3 languages: en, fr, pt-pt.
  • Set content language detection to URL (path prefix). Add English as default language and "en" for English path prefix ( "en" is optional)
  • Set interface language detection so admin pages use the user’s preferred admin language first, then URL.
  • Set the test user’s preferred_admin_langcode to en.

This is the manual test i am doing, and failing on 4 and 5. NOTE: These steps will fail in both cases (setting the default language path prefix to "en" or when leaving it empty).

  1. Create one node in English only.
  2. Visit /pt-pt/admin/content.
  3. Confirm there is one row for that node.
  4. Confirm the View link does not point to /pt-pt/node/{nid}.
  5. Confirm it points to /en/node/{nid} or /node/{nid} (if you set an empty string for english default language)
  6. Add a French translation for the same node.
  7. Visit /pt-pt/admin/content again.
  8. Confirm both rows still appear correctly.
  9. Confirm the English row links to /en/node/{nid}.
  10. Confirm the French row links to /fr/node/{nid}.

I am taking a look at it