Problem/Motivation

Every code which deals with a single content entity in a controller has to load the proper language manually, because we always load the content entity in its original form in upcasting.

Proposed resolution

We should just load the right translation in the routing level, same as we do for configuration entities, which we only load in the original language in the upcast when in an admin context.

Remaining tasks

- Review.
- Commit.

User interface changes

None.

API changes

None. But the expectation about the loaded entity will change, it will load the entity in the proper language for the page.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Berdir’s picture

Assigned: Unassigned » plach
Issue tags: +Entity Field API

This is part of a larger problem on how we now want to deal with entity translations, see the comments #125 and #145 in #2019055: Switch from field-level language fallback to entity-level language fallback, among others. We need a meta issue to discuss and implement the whole thing.

We can use this or create another issue, and the meta will need to be added to #2095603: [meta] Complete Entity Field API :)

plach’s picture

Issue tags: +D8MI, +language-content, +sprint, +API change
Gábor Hojtsy’s picture

Issue tags: -sprint

If this is an API change significant enough, we may need to elevate this to Critical and beta blocker? Since nobody was working on this for 5 months, I'm removing from the sprint board as a start at least.

plach’s picture

I am not sure this will be an API change itself, but it might imply a change in how code is written. I am sorry I was not able to work on this, but I really feel I should be the one at least starting the work. I will try to get back here as soon as we are done with the entity storage stuff.

dawehner’s picture

Status: Active » Postponed

This one is probably blocked based upon #1906810: Require type hints for automatic entity upcasting, given that it would change similar code, potentially.

plach’s picture

Issue tags: +beta target

We should really try to get this in before beta.

tim.plunkett’s picture

Status: Postponed » Active

That went in.

dawehner’s picture

Status: Active » Needs review
FileSize
2.62 KB

This is a really naive approach. Maybe we want to have this logic in another request listener.

Status: Needs review » Needs work

The last submitted patch, 8: entity_upcasting-2160965-8.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
1.45 KB

There's a method for this that we can use.

Gábor Hojtsy’s picture

Title: Load the translated entity in the upcast level » Content entities are not upcast in the page language, inconsistent with config entities
Category: Task » Bug report
Issue summary: View changes
Issue tags: +sprint

We already do this for config entities by default. It would be useful to match behaviour with content entities then. Updated issue summary and title.

Status: Needs review » Needs work

The last submitted patch, 10: entity_upcasting-2160965-10.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
1.45 KB

Kept the interface check to avoid that NULL is passed into that method.

dawehner’s picture

@berdir
Good idea to use getTranslationFromContext ... Is there a specific reason why you added the check for the TranslationInterface? I would argue that the method on the entity manager should not fail in case you pass in some other entity, and afaik it should not.

In case you just wanted to fix the NULL case I would recommend to check for EntityInterface given that this seems a little bit more semantically correct.

Status: Needs review » Needs work

The last submitted patch, 13: entity_upcasting-2160965-13.patch, failed testing.

Berdir’s picture

Wow, did expect more fails, although I have no clue what the delete thing there exactly means ;)

Yeah, possibly checking for EntityInterface or just != NULL might make more sense, getTranslationFromContext() does have the same interface check internally, checking for this interface would avoid the additional step into that function, though.

plach’s picture

Assigned: plach » Unassigned

This looks promising, thanks!

+++ b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
@@ -39,7 +40,11 @@ public function __construct(EntityManagerInterface $entity_manager) {
+        $entity = $this->entityManager->getTranslationFromContext($entity);

I am wondering whether it would make sense to pass an entity_upcast context operation, to allow modules to perform a more targeted altering.

herom’s picture

Status: Needs work » Needs review
FileSize
1.66 KB
2.06 KB

The delete issue is caused by the following lines. Now, the entity is loaded in the same language that it's removing, so the save will fail.

=== core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php
   public function submitForm(array &$form, array &$form_state) {
     // Remove the translated values.
     $this->entity->removeTranslation($this->language->id);
     $this->entity->save();

I don't know the best solution here, but I tried falling back to the default language in removeTranslation; let's see if the tests are happy.

There is still a fail in EntityTranslationFormTest.php, which I couldn't figure out. It's showing "Access Denied" when visiting [langcode]/node/[entity_id]/edit in the test.

Status: Needs review » Needs work

The last submitted patch, 18: entity_upcasting-2160965-18.patch, failed testing.

plach’s picture

+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -773,6 +773,9 @@ public function removeTranslation($langcode) {
+        $this->activeLangcode = LanguageInterface::LANGCODE_DEFAULT;

We should not be changing the active language of a translation object, instead we should just instantiate the original object in the CT remove translation controller.

yched’s picture

(tangential : the internal language handling in ContentEntityBase could be vastly simplified - #2142885: Simplify ContentEntityBase internal field storage by removing special treatment for LANGCODE_DEFAULT)

herom’s picture

Status: Needs work » Needs review
FileSize
2.07 KB
2.86 KB

update.

plach’s picture

Status: Needs review » Needs work

Thanks, this looks almost good to me!

+++ b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
@@ -39,7 +40,11 @@ public function __construct(EntityManagerInterface $entity_manager) {
+      if ($entity instanceof EntityInterface) {

This should be TranslatableInterface.

@Berdir:

What do you think about #17?

herom’s picture

correcting based on #23.

plach’s picture

RTBC here. Sasha, Daniel?

Berdir’s picture

We went back and forth on that instanceof earlier already. I don't really care, but the thing is that TranslatableInterface does not extend EntityInterface and that method is type hint on EntityInterface (and then internally checks for TranslatableInterface).

The result is that this PHPStorm highlights this as incorrect. That said, I don't care which one it is. If you're OK with this then I am. A bit surprised that there wasn't more that we had to do.

dawehner’s picture

I guess we could just typehint for both, what do you think?

Berdir’s picture

Or that. The main reason that I added the interface check at all is to avoid passing NULL (for non-existing entities) to that method. So just "if ($entity) {" would work too. Either way, a comment would probably make sense, something like "Ensure that translatable entities are set to the current language", or something like that. Suggestions on the wording and exact approach, @plach?

plach’s picture

What about this?

If the entity type is translatable, ensure we return the proper translation object for the current context.
dawehner’s picture

I am fine with that comment.

Rerolled with --3way

plach’s picture

Status: Needs review » Reviewed & tested by the community

Looks good, thanks

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

Let's get some test coverage of this.

fran seva’s picture

I'm working in the test coverage.
The patch#30 needs reroll

diff --cc core/modules/system/src/Tests/Entity/EntityTranslationFormTest.php
index 246b053,e7d5334..0000000
--- a/core/modules/system/src/Tests/Entity/EntityTranslationFormTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityTranslationFormTest.php
@@@ -106,7 -106,8 +106,12 @@@ class EntityTranslationFormTest extend
  
      // Create a body translation and check the form language.
      $langcode2 = $this->langcodes[1];
++<<<<<<< HEAD
 +    <strong>$node->getTranslation($langcode2)->body->value = $this->randomMachineName(16);</strong>
++=======
+     <strong>$node->getTranslation($langcode2)->body->value = $this->randomName(16);</strong>
+     $node->getTranslation($langcode2)->setOwnerId($web_user->id());
++>>>>>>> applying reroll 30
      $node->save();
      $this->drupalGet($langcode2 . '/node/' . $node->id() . '/edit');
      $form_langcode = \Drupal::state()->get('entity_test.form_langcode') ?: FALSE;

I have resolved the conflict changing using randomMachineName(16) instead randomName(16).
The reroll is attached.

fran seva’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 33: reroll-entity_upcasting-216095-33.patch, failed testing.

Berdir’s picture

Issue tags: +Needs reroll, +Novice

Tagging novice for reroll and tests.

A test should check that a route that receives an entity as a route parameter should get it in the right language. So enable a module with entities, add a test route in a test module that accepts {node} or similar, then output the value of $entity->language()->getId() in the controller and check that is correct when access in diferent languages.

robertdbailey’s picture

Status: Needs work » Needs review
FileSize
2.73 KB

Rerolled patch. Will work on tests next.

realityloop’s picture

Issue summary: View changes
Issue tags: +Amsterdam2014
star-szr’s picture

Issue tags: -Needs reroll
pp’s picture

I enable Language, Content translation module.
Add a language
Add a test text field to account
Edit Admin profile and set the test text to English
Try to translate Account, and set test text to Hungarian
Test text is always Hungarian. I try user/1 and hu/user/1 path.

How do I test/review this issue?

broon’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
1.42 KB

I wrote a simple demo module for that, reporting the entity language of nodes when calling /demo/entup/{nid}.

It returns Node language ID: en for all three languages (en,de,hu) in my test system when calling one of

/demo/entup/1
/de/demo/entup/1
/hu/demo/entup/1

Applying the patch via patch -p1 < entity_upcasting-216095-38.patch runs through without issues and applies correctly.
The output of the module now reflects the correct language

/demo/entup/1 => Node language ID: en
/de/demo/entup/1 => Node language ID: de
/hu/demo/entup/1 => Node language ID: hu

See attached archive for using the demo module to generate the output mentioned above.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

It would be great to have an automated test for this.

robertdbailey’s picture

Assigned: Unassigned » robertdbailey
robertdbailey’s picture

Assigned: robertdbailey » Unassigned
Status: Needs work » Needs review
FileSize
6.05 KB
3.32 KB

Created a test that adds a language (German in this case) and assigns a prefix (de), and then it creates a node in English and its German translation and then verifies that the right translation is loaded by the router. The tests-only patch should fail, as the patch is not applied there; and the patch with tests should succeed. Please review.

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

@robertdbailey proved to me on his machine that the tests pass/fail as expected. Awesome!
I think we can knock this one out.

The last submitted patch, 45: entity_upcasting-216095-45.patch, failed testing.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 45: entity_upcasting-216095-45_TESTS_ONLY_SHOULD_FAIL.patch, failed testing.

robertdbailey’s picture

reattempting patch test. Previous failure was:

FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] Failed to run tests: failed attempting to get list of tests from run-tests.sh.

The last submitted patch, 45: entity_upcasting-216095-45.patch, failed testing.

robertdbailey’s picture

Status: Needs work » Reviewed & tested by the community

Setting back to RTBC per review by @tstoeckler.

realityloop’s picture

Issue tags: -Novice
plach’s picture

Awesome work, RTBC +1.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed b35d190 and pushed to 8.0.x. Thanks!

  • alexpott committed b35d190 on 8.0.x
    Issue #2160965 by herom, dawehner, robertdbailey, Berdir, fran seva:...
Gábor Hojtsy’s picture

Issue tags: -sprint

Status: Fixed » Closed (fixed)

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