Problem/Motivation

The language_negotiation variables have only been partially converted from the variable system to the configuration system.

Proposed resolution

Convert remaining language_negotiation_* variable calls to the configuration system. Known calls are:

core/includes/language.inc
130 $negotiation = variable_get("language_negotiation_$type", array());
276 $negotiation = variable_get("language_negotiation_$type", array());
297 $negotiation = variable_get("language_negotiation_$type", array());
319 $negotiation = variable_get("language_negotiation_$type", array());
357 foreach (variable_get("language_negotiation_$type", array()) as $method_id => $method) {
404 variable_set("language_negotiation_$type", $negotiation);

core/modules/language/language.admin.inc
93 $enabled_methods = variable_get("language_negotiation_$type", array());
94 $methods_weight = variable_get("language_negotiation_methods_weight_$type", array());
262 variable_set("language_negotiation_methods_weight_$type", $method_weights_input);

core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
80 $negotiation = variable_get("language_negotiation_$type", array());
120 $negotiation = variable_get("language_negotiation_$type", array());
164 $negotiation = variable_get("language_negotiation_$type", array());

Remaining tasks

User interface changes

API changes

This is a child of #1775842: [meta] Convert all variables to state and/or config systems

Several language negioation variables were converted to CMI in #1714462: Convert language negotiation settings to configuration system, however to quote from #33 on that issue

@n3or and I discussed this patch to some lengths, and we definitely intended to keep the scope very limited, and I'm glad those parts landed now.

We also considered to simply re-open and keep this issue in order to perform the additional conversions, but in hindsight, that might not be a good idea, since that always presents a confusing situation for reviewers and other contributors.

Thus, @n3or, or whoever else wants to tackle the further language negotiation variable conversions: Let's make sure to create an issue for those and link to it from here. Thanks! :)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

penyaskito’s picture

Issue tags: +sprint

Keeping track on the D8MI focus board.

Gábor Hojtsy’s picture

#1862202: Objectify the language system definitely moves around a lot of this code. It does not seem to do the CMI conversion. It would be amazing to coordinate with those folks.

Gábor Hojtsy’s picture

Issue tags: +language-config
ianthomas_uk’s picture

Status: Active » Postponed

We shouldn't work on this until #1862202: Objectify the language system is in (which may well resolve the issue anyway)

xjm’s picture

Issue summary: View changes
Issue tags: +beta blocker
xjm’s picture

xjm’s picture

Priority: Normal » Critical
penyaskito’s picture

Status: Postponed » Needs work
ianthomas_uk’s picture

Is it worth rolling #2108599: Convert language_default to CMI into this now? I'll leave that as postponed (now on this one) until someone has had a look at the complexity.

Gábor Hojtsy’s picture

@ianthomas_uk: I think it depends on the resulting patch size and scope :) Looking at that issue (no patch yet, not much discussion), it may be best to fold it here.

sun’s picture

In #2171683: Remove all Simpletest overrides and rely on native multi-site functionality instead, I had to perform the following temporary adjustment to Drupal\language\Tests\LanguageNegotiationInfoTest:

Fixed LanguageNegotiationInfoTest does not properly synchronize child/parent environments + uses weird custom code to install language_test module.

+    // @todo Given the derived global $conf variables state tracking and
+    //   automated rewriting of variable values upon
+    //   LanguageNegotiator::purgeConfiguration(), it is impossible to
+    //   synchronize the global $conf state correctly between the child site and
+    //   the test runner (a refresh of global $conf variables would have to
+    //   happen right within the functional code). Re-enable this assertion when
+    //   converting language negotiation variables into configuration/state.

-    $negotiation = variable_get("language_negotiation_$type", array());
-    $this->assertFalse(isset($negotiation[$interface_method_id]), 'Interface language negotiation method removed from the stored settings.');
+    //$negotiation = variable_get("language_negotiation_$type", array());
+    //$this->assertFalse(isset($negotiation[$interface_method_id]), 'Interface language negotiation method removed from the stored settings.');

In other words, the current usage of ($conf) variables presents an unresolvable conflict between the parent site (test runner) and child site. The variables are correctly updated in the child site, but that operation cannot be reflected or repeated in the test runner, because LanguageNegotiator::purgeConfiguration() relies on global state.

This test is only able to pass in HEAD, because the test uses a very odd way of installing the language_test module, and it repetitively calls into the language_modules_installed() hook implementation again. This causes the LanguageNegotiationInfoTest to be extremely fragile. (I had to spend an entire day with debugging and figuring out what is actually happening in there.)

This issue/patch should revert that temporary adjustment in LanguageNegotiationInfoTest.

That said, after studying the situation post-#1862202: Objectify the language system:

It's not clear to me whether those state-alike variables should be converted into state, or whether this derived information shouldn't actually be removed altogether and dynamically composed by LanguageNegotiator instead?

sun’s picture

Status: Needs work » Active

Also correcting status, there's no patch here.

alexpott’s picture

Status: Active » Needs review
FileSize
14.99 KB

The attached patch does the bare minimum to convert the variables language_negotiation_$type and language_negotiation_methods_weight_$type to CMI.

There are strong arguments that both language types and language detection methods should be configuration entities and leverage the plugin system. This will require a massive UI overhaul and I will create another issue to discuss whether we want to do this.

Status: Needs review » Needs work

The last submitted patch, 13: 2102477.13.patch, failed testing.

sun’s picture

That follow-up issue may or may not be #2174619: Make language negotiation plugins stateless

On the minimal conversion patch here:

  1. Can we rename the parent 'settings' key into 'negotiation'?
  2. Can we skip the 'enabled' parent key and use the pattern of system.module/system.theme config files? I.e.,
    negotiation:
      language_content:
        # Only enabled negotiation plugins, ordered by weight, values = weights.
        language-url: -1
        language-interface: 0
        language-selected: 1
    

    i.e.: ensure order on save.

  3. Can we rename getEnabledMethods() into getEnabledNegotiators()?

    → To be more accurate and in line with the proposed renames in #2174611: Reconsider language system class, method and constant names to make them more developer-friendly

That said, regarding (2) and both of the related language system cleanup issues, it might make even more sense to turn the values of each negotiator into a configuration hashmap; e.g.:

negotiation:
  language_content:
    language-url:
      weight: -1
    language-interface:
      weight: 0
    language-selected:
      weight: 1

That would allow us to store the settings of each negotiator right within this config file later on, as proposed in #2174619: Make language negotiation plugins stateless

alexpott’s picture

Status: Needs work » Needs review
FileSize
9.24 KB
15.11 KB
  1. Sure
  2. I'd rather not tackle this here. This requires changes to the UI since ordering of disabled negotiation methods is currently a feature.
  3. Method renamed.

I really want to avoid UI changes with this patch. At the moment the negotiator configuration is the same for all language types - this is one of the many things that is super confusing about the UI on admin/config/regional/language/detection . To see just how confusing this UI is enable content_translation and then customise the settings for content language detection - if you change the "Selected language" configuration it will change for both the interface and content language types.

As stated in #13 I'd rather leave all of this to a follow up issue so that we can finally remove variable_* functions and have everything in CMI. It makes sense for the followup to be #2174619: Make language negotiation plugins stateless.

Berdir’s picture

  1. +++ b/core/modules/language/language.install
    @@ -5,29 +5,6 @@
    -/**
    - * Implements hook_install().
    - *
    - * Enable URL language negotiation by default in order to have a basic working
    - * system on multilingual sites without needing any preliminary configuration.
    - */
    -function language_install() {
    -  $language_manager = \Drupal::languageManager();
    -  if ($language_manager instanceof ConfigurableLanguageManagerInterface) {
    -    $negotiator = \Drupal::service('language_negotiator');
    -    $types = $language_manager->getLanguageTypes();
    -    $negotiator->updateConfiguration($types);
    -    // Enable URL language detection for each configurable language type.
    -    foreach ($types as $type) {
    -      $negotiator->saveConfiguration($type, array(LanguageNegotiationUrl::METHOD_ID => 0));
    -    }
    -  }
    

    Can we really move this into a config file, as this right now is dynamic and there could be more language types.

  2. +++ b/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php
    @@ -213,8 +212,8 @@ protected function configureFormTable(array &$form, $type)  {
         $negotiation_info = $form['#language_negotiation_info'];
    -    $enabled_methods = variable_get("language_negotiation_$type", array());
    -    $methods_weight = variable_get("language_negotiation_methods_weight_$type", array());
    +    $enabled_methods = $this->config('language.types')->get('negotiation.' . $type . '.enabled') ?: array();
    +    $methods_weight = $this->config('language.types')->get('negotiation.' . $type . '.method_weights') ?: array();
     
    

    I can't see where the weights are still used other than the form?

    If you look at the actual code:

          // Execute the language negotiation methods in the order they were set up
          // and return the first valid language found.
          foreach ($this->getConfiguration($type) as $method_id => $info) {
    
alexpott’s picture

1. Yep we can define the defaults in CMI because you can't define language types until language.types.yml exists which is part of the language module
2. The weights are used in the UI - which i'd rather not touch in this issue.

Berdir’s picture

Status: Needs review » Reviewed & tested by the community

Ok, yes, we discussed this, the weights are used for disabled methods in the UI.

Let's get this in then, whatever it takes to kill variable_get() :)

catch’s picture

16: 2102477.16.patch queued for re-testing.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 16: 2102477.16.patch, failed testing.

alexpott’s picture

Status: Needs work » Needs review
FileSize
15.03 KB

Re-rolled

sun’s picture

Status: Needs review » Reviewed & tested by the community

RTBC if bot comes back green.

Gábor Hojtsy’s picture

Woot, exciting :)

catch’s picture

Title: Convert remainder of language negotiation settings to configuration system » Change notice: Convert remainder of language negotiation settings to configuration system
Priority: Critical » Major
Status: Reviewed & tested by the community » Active
Issue tags: +Needs change record

Committed/pushed to 8.x, thanks!

ianthomas_uk’s picture

Title: Change notice: Convert remainder of language negotiation settings to configuration system » Convert remainder of language negotiation settings to configuration system
Priority: Major » Critical

Sorry, but there are still three calls to variable_set in LocaleUninstallTest :(

http://drupalcode.org/project/drupal.git?a=search&h=refs%2Fheads%2F8.x&s...

core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
103 variable_set('language_negotiation_' . Language::TYPE_INTERFACE, $config);
104 variable_set('language_negotiation_' . Language::TYPE_CONTENT, $config);
105 variable_set('language_negotiation_' . Language::TYPE_URL, $config);

ianthomas_uk’s picture

Title: Convert remainder of language negotiation settings to configuration system » Change notice: Convert remainder of language negotiation settings to configuration system
Priority: Critical » Major

We'll fix the remaining variable_sets in #2182603: Remove LocaleUninstallTest, so back to change notice for this.

Gábor Hojtsy’s picture

@catch: not sure what does this need a change notice for, the variables are not the API for language negotiation (neither in the negotiation plugin side nor for other modules), but I'll prepare a short one about this moving to config to cover all bases.

Gábor Hojtsy’s picture

Title: Change notice: Convert remainder of language negotiation settings to configuration system » Convert remainder of language negotiation settings to configuration system
Priority: Major » Critical
Status: Active » Fixed
Issue tags: -sprint, -Needs change record

Status: Fixed » Closed (fixed)

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