diff -u b/core/modules/language/config/language.mappings.yml b/core/modules/language/config/language.mappings.yml --- b/core/modules/language/config/language.mappings.yml +++ b/core/modules/language/config/language.mappings.yml @@ -1,7 +1,9 @@ -zh-tw: zh-hant-tw -zh-hk: zh-hant-hk -zh-mo: zh-hant-mo -zh-cht: zh-hant -zh-cn: zh-hans-cn -zh-sg: zh-hans-sg -zh-chs: zh-hans +# Most browser do not send abbreviated languages codes not standard +# supported by Drupal, these defaults handles the most common cases. +zh-tw: zh-hant-tw # Taiwan Chinese in traditional script +zh-hk: zh-hant-hk # Hong Kong Chinese in traditional script +zh-mo: zh-hant-mo # Macao Chinese in traditional script +zh-cht: zh-hant # traditional Chinese +zh-cn: zh-hans-cn # PRC Mainland Chinese in simplified script +zh-sg: zh-hans-sg # Singapore Chinese in simplified script +zh-chs: zh-hans # simplified Chinese diff -u b/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc --- b/core/modules/language/language.admin.inc +++ b/core/modules/language/language.admin.inc @@ -816,7 +816,8 @@ $form['info'] = array( '#type' => 'markup', - '#markup' => t('You can add custom mappings to map browser languages to Drupal languages.'), + '#markup' => t('You can add custom mappings to map browser languages to Drupal languages.') . '
' + . t('You can use this to support browser language codes not standard supported by Drupal.'), ); $form['mappings'] = array( @@ -825,18 +826,20 @@ '#theme' => 'language_negotiation_configure_browser_form_table', ); - $mappings = language_get_mappings(); + $mappings = language_get_browser_drupal_langcode_mappings(); foreach ($mappings as $browser_langcode => $drupal_langcode) { $form['mappings'][$browser_langcode] = array( 'browser_langcode' => array( '#type' => 'textfield', '#default_value' => $browser_langcode, '#size' => 20, + '#required' => TRUE, ), 'drupal_langcode' => array( '#type' => 'textfield', '#default_value' => $drupal_langcode, '#size' => 20, + '#required' => TRUE, ), ); } @@ -919,7 +922,7 @@ foreach ($mappings as $key => $data) { if ($key == '_new') { if (!empty($data['browser_langcode']) && !empty($data['drupal_langcode'])) { - // Make sure browser_langcode is unique + // Make sure browser_langcode is unique. if (array_key_exists($data['browser_langcode'], $unique_values)) { form_set_error('mappings][' . $key . '][browser_langcode', 'Browser language codes must be unique.'); } @@ -937,7 +940,7 @@ } else { if (!empty($data['browser_langcode']) && !empty($data['drupal_langcode'])) { - // Make sure browser_langcode is unique + // Make sure browser_langcode is unique. if (array_key_exists($data['browser_langcode'], $unique_values)) { form_set_error('mappings][' . $key . '][browser_langcode', 'Browser language codes must be unique.'); } @@ -968,7 +971,7 @@ function language_negotiation_configure_browser_form_submit($form, &$form_state) { $mappings = $form_state['mappings']; if (!empty($mappings)) { - language_set_mappings($mappings); + language_set_browser_drupal_langcode_mappings($mappings); } } @@ -989,10 +992,10 @@ */ function language_negotiation_configure_browser_delete_form_submit($form, &$form_state) { $browser_langcode = $form_state['browser_langcode']; - $mappings = language_get_mappings(); + $mappings = language_get_browser_drupal_langcode_mappings(); if (array_key_exists($browser_langcode, $mappings)) { unset($mappings[$browser_langcode]); - language_set_mappings($mappings); + language_set_browser_drupal_langcode_mappings($mappings); } $form_state['redirect'] = 'admin/config/regional/language/detection/browser'; } diff -u b/core/modules/language/language.module b/core/modules/language/language.module --- b/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -618,9 +618,13 @@ } /** - * Returns language mappings. + * Returns language mappings between browser and Drupal language codes. + * + * @return array + * An array containing browser language codes as keys with corresponding + * Drupal language codes as values. */ -function language_get_mappings() { +function language_get_browser_drupal_langcode_mappings() { $config = config('language.mappings'); if ($config->isNew()) { config_install_default_config('module', 'language'); @@ -630,9 +634,13 @@ } /** - * Stores language mappings. + * Stores language mappings between browser and Drupal language codes. + * + * @param array $mappings + * An array containing browser language codes as keys with corresponding + * Drupal language codes as values. */ -function language_set_mappings($mappings) { +function language_set_browser_drupal_langcode_mappings($mappings) { $config = config('language.mappings'); $config->setData($mappings); $config->save(); diff -u b/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc --- b/core/modules/language/language.negotiation.inc +++ b/core/modules/language/language.negotiation.inc @@ -58,6 +58,15 @@ /** * Identify language from the Accept-language HTTP header we got. * + * The algorithm works as follows: + * - map browser language codes to Drupal language codes. + * - order all browser language codes by qvalue from high to low. + * - add generic browser language codes if they aren't already specified + * but with a slightly lower qvalue. + * - find the most specific Drupal language code with the highest qvalue. + * - if 2 or more languages are having the same qvalue, respect the order of + * them inside the $languages array. + * * We perform browser accept-language parsing only if page cache is disabled, * otherwise we would cache a user-specific preference. * @@ -83,7 +92,7 @@ if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER)) { // Load custom mappings to support browsers that are sending non standard // language codes. - $mappings = language_get_mappings(); + $mappings = language_get_browser_drupal_langcode_mappings(); foreach ($matches as $match) { if ($mappings) { $langcode = strtolower($match[1]); @@ -111,7 +120,7 @@ // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx asort($browser_langcodes); foreach ($browser_langcodes as $langcode => $qvalue) { - // For chinese languages the generic tag is either zh-hans or zh-hant, so we + // For Chinese languages the generic tag is either zh-hans or zh-hant, so we // need to handle this separately. $generic_tag = ''; if (strlen($langcode) > 7 && (substr($langcode, 0, 7) == 'zh-hant' || substr($langcode, 0, 7) == 'zh-hans')) { diff -u b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php --- b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php @@ -27,8 +27,15 @@ /** * Unit tests for the language_from_browser() function. + * + * @see language_from_browser(). */ function testLanguageFromBrowser() { + // The order of the languages is only important if the browser language + // codes are having the same qvalue, otherwise the one with the highest + // qvalue is prefered. The automatically generated generic tags are always + // having a lower qvalue. + $languages = array( // In our test case, 'en' has priority over 'en-US'. 'en' => new Language(array( @@ -153,6 +160,10 @@ } } + /** + * Tests for adding, editing and deleting mappings between browser language + * codes and Drupal language codes. + */ function testUIBrowserLanguageMappings() { // User to manage languages. $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); @@ -170,10 +181,10 @@ // Delete zh-cn language code. $browser_langcode = 'zh-cn'; $this->drupalGet('admin/config/regional/language/detection/browser/delete/' . $browser_langcode); - $message = t('Are you sure you want to delete !browser_langcode?', array( - '!browser_langcode' => $browser_langcode, + $message = t('Are you sure you want to delete @browser_langcode?', array( + '@browser_langcode' => $browser_langcode, )); - $this->assertText($message, 'Question found.'); + $this->assertRaw($message, 'Question found.'); // Confirm the delete. $edit = array();