At first I had some troubles, getting the metatags in the correct language of the current node while indexing.
Trying to find some hints on how search_api does things with its rendered html output, I found the same problem there too.

What I want to do
Index the rendered HTML output of multilingual entities

What I expect
Get the HTML of the page, with everything being rendered in the entities activeLanguage language

What happens instead
The (most) rendered fields of the entity are in the correct language, the entities activeLanguage at index time.
Other parts of the HTML Output however are in the current active backend language.

I have tested this only with running the index job on the drupal backend or the drush console,
but I do believe it also applies to cronjobs but not while updating & indexing immediatly (if the setting is used), it only updates ONE node translation, and not several at ones.

Some more info on the setup:
on the "Detect Languages" configuration site, only Interface Language Negotiation is active, with URL and Browser being set.
there are 4 languages active for each node.
i have checked if the indexed nodes have the correct translation for their set language.

Investigations
Any time some rendering happens, this might have an effect.
So the actual problem was this kind of code: (this one is taken from MetatagManager::generateRawElements()
\Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();

Also entity reference field which get rendered on dont know their language yet, they might also ask the languageManager

In the end, I followed the second answer, switching out the LanguageNegotiater temporarly
https://drupal.stackexchange.com/questions/156094/switch-a-language-prog...
Using a custom Negotiator while getting the metatags
and for the rendering the entity, I extended the RenderedItem and overwrote the function addFieldValues and added the same lines.

Some more thoughts
This 'bug' occurs only when some code would want to render the page in a different language than negotiated from the languageManager.. Since normally, it is a good idea to ask the languageManager "what is the current language I should render things in?",.. But since, in the case of search_api, the current entity rendered might have a different activeLanguage than the languageManager might return, things dont fit together anymore.

Usually other factors determine which language the entity will be rendered in, through the conditions you can activate (Browser, URL, User, System, ...) Maybe there should be a Condition for the languageManager which respects the current rendered entities activeLanguage? Or you can set it programmatically only and it gets ignored usually?

This might not only affect search_api but any other case if something is rendered in a different language than negotiated by the languagemanager during the same "page call".

CommentFileSizeAuthor
#43 3035977-rendered-html-output-42.patch6.26 KBHitchShock
#39 3035977-38.patch7.12 KBmeanderix
#29 3035977-29--rendered_item_language.patch5.22 KBCyberwolf
#23 3035977-23--D7--fix_multilingual_site_entity_rendering.patch3.75 KBdrunken monkey
#23 3035977-23--D7--fix_multilingual_site_entity_rendering--interdiff.txt2.75 KBdrunken monkey
#22 interdiff-D7-3035977-7-22.diff3.67 KBdas-peter
#22 search_api-D7-rendered-entity-language-fix-3035977-22.patch3.56 KBdas-peter
#20 3035977-20--rendered_item_language.patch4.76 KBdrunken monkey
#20 3035977-20--rendered_item_language--interdiff.txt4.76 KBdrunken monkey
#19 3035977-19.patch3.91 KBlegolasbo
#19 interdiff-3035977-18-19.txt1.23 KBlegolasbo
#18 3035977-18.patch3.78 KBlegolasbo
#18 interdiff-3035977-15-18.txt4.13 KBlegolasbo
#15 3035977-07-d8.patch3.16 KBvierlex
#13 3035977-06-d8.patch2.91 KBvierlex
#12 3035977-05-d8.patch2.94 KBvierlex
#11 3035977-04-d8.patch2.92 KBvierlex
#10 3035977-03-d8.patch2.92 KBvierlex
#9 3035977-02-d8.patch2.92 KBvierlex
#8 3035977-01-d8.patch3.27 KBvierlex
#7 search_api-D7-rendered-entity-language-fix-3035977-7.patch2.25 KBdas-peter

Issue fork search_api-3035977

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:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

vierlex created an issue. See original summary.

vierlex’s picture

Issue summary: View changes
echo15’s picture

Issue tags: +epam-contrib-2019.03
linichalexey’s picture

Issue tags: -epam-contrib-2019.03
vierlex’s picture

Issue summary: View changes
drunken monkey’s picture

Component: General code » Plugins

Can you specify in a bit more detail what parts of the HTML output use the wrong language?
Also, if you say yourself this might be a general issue, did you also raise it in the Core issue queue? Would of course be great if this could be fixed there.
Otherwise: What do you suggest? Switching out the language negotiator temporarily in our plugin? Might work, but sounds risky. Don’t have any other ideas, though, so it seems our best shot. Can you provide a patch?

das-peter’s picture

I've come across the same issue with D7. There we've the same problem - passing in the language to entity_view() won't necessarily have effect on lots of things e.g. the rendering of links (url() still relying on $GLOBALS['language_url'])

Switching out the language negotiator temporarily in our plugin? Might work, but sounds risky.

For D7 this seems the only way too. Even thought we can't guarantee that we won't have weird effects due static caches which are generally not language aware and hence could make trouble. However, the output is definitely better with the adjustment.

Unfortunately can only provide a D7 patch for now.

vierlex’s picture

Got around and created a patch!

As always, I haven't figured out yet why they won't apply when running through the Testbot :(

In any case, I read through my original issue and wanted to rephrase it, and give a specific example:

In our case, we had an entity in 4 different languages.
As a User, when viewing the entity, there are also View Blocks, which display other entities.
These specific entities activeLanguage would always be the default language, it didn't matter if they
had a translation in the same language like the parent entities activeLanguage!

How i think it all works in the background:
Normally, when viewing a site, one entity will be rendered, which asks the LanguageNegotiator Service which language to use.
As seen in the Drupal Backend UI, there are different Conditions to choose from with varying weight: Browser, URL, etc,..

In the case of Search API, these conditions are not available. Search API renders "programmatically".
So when the LanguageNegotiator is asked which language to render in, it will always return the current active backend language.
Current active backend language?
Indeed, if your Backend supports multiple languages, starting the index process through the UI will use that language!
If indexed via a cronjob, it will use the default language.

As I mentioned, the current entity to be indexed will not be affected, the activeLanguage of that entity will be used for rendering BUT
any nested/added "things", in my case View Blocks specifically, but possibly tokens? entity reference fields?, will ask the LanguageNegotiator which language to use during rendering which will return the current active backend language/default language.

The current patch provides a seperate LanguageNegotiator which is switched out before rendering the item and switched back when finished.

There may be a more clever solution by implementing and adding a LanguageNegotiator Condition ONLY (not a whole Negotiator) which triggers only for "programmatically rendered" items, and trys to apply the current entity-to-be-indexed's activeLanguage for every other things which wants/needs to be rendered.

Does that make sense? :)

vierlex’s picture

Hm trying again, maybe I can get this to work.

vierlex’s picture

vierlex’s picture

Sorry for spamming, but I really want to figure this out now.

vierlex’s picture

vierlex’s picture

Status: Needs review » Needs work

The last submitted patch, 13: 3035977-06-d8.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

vierlex’s picture

at least it applies!

and the final thing missing is the dependency for the language module.

updt: alright, so I added the drupal:language module to the dependencies in the .info.yml file
but some tests still complain that the service @plugin.manager.language_negotiation_method isn't there..

For anyone who want to apply to try out the patch: enable the language module in core.

keats76’s picture

Timely addition. Thanks @vierlex!

I'm using it in a dev environment and it seems to be working for me on 8.7.1 with search api 8.13.

I'll let you know if I run into any issues.

Thanks!

legolasbo’s picture

Assigned: Unassigned » legolasbo

Taking a look at those failing tests.

legolasbo’s picture

I've found a way to swap out the language negotiator without having to add language as a dependency. This resolves most of the test failures, but there are still some failures remaining when I run the tests locally. I'm out of time today, so posting my work in progress for now.

legolasbo’s picture

Status: Needs work » Needs review
FileSize
1.23 KB
3.91 KB

I think this should fix the last failing tests.

drunken monkey’s picture

As always, I haven't figured out yet why they won't apply when running through the Testbot :(

Glad you could make it work! You’ve probably realized it by now, but the key is to have something that will be accepted by git apply. Using git diff is the simplest way to make sure of that.
Your initial patch missed the usual a/*/b/* prefixes for file paths, so git apply removed the leading src/ part of the paths – which of course breaks them. Not sure what was wrong with your later patches – maybe not using the current dev version?

In any case, thanks a lot for posting your work!
Some of the code was not quite perfect in my opinion, and it also ignored the Drupal coding standards partially. Please see/test/review my attached revision!
Two more things:

  1. This still needs a regression test for verifying that a) there was a problem and b) the patch fixes it. This will make sure we’re actually solving a problem, and that we don’t break this again in the future. Probably, it can just live in the existing RenderedItemTest class – install the language module, set the current language to English and then try to render a French translation of a node with comments (or term references, or something like that).
  2. As I’m still not convinced this won’t break things for some sites, and as it might not actually be needed on them, should we maybe make this configurable on the processor? Or would you say, as long as the Language module is present it’s pretty sure people will want this?

@ das-peter/#7: Thanks for posting, looks very good! While tests are not necessary for D7, my other comment still applies there, too: Do you think we should maybe make this configurable?

drunken monkey’s picture

Status: Needs review » Needs work

“Needs work” status for tests.

das-peter’s picture

@ das-peter/#7: Thanks for posting, looks very good! While tests are not necessary for D7, my other comment still applies there, too: Do you think we should maybe make this configurable?

...should we maybe make this configurable on the processor? Or would you say, as long as the Language module is present it’s pretty sure people will want this?

Thanks for the feedback :)
I think it's fair to be cautious regarding existing sites. Making it configurable sounds like a good way to go. I've update the D7 patch to make the behavior configurable and it also sets a "sensible" default. This means for existing configurations the language switch will be disabled unless explicitly enabled but for new configurations it will be enabled by default upon creation.

drunken monkey’s picture

Thanks for the updated patch, Peter! Looks very good now.
Just expanded the form field description to hopefully cause less confusion to everyone who didn’t follow this issue and moved the code around a bit to look cleaner to me.
Otherwise, RTBC, if you agree? Thanks again, in any case!

drunken monkey’s picture

Status: Needs review » Needs work

Back to NW for D8 tests.

das-peter’s picture

Otherwise, RTBC, if you agree? Thanks again, in any case!

Sure! Thanks for the review and the pro-active feedback, much appreciated!!

drunken monkey’s picture

Great, thanks for the feedback!
Committed the D7 patch, then. Thanks again!

D8 still needs such a final push. Any takers?

maacl’s picture

Thanks for your work, the #20 patch solves my problem of the default language is used while rendering twig files, resulting in wrong tranlslations when the t-filter is used, urls linking to wrong language or untranslated dates. My use case is storing the rendered search results in solr and fetch them from there. (D8.6, Search-API 1.13, Search API Solr Search 3.1)

Edit: I have to correct myself, the t-Filter is still using the default language. A workaround is to pass the String via preprocess or use the trans-function, and pass the now correct available langcode from preprocessor:

{% set link_title %}
  {% trans with {langcode: current_langcode} %}
    Details anzeigen
  {% endtrans %}
{% endset %}
Cyberwolf’s picture

Patch 20 for D8 works well for us when indexing via the UI, however on the command line it fails to make sure that the rendered html output is in the right language.
I was able to track the cause down.
Somewhere after the static negotiator (introduced by the patch) is put into the language manager, the language_negotiator service gets initialised by the service container (it seems to be somewhere in the dependency tree of content moderation's EntityOperations class). And the language_negotiator service initialises the language manager, setting itself again as the negotiator and thus replacing our static negotiator again.

core/modules/language/language.services.yml:

  language_negotiator:
    class: Drupal\language\LanguageNegotiator
    arguments: ['@language_manager', '@plugin.manager.language_negotiation_method', '@config.factory', '@settings', '@request_stack']
    calls:
      - [initLanguageManager]

core/modules/language/src/LanguageNegotiator.php,

/**
   * Initializes the injected language manager with the negotiator.
   *
   * This should be called right after instantiating the negotiator to make it
   * available to the language manager without introducing a circular
   * dependency.
   */
  public function initLanguageManager() {
    $this->languageManager->setNegotiator($this);
  }

To solve this, I've put one line of code before setting up the static negotiator, to initialize the language_negotiatior service. Updated patch attached.

meanderix’s picture

After installing the patch, I'm experiencing the issue described here for some of my nodes: https://www.drupal.org/project/drupal/issues/2869347

meanderix’s picture

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

johnzzon’s picture

We've been using the patch in #29 but has noticed a bug where translations are not rendered in the correct language. After a lengthy debug session I found that the string_translation service is initialized with the default language so we need to update that as well.

I created an issue fork, rerolled the patch from #29 and then implement my changes.

https://git.drupalcode.org/issue/search_api-3035977/-/compare/8.x-1.x......

johnzzon’s picture

drunken monkey’s picture

I don’t think there is a need for patches when we already have a fork? Anyways, also no harm in creating an MR before this is completely done. That way, there is also constant automated testing. (Even though something seems to have gone wrong in the last test …)

Anyways, thanks a lot for working on this!
I looked into it, and your change does make sense, even though the comment explains it a bit poorly, and you forgot to reset the language afterwards. I hopefully fixed both of those things now, plus made a few cosmetic changes.

Also, apart from all that, as RenderedItem is a plugin, it uses dependency injection, so we shouldn’t get those services via \Drupal::… but through DI.
So, in addition to the automated tests, this would also need to be fixed before this can be merged.

drunken monkey’s picture

johnzzon’s picture

Good catch!

meanderix’s picture

FileSize
7.12 KB

Here's the original patch from #35. It looks like the MR is updated dynamically, and the MR-patch can no longer be applied cleanly with composer-patches.

kevinquillen’s picture

Last patch fixes the issue in the OP completely for me using rendered item indexing!

d.steindl’s picture

Same, patch from #39 solves the issue — Thanks!
We had the problem, that the UI translations were rendered in the language negotiated by Drupal --> so always German.

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

HitchShock’s picture

Reroll the MR and make a patch for it.

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

fjgarlin’s picture

drunken monkey’s picture

@fjgarlin: Thanks a lot for your work on that!
However, please do such work in #3396064: (Try to) fix the GitLab CI in the future, it’s quite unrelated to this ticket.

fjgarlin’s picture

@drunken-monkey 100% you’re right, apologies. I was meant to undo the changes but totally forgot. My bad.

I arrived at this issue following the related ones and just wanted to prove something (which I did) in the related issues.

I’ll undo my commits Monday first thing (away from computer right now).

fjgarlin’s picture

My changes are now reverted.

drunken monkey’s picture

#3380219-2: Rendering is not changing the current content and string translation language is a duplicate that contains a patch with a different approach, not needing a new service class (which I’d count as a big plus). Please try it out and see whether this doesn’t also resolve this problem for you.

In either case, though, we still need a regression test for this.