diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
index 48a8083..8ae625d 100644
--- a/core/modules/language/language.negotiation.inc
+++ b/core/modules/language/language.negotiation.inc
@@ -82,6 +82,15 @@ function language_from_browser($languages) {
   $browser_langcodes = array();
   if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER)) {
     foreach ($matches as $match) {
+      // Handle Chinese so it maps right, some browsers are sending
+      // wrong langcodes.
+      if (strtolower($match[1]) === 'zh-tw') {
+        $match[1] = 'zh-hant';
+      }
+      elseif (strtolower($match[1]) === 'zh-cn') {
+        $match[1] = 'zh-hans';
+      }
+
       // We can safely use strtolower() here, tags are ASCII.
       // RFC2616 mandates that the decimal part is no more than three digits,
       // so we multiply the qvalue by 1000 to avoid floating point comparisons.
@@ -102,7 +111,7 @@ function language_from_browser($languages) {
   foreach ($browser_langcodes as $langcode => $qvalue) {
     $generic_tag = strtok($langcode, '-');
     if (!isset($browser_langcodes[$generic_tag])) {
-      $browser_langcodes[$generic_tag] = $qvalue;
+      $browser_langcodes[$generic_tag] = $qvalue - 1;
     }
   }
 
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
index bb7b481..a212e0f 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
@@ -57,6 +57,13 @@ class LanguageBrowserDetectionUnitTest extends UnitTestBase {
       'eh-oh-laa-laa' => (object) array(
         'langcode' => 'eh-oh-laa-laa',
       ),
+      // Chinese languages.
+      'zh-hans' => (object) array(
+        'langcode' => 'zh-hans',
+      ),
+      'zh-hant' => (object) array(
+        'langcode' => 'zh-hant',
+      ),
     );
 
     $test_cases = array(
@@ -65,8 +72,8 @@ class LanguageBrowserDetectionUnitTest extends UnitTestBase {
       'en-US,en,fr-CA,fr,es-MX' => 'en',
       'fr,en' => 'en',
       'en,fr' => 'en',
-      'en-US,fr' => 'en',
-      'fr,en-US' => 'en',
+      'en-US,fr' => 'en-US',
+      'fr,en-US' => 'en-US',
       'fr,fr-CA' => 'fr-CA',
       'fr-CA,fr' => 'fr-CA',
       'fr' => 'fr-CA',
@@ -119,6 +126,16 @@ class LanguageBrowserDetectionUnitTest extends UnitTestBase {
       'de,pl' => FALSE,
       'iecRswK4eh' => FALSE,
       $this->randomName(10) => FALSE,
+
+      // Chinese langcodes.
+      'zh-cn, en-us;q=0.90, en;q=0.80, zh;q=0.70' => 'zh-hans',
+      'zh-tw, en-us;q=0.90, en;q=0.80, zh;q=0.70' => 'zh-hant',
+      'zh-hant, en-us;q=0.90, en;q=0.80, zh;q=0.70' => 'zh-hant',
+      'zh-hans, en-us;q=0.90, en;q=0.80, zh;q=0.70' => 'zh-hans',
+      'zh-cn' => 'zh-hans',
+      'zh-tw' => 'zh-hant',
+      'zh-hant' => 'zh-hant',
+      'zh-hans' => 'zh-hans',
     );
 
     foreach ($test_cases as $accept_language => $expected_result) {
