Problem/Motivation

Language negotiation is handled by the LanguageNegotiator class. It should not be making runtime changes to the Language or ConfigurableLanguage object.

Proposed resolution

Remove method_id from Language and ConfigurableLanguage

Remaining tasks

Review patch

User interface changes

No.

API changes

  • Add ConfigurableLanguageManagerInterface::getNegotiatedLanguageMethod($type = LanguageInterface::TYPE_INTERFACE) so that the system can get information as to how language negotiation has occurred for a particular language type.
  • Change LanguageNegotiatorInterface::initializeType($type) to support the above addition.

Original report

#2226533: Changes to the Language class due to the LanguageInterface (followup) is setting method_id to protected was
diff --git a/core/modules/language/src/LanguageNegotiationMethodBase.php b/core/modules/language/src/LanguageNegotiationMethodBase.php
index 3f48f7a..8fc98e9 100644
--- a/core/modules/language/src/LanguageNegotiationMethodBase.php
+++ b/core/modules/language/src/LanguageNegotiationMethodBase.php
@@ -63,7 +63,7 @@ public function setCurrentUser(AccountInterface $current_user) {
*/
public function persist(BaseLanguageInterface $language) {
// Remember the method ID used to detect the language.
- $language->method_id = static::METHOD_ID;
+ $language->setNegotiationMethodId(static::METHOD_ID);
}

}

but #2334763: Tidy up of LanguageInterface - removal of setters and other unnecessary methods is taking out method_id and setNegotiationMethodId()

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

YesCT’s picture

similar in

diff --git a/core/modules/language/src/LanguageNegotiator.php b/core/modules/language/src/LanguageNegotiator.php
index eb1df93..b00b3fc 100644
--- a/core/modules/language/src/LanguageNegotiator.php
+++ b/core/modules/language/src/LanguageNegotiator.php
@@ -152,7 +152,7 @@ public function initializeType($type) {
if (!$language) {
// If no other language was found use the default one.
$language = $this->languageManager->getDefaultLanguage();
- $language->method_id = LanguageNegotiatorInterface::METHOD_ID;
+ $language->setNegotiationMethodId(LanguageNegotiatorInterface::METHOD_ID);
}

return $language;

martin107’s picture

Status: Active » Needs review
FileSize
647 bytes

It a howler when you see it. Simple to fix.

alexpott’s picture

FileSize
7.47 KB

Actually there is no need to set method id on the language and in fact we probably should not since languages are what is negotiated and the method is what is use to do the negotiation - it's not really a property of the language at all. It's a state of the languageNegotiator - i.e. what method have been used to negotiate which languages.

Patch attached removes the method_id property from both Language and ConfigurableLanguage and adds a method to LanguageNegotiator to retrieve the current state.

alexpott’s picture

Title: setting method_id in LanguageNegotiationMethodBase, handle it differently » The negotiated method_id does not need to be sorted on the Language or ConfigurableLanguage object.
Issue summary: View changes
FileSize
8.88 KB
6.5 KB
+++ b/core/modules/language/src/HttpKernel/PathProcessorLanguage.php
@@ -110,7 +110,6 @@ public function processOutbound($path, &$options = array(), Request $request = N
     if ($this->multilingual) {
-      $this->negotiator->reset();
       $scope = 'outbound';

I'm not 100% certain about this change - obviously we don't have test coverage for it since the patch is green but the patch attached moves stuff around so you can get the negotiation method from the ConfigurableLanuageManager. This feels correct since it is responsible for integrating all the language subsystems. This means that the negotiator remains resettable.

Gábor Hojtsy’s picture

Status: Needs review » Needs work
Issue tags: +sprint

I agree with the goals, the language indeed does not have anything to do with how it got to be born :) AKA the negotiator. But we need it for at least testing that it works right and possible for other reasons in the future. I have concern with the implementation though.

+++ b/core/modules/language/src/ConfigurableLanguageManager.php
@@ -204,7 +204,7 @@ public function saveLanguageTypesConfiguration(array $values) {
-      $this->negotiatedLanguages[$type] = $this->getDefaultLanguage();
+      $this->negotiatedLanguages[$type][LanguageNegotiatorInterface::METHOD_ID] = $this->getDefaultLanguage();

@@ -224,7 +224,7 @@ public function getCurrentLanguage($type = LanguageInterface::TYPE_INTERFACE) {
-    return $this->negotiatedLanguages[$type];
+    return reset($this->negotiatedLanguages[$type]);

@@ -453,4 +453,15 @@ public function getStandardLanguageListWithoutConfigured() {
+  public function getNegotiatedLanguageMethod($type = LanguageInterface::TYPE_INTERFACE) {
+    if (isset($this->negotiatedLanguages[$type])) {
+      reset($this->negotiatedLanguages[$type]);
+      return key($this->negotiatedLanguages[$type]);
+    }
+  }

While it is a neat trick to store the method ID on an array element, this array will only ever contain one element, so the array nesting is used to store metainfo on how the negotiation got the value. Why not store it in another property of the configurable language manager for each type? Sounds less nifty tricks but maybe easier to understand?

alexpott’s picture

Status: Needs work » Needs review
FileSize
2.93 KB
9.64 KB

How about something like this. I'm keen to keep LanguageNegotiatorInterface::initializeType($type) returning an array since this means the typehint works. And anything else that uses it can get the method id just as easily.

Gábor Hojtsy’s picture

That looks better, thanks. As for your concern on the change in #4, you are not making that change anymore in recent patches? I am not sure why it was added/removed? :)

alexpott’s picture

In the changes from #3 to #4 I moved stuff to the LanguageManager from LanguageNegotiator so yep no longer a concern.

Gábor Hojtsy’s picture

Status: Needs review » Reviewed & tested by the community

All good with me then, thanks!

martin107’s picture

I can see a minor snafu by running this patch through a lint checker...

Method 'getNegotiatedLanguageMethod' not found in class

from this code in language_test.module

drupal_set_message(t('Language negotiation method: @name', array('@name' => \Drupal::languageManager()->getNegotiatedLanguageMethod())));

From the annotations in Drupal.php the return from \Drupal::languageManager() is a \Drupal\Core\Language\LanguageManagerInterface object
and not \Drupal\language\ConfigurableLanguageManagerInterface

alexpott’s picture

@martin107 that's not really a snafu - the ConfigurableLanguageManger implements LanguageManagerInterface as well as ConfigurableLanguageManagerInterface but \Drupal can only be sure that it returns a object that implements LanguageManagerInterface. There is no way of changing \Drupal return types if a module is enabled that replaces a service that is returned by \Drupal and extends the original interface. That is exactly what the language module does.

martin107’s picture

@alexpott thanks for the update

YesCT’s picture

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

read through this. Interested because it effects #2226533: Changes to the Language class due to the LanguageInterface (followup)

found two small nits while reading. fixed. The standards got updated ... a year ago taking out the need to restate the Default value on @params, when it is just repeating the info in the function signature. #1916652: defaults to in 1354 doc standards for optional args
The other was a double blank line.

Should still be rtbc.

alexpott’s picture

Status: Needs review » Reviewed & tested by the community

Should still be rtbc.

I agree :)

alexpott’s picture

And thanks @YesCT - I didn't know about that update to the standard.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.0.x, thanks!

  • catch committed 801d850 on 8.0.x
    Issue #2337847 by alexpott, YesCT, martin107: The negotiated method_id...
Gábor Hojtsy’s picture

Issue tags: -sprint

Status: Fixed » Closed (fixed)

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