Skip to content
Snippets Groups Projects
Commit b06dd311 authored by Dries Buytaert's avatar Dries Buytaert
Browse files

Issue #1776166 by c31ck, Artusamak, webflo, YesCT: improve default language negotiation.

parent c98eebe4
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
......@@ -245,6 +245,11 @@
*/
const LANGUAGE_ALL = 3;
/**
* The language state used when referring to the site's default language.
*/
const LANGUAGE_SITE_DEFAULT = 4;
/**
* The type of language used to define the content language.
*/
......@@ -2799,6 +2804,15 @@ function language_list($flags = LANGUAGE_CONFIGURABLE) {
// default we remove the locked languages, but the caller may request for
// those languages to be added as well.
$filtered_languages = array();
// Add the site's default language if flagged as allowed value.
if ($flags & LANGUAGE_SITE_DEFAULT) {
$default = isset($default) ? $default : language_default();
// Rename the default language.
$default->name = t("Site's default language (@lang_name)", array('@lang_name' => $default->name));
$filtered_languages['site_default'] = $default;
}
foreach ($languages as $langcode => $language) {
if (($language->locked && !($flags & LANGUAGE_LOCKED)) || (!$language->locked && !($flags & LANGUAGE_CONFIGURABLE))) {
continue;
......
......@@ -10,7 +10,7 @@
/**
* No language negotiation. The default language is used.
*/
const LANGUAGE_NEGOTIATION_DEFAULT = 'language-default';
const LANGUAGE_NEGOTIATION_SELECTED = 'language-selected';
/**
* @defgroup language_negotiation Language Negotiation API functionality
......@@ -133,7 +133,7 @@ function language_types_initialize($type, $request = NULL) {
// If no other language was found use the default one.
$language = language_default();
$language->method_id = LANGUAGE_NEGOTIATION_DEFAULT;
$language->method_id = LANGUAGE_NEGOTIATION_SELECTED;
return $language;
}
......@@ -263,7 +263,7 @@ function language_types_set() {
*/
function language_negotiation_method_get_first($type) {
$negotiation = variable_get("language_negotiation_$type", array());
return empty($negotiation) ? LANGUAGE_NEGOTIATION_DEFAULT : key($negotiation);
return empty($negotiation) ? LANGUAGE_NEGOTIATION_SELECTED : key($negotiation);
}
/**
......@@ -404,14 +404,19 @@ function language_negotiation_info() {
if (!isset($negotiation_info)) {
// Collect all the module-defined language negotiation methods.
$negotiation_info = module_invoke_all('language_negotiation_info');
$languages = language_list();
$selected_language = $languages[language_from_selected($languages)];
$description = 'Language based on a selected language. ';
$description .= ($selected_language->langcode == language_default()->langcode) ? "(Site's default language (@language_name))" : '(@language_name)';
// Add the default language negotiation method.
$negotiation_info[LANGUAGE_NEGOTIATION_DEFAULT] = array(
'callbacks' => array('language' => 'language_from_default'),
'weight' => 10,
'name' => t('Default language'),
'description' => t('Use the default site language (@language_name).', array('@language_name' => language_default()->name)),
'config' => 'admin/config/regional/language',
$negotiation_info[LANGUAGE_NEGOTIATION_SELECTED] = array(
'callbacks' => array(
'negotiation' => 'language_from_selected',
),
'weight' => 12,
'name' => t('Selected language'),
'description' => t($description, array('@language_name' => $selected_language->name)),
'config' => 'admin/config/regional/language/detection/selected',
);
// Let other modules alter the list of language negotiation methods.
......@@ -468,14 +473,22 @@ function language_negotiation_method_invoke($method_id, $method = NULL, $request
return !empty($results[$method_id]) ? clone($results[$method_id]) : $results[$method_id];
}
/**
* Returns the default language code.
*
* @return
* The default language code.
*/
function language_from_default() {
return language_default()->langcode;
/**
* Identifies language from configuration.
*
* @param $languages
* An array of valid language objects.
*
* @return
* A valid language code on success, FALSE otherwise.
*/
function language_from_selected($languages) {
$langcode = (string) config('language.negotiation')->get('selected_langcode');
// Replace the site's default langcode by its real value.
if ($langcode == 'site_default') {
$langcode = language_default()->langcode;
}
return isset($languages[$langcode]) ? $langcode : language_default()->langcode;
}
/**
......
selected_langcode: site_default
......@@ -519,6 +519,7 @@ function language_negotiation_configure_form_table(&$form, $type) {
'#title_display' => 'invisible',
'#default_value' => $weight,
'#attributes' => array('class' => array("language-method-weight-$type")),
'#delta' => 20,
);
$table_form['title'][$method_id] = array('#markup' => $method_name);
......@@ -529,7 +530,7 @@ function language_negotiation_configure_form_table(&$form, $type) {
'#title_display' => 'invisible',
'#default_value' => $enabled,
);
if ($method_id === LANGUAGE_NEGOTIATION_DEFAULT) {
if ($method_id === LANGUAGE_NEGOTIATION_SELECTED) {
$table_form['enabled'][$method_id]['#default_value'] = TRUE;
$table_form['enabled'][$method_id]['#attributes'] = array('disabled' => 'disabled');
}
......@@ -624,7 +625,7 @@ function language_negotiation_configure_form_submit($form, &$form_state) {
foreach ($configurable_types as $type) {
$method_weights = array();
$enabled_methods = $form_state['values'][$type]['enabled'];
$enabled_methods[LANGUAGE_NEGOTIATION_DEFAULT] = TRUE;
$enabled_methods[LANGUAGE_NEGOTIATION_SELECTED] = TRUE;
$method_weights_input = $form_state['values'][$type]['weight'];
foreach ($method_weights_input as $method_id => $weight) {
......@@ -814,6 +815,27 @@ function language_negotiation_configure_session_form($form, &$form_state) {
return system_settings_form($form);
}
/**
* Builds the selected language negotiation method configuration form.
*/
function language_negotiation_configure_selected_form($form, &$form_state) {
$form['selected_langcode'] = array(
'#type' => 'language_select',
'#title' => t('Language'),
'#languages' => LANGUAGE_CONFIGURABLE | LANGUAGE_SITE_DEFAULT,
'#default_value' => config('language.negotiation')->get('selected_langcode'),
);
return system_config_form($form, $form_state);
}
/**
* Form submission handler for language_negotiation_configure_selected_form().
*/
function language_negotiation_configure_selected_form_submit($form, &$form_state) {
config('language.negotiation')->set('selected_langcode', $form_state['values']['selected_langcode'])->save();
}
/**
* Builds the browser language negotiation method configuration form.
*/
......
......@@ -41,6 +41,12 @@ function language_help($path, $arg) {
$output = '<p>' . t('Browsers use different language codes to refer to the same languages. You can add and edit mappings from browser language codes to the <a href="@configure-languages">languages used by Drupal</a>.', array('@configure-languages' => url('admin/config/regional/language'))) . '</p>';
return $output;
case 'admin/config/regional/language/detection/selected':
$output = '<p>' . t('Changing the selected language here (and leaving this option as the last among the detection and selection options) is the easiest way to change the fallback language for the website, if you need to change how your site works by default (eg. when using an empty path prefix or using the default domain). <a href="@admin-change-language">Changing the site\'s default language</a> itself might have other undesired side effects.
', array('@admin-change-language' => url('admin/config/regional/language'))) . '</p>';
return $output;
case 'admin/structure/block/manage/%/%':
if ($arg[4] == 'language' && $arg[5] == 'language_interface') {
return '<p>' . t('With multiple languages enabled, registered users can select their preferred language and authors can assign a specific language to content.') . '</p>';
......@@ -135,6 +141,14 @@ function language_menu() {
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
);
$items['admin/config/regional/language/detection/selected'] = array(
'title' => 'Selected language detection configuration',
'page callback' => 'drupal_get_form',
'page arguments' => array('language_negotiation_configure_selected_form'),
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
'type' => MENU_VISIBLE_IN_BREADCRUMB,
);
return $items;
}
......
......@@ -140,19 +140,56 @@ function testUILanguageNegotiation() {
// Configure URL language rewrite.
variable_set('language_negotiation_url_type', LANGUAGE_TYPE_INTERFACE);
// Configure selected language negotiation to use zh-hans.
$edit = array('selected_langcode' => $langcode);
$this->drupalPost('admin/config/regional/language/detection/selected', $edit, t('Save configuration'));
$test = array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $language_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => $http_header_browser_fallback,
'message' => 'SELECTED: UI language is switched based on selected language.',
);
$this->runTest($test);
// An invalid language is selected.
config('language.negotiation')->set('selected_langcode', NULL)->save();
$test = array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => $http_header_browser_fallback,
'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.',
);
$this->runTest($test);
// No selected language is available.
config('language.negotiation')->set('selected_langcode', $langcode_unknown)->save();
$test = array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => $http_header_browser_fallback,
'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.',
);
$this->runTest($test);
$tests = array(
// Default, browser preference should have no influence.
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_DEFAULT,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (PATH) > DEFAULT: no language prefix, UI language is default and the browser language preference setting is not used.',
),
// Language prefix.
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED),
'path' => "$langcode/admin/config",
'expect' => $language_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_URL,
......@@ -179,10 +216,10 @@ function testUILanguageNegotiation() {
),
// Default, browser language preference is not one of site's lang.
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER, LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_DEFAULT,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => $http_header_blah,
'message' => 'URL (PATH) > BROWSER > DEFAULT: no language prefix and browser language preference set to unknown language should use default language',
),
......@@ -204,10 +241,10 @@ function testUILanguageNegotiation() {
$tests = array(
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_DEFAULT,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => array(),
'message' => 'USER > DEFAULT: no preferred user language setting, the UI language is default',
),
......@@ -220,10 +257,10 @@ function testUILanguageNegotiation() {
$tests = array(
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_DEFAULT,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => array(),
'message' => 'USER > DEFAULT: invalid preferred user language setting, the UI language is default',
),
......@@ -236,7 +273,7 @@ function testUILanguageNegotiation() {
$tests = array(
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $language_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_USER,
......@@ -255,10 +292,10 @@ function testUILanguageNegotiation() {
$tests = array(
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_DEFAULT,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => array(),
'message' => 'USER ADMIN > DEFAULT: no preferred user admin language setting, the UI language is default',
),
......@@ -270,10 +307,10 @@ function testUILanguageNegotiation() {
$tests = array(
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_DEFAULT,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => array(),
'message' => 'USER ADMIN > DEFAULT: invalid preferred user admin language setting, the UI language is default',
),
......@@ -285,7 +322,7 @@ function testUILanguageNegotiation() {
$tests = array(
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED),
'path' => 'admin/config',
'expect' => $language_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_USER_ADMIN,
......@@ -307,18 +344,18 @@ function testUILanguageNegotiation() {
$tests = array(
// Default domain, browser preference should have no influence.
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED),
'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN,
'path' => 'admin/config',
'expect' => $default_string,
'expected_method_id' => LANGUAGE_NEGOTIATION_DEFAULT,
'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (DOMAIN) > DEFAULT: default domain should get default language',
),
// Language domain specific URL, we set the $_SERVER['HTTP_HOST'] in
// language_test.module hook_boot() to simulate this.
array(
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT),
'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED),
'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN,
'language_test_domain' => $language_domain . ':88',
'path' => 'admin/config',
......
......@@ -118,11 +118,11 @@ function testUninstallProcess() {
// Check language negotiation.
require_once DRUPAL_ROOT . '/core/includes/language.inc';
$this->assertTrue(count(language_types_get_all()) == count(language_types_get_default()), t('Language types reset'));
$language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_DEFAULT;
$language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_SELECTED;
$this->assertTrue($language_negotiation, t('Interface language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
$language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_CONTENT) == LANGUAGE_NEGOTIATION_DEFAULT;
$language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_CONTENT) == LANGUAGE_NEGOTIATION_SELECTED;
$this->assertTrue($language_negotiation, t('Content language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
$language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_DEFAULT;
$language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_SELECTED;
$this->assertTrue($language_negotiation, t('URL language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
// Check language negotiation method settings.
......
......@@ -731,6 +731,28 @@ function locale_update_8011() {
));
}
/**
* Renames language_default language negotiation method to language_selected.
*/
function locale_update_8013() {
$weight = update_variable_get('language_negotiation_methods_weight_language_interface', NULL);
if ($weight !== NULL) {
$weight[LANGUAGE_NEGOTIATION_SELECTED] = $weight['language-default'];
unset($weight['language-default']);
update_variable_set('language_negotiation_methods_weight_language_interface', $weight);
}
$negotiation_interface = update_variable_get('language_negotiation_language_interface', NULL);
if ($negotiation_interface !== NULL) {
if (isset($negotiation_interface['language-default'])) {
$negotiation_interface[LANGUAGE_NEGOTIATION_SELECTED] = $negotiation_interface['language-default'];
$negotiation_interface[LANGUAGE_NEGOTIATION_SELECTED]['callbacks']['negotiation'] = 'language_from_selected';
unset($negotiation_interface['language-default']);
update_variable_set('language_negotiation_language_interface', $negotiation_interface);
}
}
}
/**
* @} End of "addtogroup updates-7.x-to-8.x".
* The next series of updates should start at 9000.
......
......@@ -106,12 +106,18 @@ public function testLanguageUpgrade() {
'language-session' => '-6',
'language-user' => '-4',
'language-browser' => '-2',
'language-default' => '10',
'language-selected' => '10',
);
// Check that locale_language_providers_weight_language is correctly
// renamed.
$current_weights = update_variable_get('language_negotiation_methods_weight_language_interface', array());
$this->assertTrue(serialize($expected_weights) == serialize($current_weights), t('Language negotiation method weights upgraded.'));
$this->assertTrue(isset($current_weights['language-selected']), 'Language-selected is present.');
$this->assertFalse(isset($current_weights['language-default']), 'Language-default is not present.');
// Check that negotiation callback was added to language_negotiation_language_interface.
$language_negotiation_language_interface = update_variable_get('language_negotiation_language_interface', NULL);
$this->assertTrue(isset($language_negotiation_language_interface[LANGUAGE_NEGOTIATION_SELECTED]['callbacks']['negotiation']), 'Negotiation callback was added to language_negotiation_language_interface.');
// Look up migrated plural string.
$source_string = db_query('SELECT * FROM {locales_source} WHERE lid = 22')->fetchObject();
......
......@@ -210,7 +210,7 @@ public function form(array $form, array &$form_state, EntityInterface $account)
// Is default the interface language?
include_once DRUPAL_ROOT . '/core/includes/language.inc';
$interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT;
$interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_SELECTED;
$form['language'] = array(
'#type' => language_multilingual() ? 'fieldset' : 'container',
'#title' => t('Language settings'),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment