$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; } } } sort($codes); 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) { $tagA = explode(';', $a[0], 2); $tagB = explode(';', $b[0], 2); $qValA = (float) (strpos($a[0], ';') > 0 ? substr(array_pop($tagA), 2) : 1); $qValB = (float) (strpos($b[0], ';') > 0 ? substr(array_pop($tagB), 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); } $requestedLocales = array_combine( array_map('strtolower', array_values($requestedLocales)), array_values($requestedLocales) ); $availableLocales = static::getAvailableLocaleCodes(); $availableLocales = array_combine( array_map('strtolower', array_values($availableLocales)), array_values($availableLocales) ); $similarMatch = null; foreach ($requestedLocales as $requestedLocaleLowered => $requestedLocale) { $localeObj = static::splitLocaleCode($requestedLocaleLowered); if (isset($availableLocales[$requestedLocaleLowered]) && (! $similarMatch || static::splitLocaleCode($similarMatch)->language === $localeObj->language) ) { // Prefer perfect match only if no similar match has been found yet or the perfect match is more precise // than the similar match return $availableLocales[$requestedLocaleLowered]; } if (! $similarMatch) { foreach ($availableLocales as $availableLocaleLowered => $availableLocale) { if (static::splitLocaleCode($availableLocaleLowered)->language === $localeObj->language) { $similarMatch = $availableLocaleLowered; break; } } } } return $similarMatch ? $availableLocales[$similarMatch] : static::DEFAULT_LOCALE; } }