Problem/Motivation
When the language is negotiated via a user-preference and url-prefix, and the user sets an administrative language, the LanguageInterface::TYPE_URL language-type seems to be negotiated to the sites default-language.
The most visible result is that when accessing the site via eg. /da/page1 (da being the url-prefix for Danish) - the main-menu on that page, is rendered with /en/ prefixes.

Steps to reproduce:
- Install a english site with the standard profile
- Enable the language module
- Access /admin/config/regional/language and add a language (eg Danish)
- Access /admin/config/regional/language/detection and enable "Account administration pages"
- Configure the URL detection to use /en as prefix for english
- Add an article and enable "Provide a menu link" so that a link is added to the Main Menu
- Access /en and verify that the main-menu tabs on the front-page links to the article with a /en prefix
- Access /da and verify that the main-menu tabs on the front-page links to the article with a /da prefix
- Edit the users preferences, and set "Administration pages language" to any language (eg danish)
- Access /en and /da and see that no matter what prefix is used, all menu-links are prefixed with /en (the default language)
In the last step I would have expected no change from the previous visits to the front-page as I have only changed a configuration relevant for the admin-interface. If the preference is set to "No preference" the prefixes behaves normally.
The following code in ConfigurableLanguageManager seems to be the culprit. The first time a language-type is requested, a initialization flag is set and the initialization is begun. As long this flag is set, the sites default language is returned - this seems like a sensible precaution in case the initialization itself requests a language. When the initialization is completed, the temporary value is replaced with negotiated value.
The problem is that this flag affects all language-types so that if another language-type is requested, a "temporary" value is again set - but as we then hit the initialization-flag, it is never overwritten, and is then made permanent.
public function getCurrentLanguage($type = LanguageInterface::TYPE_INTERFACE) {
if (!isset($this->negotiatedLanguages[$type])) {
// Ensure we have a valid value for this language type.
$this->negotiatedLanguages[$type] = $this->getDefaultLanguage();
if ($this->negotiator && $this->isMultilingual()) {
if (!$this->initializing) {
$this->initializing = TRUE;
$negotiation = $this->negotiator->initializeType($type);
$this->negotiatedLanguages[$type] = reset($negotiation);
$this->negotiatedMethods[$type] = key($negotiation);
$this->initializing = FALSE;
}
// If the current interface language needs to be retrieved during
// initialization we return the system language. This way string
// translation calls happening during initialization will return the
// original strings which can be translated by calling them again
// afterwards. This can happen for instance while parsing negotiation
// method definitions.
elseif ($type == LanguageInterface::TYPE_INTERFACE) {
return new Language(array('id' => LanguageInterface::LANGCODE_SYSTEM));
}
}
}
return $this->negotiatedLanguages[$type];
}
Proposed resolution
A possible solution would be to maintain seperate flags for each language-type. Another would be to return the default-language during initialization straight away, without persisting it if we're initializing. The first approach seems the best.
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | interdiff.txt | 1.1 KB | xano |
| #20 | drupal_2772891_20.patch | 9.81 KB | xano |
| #19 | drupal_2772891_19-test-only.patch | 7.88 KB | xano |
Comments
Comment #2
danquah commentedAdded patch that implements the first suggestion, maintaining seperate flags for each language-type.
Comment #3
danquah commentedComment #4
danquah commentedComment #5
xanoI improved some code comments, and fixed the name of the variable passed on to
unset(); For clarity, I also converted the associative array to use boolean values (the keys are important, the values aren't).Comment #6
lauriiiThe fix itself looks good. I think we should add test coverage for this.
The short description should fit on one line.
Comment #7
xanoThis addresses all feedback from #6.
Comment #10
xanoComment #12
xanoApparently patch upload order matters?
Comment #14
danquah commentedFYI the test is failing because the prophecy language-object tracks the number of methodcalls made on int, which changes the object and leads to uasort complaining that the custom comparison-function over in Drupal\Core\Language\Language changes the array-elements it is comparing.
Apparently one way to fix it is to fall back to plain old phpunit mocks.
https://github.com/phpspec/prophecy/issues/161
Comment #15
lauriiiI think the suggestion from @danquah is totally worth testing
Comment #16
danquah commentedOK, think I have a working test now, I'll post it later today.
Comment #17
danquah commentedChanges
Comment #19
xano@danquah: That is some excellent documentation you added there! Thank you for explaining our test approach so thoroughly!
I fixed some minor language issues, and I tried to clarify the comment on what exactly happens if Prophecy is used. I hope that's an improvement.
Comment #20
xanoSmall docs update, because I misunderstood @danquah's research.
Comment #24
wannesdrThis issue is a duplicate of #2189267 . Does anyone know whether any work done here can be transferred?
Since it is a duplicate I will close it. Any new contributions or info must go to #2189267
Comment #25
wannesdrComment #26
wannesdr