$languageCode, 'country' => $countryCode); } /** * Return a list of all locale codes currently available in the known domains * * @return array */ public static function getAvailableLocaleCodes() { $codes = array(static::DEFAULT_LOCALE); foreach (array_values(self::$knownDomains) as $directory) { $dh = opendir($directory); while (false !== ($name = readdir($dh))) { if (substr($name, 0, 1) !== '.' && false === in_array($name, $codes) && is_dir($directory . DIRECTORY_SEPARATOR . $name) ) { $codes[] = $name; } } } return $codes; } /** * Return the preferred locale based on the given HTTP header and the available translations * * @param string $header The HTTP "Accept-Language" header * * @return string The browser's preferred locale code */ public static function getPreferredLocaleCode($header) { $headerValues = explode(',', $header); for ($i = 0; $i < count($headerValues); $i++) { // In order to accomplish a stable sort we need to take the original // index into account as well during element comparison $headerValues[$i] = array($headerValues[$i], $i); } usort( // Sort DESC but keep equal elements ASC $headerValues, function ($a, $b) { $qValA = (float) (strpos($a[0], ';') > 0 ? substr(array_pop((explode(';', $a[0], 2))), 2) : 1); $qValB = (float) (strpos($b[0], ';') > 0 ? substr(array_pop((explode(';', $b[0], 2))), 2) : 1); return $qValA < $qValB ? 1 : ($qValA > $qValB ? -1 : ($a[1] > $b[1] ? 1 : ($a[1] < $b[1] ? -1 : 0))); } ); for ($i = 0; $i < count($headerValues); $i++) { // We need to reset the array to its original structure once it's sorted $headerValues[$i] = $headerValues[$i][0]; } $requestedLocales = array(); foreach ($headerValues as $headerValue) { if (strpos($headerValue, ';') > 0) { $parts = explode(';', $headerValue, 2); $headerValue = $parts[0]; } $requestedLocales[] = str_replace('-', '_', $headerValue); } $similarMatch = null; $availableLocales = static::getAvailableLocaleCodes(); $perfectMatch = array_shift((array_intersect($requestedLocales, $availableLocales))); foreach ($requestedLocales as $requestedLocale) { if ($perfectMatch === $requestedLocale) { // The perfect match must be preferred when reached before a similar match is found return $perfectMatch; } $similarMatches = array(); $localeObj = static::splitLocaleCode($requestedLocale); foreach ($availableLocales as $availableLocale) { if (static::splitLocaleCode($availableLocale)->language === $localeObj->language) { $similarMatches[] = $availableLocale; } } if (!empty($similarMatches)) { $similarMatch = array_shift($similarMatches); // There is no "best" similar match, just use the first break; } } if (!$perfectMatch && $similarMatch) { return $similarMatch; } elseif ($similarMatch && static::splitLocaleCode($similarMatch)->language === static::splitLocaleCode($perfectMatch)->language) { return $perfectMatch; } elseif ($similarMatch) { return $similarMatch; } return static::DEFAULT_LOCALE; } }