Skip to content

Commit

Permalink
Merge pull request #133 from codemix/131-geoip
Browse files Browse the repository at this point in the history
GeoIp tests and code reorganization
  • Loading branch information
mikehaertl authored Feb 13, 2018
2 parents 06b9294 + 6c3d473 commit 6f0c49e
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 42 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,27 @@ Any other `pt-CC` code | `/pt-cc` | `pt-CC`
`pt` | `/pt` | `pt`


#### Detection via GeoIP server module

Since 1.7.0 language can also be detected via the webserver's GeoIP module.
Note though that this only happens if no valid language was found in the
browser settings.

For this feature to work the related GeoIp module must already be installed and
it must provide the country code in a server variable in `$_SERVER`. You can
configure the key in `$geoIpServerVar`. The default is `HTTP_X_GEO_COUNTRY`.

To enable this feature, you have to provide a list of GeoIp country codes and
index them by the corresponding language that should be set:

```php
'geoIpLanguageCountries' => [
'de' => ['DEU', 'AUT'],
'pt' => ['PRT', 'BRA'],
],
```


### Excluding Routes / URLs

You may want to disable the language processing for some routes and URLs with the
Expand Down
81 changes: 50 additions & 31 deletions UrlManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,17 @@ class UrlManager extends BaseUrlManager
public $geoIpServerVar = 'HTTP_X_GEO_COUNTRY';

/**
* @var array list of GeoIP countries indexed by corresponding language code
* e.g. 'ru' => ['RUS','AZE','ARM','BLR','KAZ','KGZ','MDA','TJK','TKM','UZB','UKR']
* will set app language to ru for listed countries.
* The default is an empty list which disables GeoIP detection.
* @var array list of GeoIP countries indexed by corresponding language
* code. The default is an empty list which disables GeoIP detection.
* Example:
*
* ~~~php
* [
* // Set app language to 'ru' for these GeoIp countries
* 'ru' => ['RUS','AZE','ARM','BLR','KAZ','KGZ','MDA','TJK','TKM','UZB','UKR']
*
* ]
* ~~~
*/
public $geoIpLanguageCountries = [];

Expand Down Expand Up @@ -236,7 +243,7 @@ public function createUrl($params)

$isLanguageGiven = isset($params[$this->languageParam]);
$language = $isLanguageGiven ? $params[$this->languageParam] : Yii::$app->language;
$isDefaultLanguage = $language===$this->getDefaultLanguage();
$isDefaultLanguage = $language === $this->getDefaultLanguage();

if ($isLanguageGiven) {
unset($params[$this->languageParam]);
Expand Down Expand Up @@ -337,7 +344,7 @@ protected function processLocaleUrl($normalized)
$parts = [];
foreach ($this->languages as $k => $v) {
$value = is_string($k) ? $k : $v;
if (substr($value, -2)==='-*') {
if (substr($value, -2) === '-*') {
$lng = substr($value, 0, -2);
$parts[] = "$lng\-[a-z]{2,3}";
$parts[] = $lng;
Expand All @@ -356,13 +363,13 @@ protected function processLocaleUrl($normalized)
// lowercase language, uppercase country
list($language,$country) = $this->matchCode($code);
if ($country!==null) {
if ($code==="$language-$country" && !$this->keepUppercaseLanguageCode) {
if ($code === "$language-$country" && !$this->keepUppercaseLanguageCode) {
$this->redirectToLanguage(strtolower($code)); // Redirect ll-CC to ll-cc
} else {
$language = "$language-$country";
}
}
if ($language===null) {
if ($language === null) {
$language = $code;
}
}
Expand All @@ -374,7 +381,7 @@ protected function processLocaleUrl($normalized)

// "Reset" case: We called e.g. /fr/demo/page so the persisted language was set back to "fr".
// Now we can redirect to the URL without language prefix, if default prefixes are disabled.
$reset = !$this->enableDefaultLanguageUrlCode && $language===$this->_defaultLanguage;
$reset = !$this->enableDefaultLanguageUrlCode && $language === $this->_defaultLanguage;

if ($reset || $normalized) {
$this->redirectToLanguage('');
Expand All @@ -384,33 +391,18 @@ protected function processLocaleUrl($normalized)
if ($this->enableLanguagePersistence) {
$language = $this->loadPersistedLanguage();
}
if ($language===null && $this->enableLanguageDetection) {
foreach ($this->_request->getAcceptableLanguages() as $acceptable) {
list($language,$country) = $this->matchCode($acceptable);
if ($language!==null) {
$language = $country===null ? $language : "$language-$country";
Yii::trace("Detected browser language '$language'.", __METHOD__);
break;
}
}
}
if ($language===null && isset($_SERVER[$this->geoIpServerVar])) {
foreach ($this->geoIpLanguageCountries as $key => $codes) {
if (in_array($_SERVER[$this->geoIpServerVar], $codes)) {
$language = $key;
break;
}
}
if ($language === null) {
$language = $this->detectLanguage();
}
if ($language===null || $language===$this->_defaultLanguage) {
if ($language === null || $language === $this->_defaultLanguage) {
if (!$this->enableDefaultLanguageUrlCode) {
return;
} else {
$language = $this->_defaultLanguage;
}
}
// #35: Only redirect if a valid language was found
if ($this->matchCode($language)===[null, null]) {
if ($this->matchCode($language) === [null, null]) {
return;
}

Expand Down Expand Up @@ -469,13 +461,40 @@ protected function loadPersistedLanguage()
$language = Yii::$app->session->get($this->languageSessionKey);
$language!==null && Yii::trace("Found persisted language '$language' in session.", __METHOD__);
}
if ($language===null) {
if ($language === null) {
$language = $this->_request->getCookies()->getValue($this->languageCookieName);
$language!==null && Yii::trace("Found persisted language '$language' in cookie.", __METHOD__);
}
return $language;
}

/**
* @return string|null the language detected from request headers or via
* GeoIp module
*/
protected function detectLanguage()
{
if ($this->enableLanguageDetection) {
foreach ($this->_request->getAcceptableLanguages() as $acceptable) {
list($language,$country) = $this->matchCode($acceptable);
if ($language!==null) {
$language = $country === null ? $language : "$language-$country";
Yii::trace("Detected browser language '$language'.", __METHOD__);
return $language;
}
}
}
if (isset($_SERVER[$this->geoIpServerVar])) {
foreach ($this->geoIpLanguageCountries as $key => $codes) {
$country = $_SERVER[$this->geoIpServerVar];
if (in_array($country, $codes)) {
Yii::trace("Detected GeoIp language '$key'.", __METHOD__);
return $key;
}
}
}
}

/**
* Tests whether the given code matches any of the configured languages.
*
Expand Down Expand Up @@ -536,7 +555,7 @@ protected function matchCode($code)
$language = $code;
$country = null;
$parts = explode('-', $code);
if (count($parts)===2) {
if (count($parts) === 2) {
$language = $parts[0];
$country = strtoupper($parts[1]);
}
Expand Down Expand Up @@ -588,7 +607,7 @@ protected function redirectToLanguage($language)
array_unshift($params, $route);
$url = $this->createUrl($params);
// Required to prevent double slashes on generated URLs
if ($this->suffix==='/' && $route==='' && count($params)===1) {
if ($this->suffix === '/' && $route === '' && count($params) === 1) {
$url = rtrim($url, '/').'/';
}
// Prevent redirects to same URL which could happen in certain
Expand Down
Loading

0 comments on commit 6f0c49e

Please sign in to comment.