Skip to content

Commit

Permalink
Chinese language negotiation
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg Roach committed Mar 9, 2019
1 parent bb79927 commit e384ba5
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 8 deletions.
73 changes: 65 additions & 8 deletions src/Locale.php
Expand Up @@ -14,6 +14,19 @@
*/
class Locale
{
/**
* Some browsers let the user choose "Chinese, Traditional", but add headers for "zh-HK"...
*
* @var array
*/
private static $http_accept_chinese = array(
'zh-cn' => 'zh-hans-cn',
'zh-sg' => 'zh-hans-sg',
'zh-hk' => 'zh-hant-hk',
'zh-mo' => 'zh-hant-mo',
'zh-tw' => 'zh-hant-tw',
);

/**
* Callback for PHP sort functions - allows lists of locales to be sorted.
* Diacritics are removed and text is capitalized to allow fast/simple sorting.
Expand Down Expand Up @@ -44,9 +57,9 @@ public static function create($code)

if (class_exists($class)) {
return new $class();
} else {
throw new DomainException($code);
}

throw new DomainException($code);
}

/**
Expand Down Expand Up @@ -78,12 +91,9 @@ public static function httpAcceptLanguage(array $server, array $available, Local
return $x[0];
}, $preferences);

// If "de-DE" requested, but not "de", then add it at a lower priority
foreach ($preferences as $code => $priority) {
if (preg_match('/^([a-z]+)[^a-z]/', $code, $match) && !isset($preferences[$match[1]])) {
$preferences[$match[1]] = $priority * 0.5;
}
}
// "Common sense" logic for badly configured clients.
$preferences = self::httpAcceptChinese($preferences);
$preferences = self::httpAcceptDowngrade($preferences);

foreach (array_keys($preferences) as $code) {
try {
Expand All @@ -99,4 +109,51 @@ public static function httpAcceptLanguage(array $server, array $available, Local

return $default;
}

/**
* If a client requests "de-DE" (but not "de"), then add "de" as a lower-priority fallback.
*
* @param $preferences
*
* @return int[]
*/
private static function httpAcceptDowngrade($preferences)
{
foreach ($preferences as $code => $priority) {
// Three parts: "zh-hans-cn" => "zh-hans" and "zh"
if (preg_match('/^(([a-z]+)-[a-z]+)-[a-z]+$/', $code, $match)) {
if (!isset($preferences[$match[2]])) {
$preferences[$match[2]] = $priority * 0.5;
}
if (!isset($preferences[$match[1]])) {
$preferences[$match[1]] = $priority * 0.5;
}
}
// Two parts: "de-de" => "de"
if (preg_match('/^([a-z]+)-[a-z]+$/', $code, $match) && !isset($preferences[$match[1]])) {
$preferences[$match[1]] = $priority * 0.5;
}
}

return $preferences;
}

/**
* Some browsers allow the user to select "Chinese (simplified)", but then use zh-CN instead of zh-Hans.
* This goes against the advice of w3.org.
*
* @param int[] $preferences
*
* @return int[]
*/
private static function httpAcceptChinese($preferences)
{
foreach (self::$http_accept_chinese as $old => $new) {
if (array_key_exists($old, $preferences) && !array_key_exists($new, $preferences)) {
$preferences[$new] = $preferences[$old] * 0.5;
}
}

return $preferences;
}
}
48 changes: 48 additions & 0 deletions test/LocaleTest.php
Expand Up @@ -138,6 +138,54 @@ public function testHttpAcceptLanguageDowngrade()
$this->assertEquals(Locale::create('de'), $locale);
}

/**
* Test language negotiation
*/
public function testHttpAcceptLanguageDoubleDowngrade()
{
$available = array(
Locale::create('zh'),
);
$server = array('HTTP_ACCEPT_LANGUAGE' => 'zh-Hans-CN');
$default = Locale::create('en-US');
$locale = Locale::httpAcceptLanguage($server, $available, $default);

$this->assertEquals(Locale::create('zh'), $locale);
}

/**
* Test language negotiation
*/
public function testHttpAcceptChinese()
{
$available = array(
Locale::create('zh-Hans'),
Locale::create('zh-Hant'),
);

$default = Locale::create('en-US');

$server = array('HTTP_ACCEPT_LANGUAGE' => 'zh-CN');
$locale = Locale::httpAcceptLanguage($server, $available, $default);
$this->assertEquals(Locale::create('zh-Hans'), $locale);

$server = array('HTTP_ACCEPT_LANGUAGE' => 'zh-SG');
$locale = Locale::httpAcceptLanguage($server, $available, $default);
$this->assertEquals(Locale::create('zh-Hans'), $locale);

$server = array('HTTP_ACCEPT_LANGUAGE' => 'zh-HK');
$locale = Locale::httpAcceptLanguage($server, $available, $default);
$this->assertEquals(Locale::create('zh-Hant'), $locale);

$server = array('HTTP_ACCEPT_LANGUAGE' => 'zh-MO');
$locale = Locale::httpAcceptLanguage($server, $available, $default);
$this->assertEquals(Locale::create('zh-Hant'), $locale);

$server = array('HTTP_ACCEPT_LANGUAGE' => 'zh-TW');
$locale = Locale::httpAcceptLanguage($server, $available, $default);
$this->assertEquals(Locale::create('zh-Hant'), $locale);
}

/**
* Test language negotiation
*/
Expand Down

0 comments on commit e384ba5

Please sign in to comment.