Repository URL to install this package:
|
Version:
1.0.0 ▾
|
<?php namespace Modules\Core\Services;
use Illuminate\Http\Request;
use Locale;
class LanguageNegotiator
{
/**
* @var String
*/
private $defaultLocale;
/**
* @var Array
*/
private $supportedLanguages;
/**
* @var Request
*/
private $request;
/**
* @param string $defaultLocale
* @param array $supportedLanguages
* @param Request $request
*/
public function __construct($defaultLocale, $supportedLanguages, Request $request)
{
$this->defaultLocale = $defaultLocale;
$this->supportedLanguages = $supportedLanguages;
$this->request = $request;
}
/**
* Negotiates language with the user's browser through the Accept-Language
* HTTP header or the user's host address. Language codes are generally in
* the form "ll" for a language spoken in only one country, or "ll-CC" for a
* language spoken in a particular country. For example, U.S. English is
* "en-US", while British English is "en-UK". Portuguese as spoken in
* Portugal is "pt-PT", while Brazilian Portuguese is "pt-BR".
*
* This function is based on negotiateLanguage from Pear HTTP2
* http://pear.php.net/package/HTTP2/
*
* Quality factors in the Accept-Language: header are supported, e.g.:
* Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
*
* @return string The negotiated language result or app.locale.
*/
public function negotiateLanguage()
{
$matches = $this->getMatchesFromAcceptedLanguages();
foreach (array_keys($matches) as $key) {
if (!empty($this->supportedLanguages[$key])) {
return $key;
}
}
// If any (i.e. "*") is acceptable, return the first supported format
if (isset($matches['*'])) {
reset($this->supportedLanguages);
return key($this->supportedLanguages);
}
if (class_exists('Locale') && !empty(request()->server('HTTP_ACCEPT_LANGUAGE'))) {
$httpAcceptLanguage = Locale::acceptFromHttp(request()->server('HTTP_ACCEPT_LANGUAGE'));
if (!empty($this->supportedLanguages[$httpAcceptLanguage])) {
return $httpAcceptLanguage;
}
}
if ($this->request->server('remoteHost')) {
$remoteHost = explode('.', $this->request->server('remoteHost'));
$lang = strtolower(end($remoteHost));
if (!empty($this->supportedLanguages[$lang])) {
return $lang;
}
}
return $this->defaultLocale;
}
/**
* Return all the accepted languages from the browser
* @return array Matches from the header field Accept-Languages
*/
private function getMatchesFromAcceptedLanguages()
{
$matches = [];
$acceptLanguages = $this->request->header('Accept-Language');
$acceptLanguages = explode(',', $acceptLanguages);
$genericMatches = [];
foreach ($acceptLanguages as $option) {
$option = array_map('trim', explode(';', $option));
$l = $option[0];
$q = null;
// Assign default low weight for generic values
if ($l == '*/*') {
$q = 0.01;
} elseif (substr($l, -1) == '*') {
$q = 0.02;
}
if (isset($option[1])) {
$q = (float)str_replace('q=', '', $option[1]);
}
// Unweighted values, get high weight by their position in the
// list
$q = isset($q) ? $q : 1000 - count($matches);
$matches[$l] = $q;
//If for some reason the Accept-Language header only sends language with country
//we should make the language without country an accepted option, with a value
//less than it's parent.
$langOps = explode('-', $l);
array_pop($langOps);
while (!empty($langOps)) {
//The new generic option needs to be slightly less important than it's base
$q -= 0.001;
$op = implode('-', $langOps);
if (empty($genericMatches[$op]) || $genericMatches[$op] > $q) {
$genericMatches[$op] = $q;
}
array_pop($langOps);
}
}
$matches = array_merge($genericMatches, $matches);
arsort($matches, SORT_NUMERIC);
return $matches;
}
}