diff --git a/core/includes/language.inc b/core/includes/language.inc index 74d5752..1f7d392 100644 --- a/core/includes/language.inc +++ b/core/includes/language.inc @@ -178,19 +178,10 @@ function language_types_info() { function language_types_get_configurable($stored = TRUE) { $configurable = &drupal_static(__FUNCTION__); - if ($stored && !isset($configurable)) { - $types = variable_get('language_types', language_types_get_default()); - $configurable = array_keys(array_filter($types)); - } - - if (!$stored) { - $result = array(); - foreach (language_types_info() as $type => $info) { - if (!isset($info['fixed'])) { - $result[] = $type; - } - } - return $result; + if (!$stored || !isset($configurable)) { + $configurable = config('system.language.types')->get('configurable'); + $configurable = is_null($configurable) ? array() : array_keys(array_filter($configurable)); + return $configurable; } return $configurable; @@ -214,37 +205,63 @@ function language_types_disable($types) { /** * Updates the language type configuration. + * + * @param array $configurable + * The configuration object containing the user defined preferences for + * language type customizability. If it's not provided it will be loaded from + * configuration. */ -function language_types_set() { +function language_types_set($configurable = NULL) { + language_negotiation_include(); // Ensure that we are getting the defined language negotiation information. An // invocation of module_enable() or module_disable() could outdate the cached // information. drupal_static_reset('language_types_info'); drupal_static_reset('language_negotiation_info'); + // In case the $configurable array is not passed in load it from + // configuration. + if (is_null($configurable)) { + $configurable = config('system.language.types')->get('configurable'); + } + // Determine which language types are configurable and which not by checking // whether the 'fixed' key is defined. Non-configurable (fixed) language types // have their language negotiation settings stored there. $language_types = array(); $negotiation_info = language_negotiation_info(); foreach (language_types_info() as $type => $info) { - if (isset($info['fixed'])) { - $language_types[$type] = FALSE; - $method_weights = array(); - foreach ($info['fixed'] as $weight => $method_id) { - if (isset($negotiation_info[$method_id])) { - $method_weights[$method_id] = $weight; + if ($locked = !empty($info['locked'])) { + // For locked languages the configurability is set based on the presence + // of the default settings. + $configurable[$type] = empty($info['fixed']); + $language_types[$type] = !$locked; + if (!$configurable[$type]) { + $method_weights = array(); + foreach ($info['fixed'] as $weight => $method_id) { + if (isset($negotiation_info[$method_id])) { + $method_weights[$method_id] = $weight; + } } + language_negotiation_set($type, $method_weights); } - language_negotiation_set($type, $method_weights); } else { - $language_types[$type] = TRUE; + // For unlocked languages read the configurable array. + $language_types[$type] = !empty($configurable[$type]); + if (!$language_types[$type] && empty($info['fixed'])) { + // If the language is non configurable and there is no default + // language negotiation setting then provide one. + $method_weights = array(LANGUAGE_NEGOTIATION_INTERFACE); + $method_weights = array_flip($method_weights); + language_negotiation_set($type, $method_weights); + } } } // Save enabled language types. variable_set('language_types', $language_types); + config('system.language.types')->set('configurable', $configurable)->save(); // Ensure that subsequent calls of language_types_get_configurable() return // the updated language type information. diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc index f59fb38..8d5f551 100644 --- a/core/modules/language/language.admin.inc +++ b/core/modules/language/language.admin.inc @@ -357,11 +357,17 @@ function language_negotiation_configure_form() { $form = array( '#submit' => array('language_negotiation_configure_form_submit'), '#theme' => 'language_negotiation_configure_form', - '#language_types' => language_types_get_configurable(FALSE), '#language_types_info' => language_types_info(), '#language_negotiation_info' => language_negotiation_info(), ); - + $form['#language_types'] = array(); + foreach ($form['#language_types_info'] as $type => $info) { + // Show locked language types only if they do not already define a fixed + // configuration. In the latter case they always are configurable. + if (empty($info['locked']) || !isset($info['fixed'])) { + $form['#language_types'][] = $type; + } + } foreach ($form['#language_types'] as $type) { language_negotiation_configure_form_table($form, $type); } @@ -389,6 +395,19 @@ function language_negotiation_configure_form_table(&$form, $type) { '#show_operations' => FALSE, 'weight' => array('#tree' => TRUE), ); + // Only show configurability checkbox for the unlocked language types. + if (empty($info['locked'])) { + $configurable = config('system.language.types')->get('configurable'); + $table_form['configurable'] = array( + '#type' => 'checkbox', + '#title' => t('Customize %language_name language detection to differ from User interface text language detection settings.', array('%language_name' => $info['name'])), + '#default_value' => !empty($configurable[$type]), + '#attributes' => array('class' => array('language-customization-checkbox')), + '#attached' => array( + 'js' => array(drupal_get_path('module', 'language') . '/language.admin.js' => array('type' => 'file')), + ), + ); + } $negotiation_info = $form['#language_negotiation_info']; $enabled_methods = variable_get("language_negotiation_$type", array()); @@ -519,12 +538,13 @@ function theme_language_negotiation_configure_form($variables) { 'rows' => $rows, 'attributes' => array('id' => "language-negotiation-methods-$type"), ); - $table = theme('table', $variables); + $table = drupal_render($form[$type]['configurable']); + $table .= theme('table', $variables); $table .= drupal_render_children($form[$type]); drupal_add_tabledrag("language-negotiation-methods-$type", 'order', 'sibling', "language-method-weight-$type"); - $output .= '
' . $title . $description . $table . '
'; + $output .= '
' . $title . $description . $table . '
'; } $output .= drupal_render_children($form); @@ -537,11 +557,16 @@ function theme_language_negotiation_configure_form($variables) { function language_negotiation_configure_form_submit($form, &$form_state) { $configurable_types = $form['#language_types']; + $configurable = config('system.language.types')->get('configurable'); + foreach ($configurable_types as $type) { $method_weights = array(); $enabled_methods = $form_state['values'][$type]['enabled']; $enabled_methods[LANGUAGE_NEGOTIATION_SELECTED] = TRUE; $method_weights_input = $form_state['values'][$type]['weight']; + if (isset($form_state['values'][$type]['configurable'])) { + $configurable[$type] = !empty($form_state['values'][$type]['configurable']); + } foreach ($method_weights_input as $method_id => $weight) { if ($enabled_methods[$method_id]) { @@ -555,7 +580,15 @@ function language_negotiation_configure_form_submit($form, &$form_state) { // Update non-configurable language types and the related language negotiation // configuration. - language_types_set(); + language_types_set($configurable); + + // Clear the block's cache to change the block name when there is only one + // Invalidate the block cache to update custom block-based derivatives. + if (module_exists('block')) { + drupal_container()->get('plugin.manager.block')->clearCachedDefinitions(); + } + // $block_manager = new BlockManager(array('Drupal\language\Plugin\Derivative\LanguageBlock')); + // $block_manager->clearCachedDefinitions(); $form_state['redirect'] = 'admin/config/regional/language/detection'; drupal_set_message(t('Language negotiation configuration saved.')); diff --git a/core/modules/language/language.admin.js b/core/modules/language/language.admin.js new file mode 100644 index 0000000..63b3acd --- /dev/null +++ b/core/modules/language/language.admin.js @@ -0,0 +1,35 @@ +(function ($) { + +"use strict"; + +/** + * Makes language negotiation inherit user interface negotiation. + */ +Drupal.behaviors.negotiationLanguage = { + attach: function (context) { + var $context = $(context); + // Given a customization checkbox derive the language type being changed. + var toggleTable = function ($checkbox) { + // Get the language detection type such as User interface text + // language detection or Content language detection. + var detectiontype = $checkbox.attr('name').replace('[configurable]', ''); + var $table = $('.table-' + detectiontype + '-wrapper'); + if ($checkbox.is(':checked')) { + $table.find('table, .tabledrag-toggle-weight-wrapper').show(); + } + else { + $table.find('table, .tabledrag-toggle-weight-wrapper').hide(); + } + }; + // Bind hide/show + rearrange to customization checkboxes. + $('body').once('negotiation-language-admin-bind').on('click', '#language-negotiation-configure-form :input.language-customization-checkbox', function (e) { + toggleTable($(e.target)); + }); + // Initially, hide language detection types that are not customized. + $('#language-negotiation-configure-form :input.language-customization-checkbox:not(:checked)').each(function (index, element) { + toggleTable($(element)); + }); + } +}; + +})(jQuery); diff --git a/core/modules/language/language.module b/core/modules/language/language.module index 6bbb5a7..d28a60c 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -607,14 +607,17 @@ function language_language_types_info() { LANGUAGE_TYPE_INTERFACE => array( 'name' => t('User interface text'), 'description' => t('Order of language detection methods for user interface text. If a translation of user interface text is available in the detected language, it will be displayed.'), + 'locked' => TRUE, ), LANGUAGE_TYPE_CONTENT => array( 'name' => t('Content'), 'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'), 'fixed' => array(LANGUAGE_NEGOTIATION_INTERFACE), + 'locked' => TRUE, ), LANGUAGE_TYPE_URL => array( 'fixed' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_URL_FALLBACK), + 'locked' => TRUE, ), ); } diff --git a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php index d451b27..d27ca3e 100644 --- a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php +++ b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php @@ -38,11 +38,17 @@ public function getDerivativeDefinition($derivative_id, array $base_plugin_defin public function getDerivativeDefinitions(array $base_plugin_definition) { include_once DRUPAL_ROOT . '/core/includes/language.inc'; $info = language_types_info(); - foreach (language_types_get_configurable(FALSE) as $type) { + $configurable_types = language_types_get_configurable(FALSE); + foreach ($configurable_types as $type) { $this->derivatives[$type] = $base_plugin_definition; $this->derivatives[$type]['admin_label'] = t('Language switcher (!type)', array('!type' => $info[$type]['name'])); $this->derivatives[$type]['cache'] = DRUPAL_NO_CACHE; } + // If there is just one configurable type then change the title of the + // block. + if (count($configurable_types) == 1) { + $this->derivatives[reset($configurable_types)]['admin_label'] = t('Language switcher'); + } return $this->derivatives; } diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php index 8aea763..e96a3d1 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php @@ -68,6 +68,7 @@ function testInfoAlterations() { $form_field => TRUE, $type . '[enabled][' . $test_method_id . ']' => TRUE, $test_type . '[enabled][' . $test_method_id . ']' => TRUE, + $test_type . '[configurable]' => TRUE, ); $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings')); @@ -157,8 +158,9 @@ protected function languageNegotiationUpdate($op = 'enable') { */ protected function checkFixedLanguageTypes() { drupal_static_reset('language_types_info'); + $configurable = config('system.language.types')->get('configurable'); foreach (language_types_info() as $type => $info) { - if (isset($info['fixed'])) { + if (empty($configurable[$type]) && isset($info['fixed'])) { $negotiation = variable_get("language_negotiation_$type", array()); $equal = count($info['fixed']) == count($negotiation); while ($equal && list($id) = each($negotiation)) { diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php index e614da2..294b4de 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php @@ -190,7 +190,7 @@ function testUILanguageNegotiation() { ), // Language prefix. array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER), 'path' => "$langcode/admin/config", 'expect' => $language_string, 'expected_method_id' => LANGUAGE_NEGOTIATION_URL, @@ -372,6 +372,15 @@ protected function runTest($test) { } $this->container->get('language_manager')->reset(); $this->drupalGet($test['path'], array(), $test['http_header']); + // array( + // 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED), + // 'path' => "$langcode/admin/config", + // 'expect' => $language_string, + // 'expected_method_id' => LANGUAGE_NEGOTIATION_URL, + // 'http_header' => $http_header_browser_fallback, + // 'message' => 'URL (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix', + // ), + $this->assertText($test['expect'], $test['message']); $this->assertText(t('Language negotiation method: @name', array('@name' => $test['expected_method_id']))); } diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module index cb97937..0b98ed5 100644 --- a/core/modules/language/tests/language_test/language_test.module +++ b/core/modules/language/tests/language_test/language_test.module @@ -30,6 +30,7 @@ function language_test_language_types_info() { ), 'fixed_test_language_type' => array( 'fixed' => array('test_language_negotiation_method'), + 'locked' => TRUE, ), ); } @@ -40,7 +41,7 @@ function language_test_language_types_info() { */ function language_test_language_types_info_alter(array &$language_types) { if (state()->get('language_test.content_language_type')) { - unset($language_types[LANGUAGE_TYPE_CONTENT]['fixed']); + $language_types[LANGUAGE_TYPE_CONTENT]['locked'] = FALSE; } } diff --git a/core/modules/system/config/system.language.types.yml b/core/modules/system/config/system.language.types.yml new file mode 100644 index 0000000..114fef6 --- /dev/null +++ b/core/modules/system/config/system.language.types.yml @@ -0,0 +1,5 @@ +# system.language.types.yml +configurable: + language_interface: '1' + language_content: '0' + language_url: '0' diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module index 68eb96d..2d2f275 100644 --- a/core/modules/translation_entity/translation_entity.module +++ b/core/modules/translation_entity/translation_entity.module @@ -63,9 +63,9 @@ function translation_entity_module_implements_alter(&$implementations, $hook) { * Implements hook_language_type_info_alter(). */ function translation_entity_language_types_info_alter(array &$language_types) { - // Make content language negotiation configurable by removing its predefined - // configuration. - unset($language_types[LANGUAGE_TYPE_CONTENT]['fixed']); + // Make content language negotiation configurable by removing unsetting the + // 'locked' flag. + $language_types[LANGUAGE_TYPE_CONTENT]['locked'] = FALSE; } /**