Skip to content

Commit 6f0c49e

Browse files
authored
Merge pull request #133 from codemix/131-geoip
GeoIp tests and code reorganization
2 parents 06b9294 + 6c3d473 commit 6f0c49e

File tree

5 files changed

+258
-42
lines changed

5 files changed

+258
-42
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,27 @@ Any other `pt-CC` code | `/pt-cc` | `pt-CC`
329329
`pt` | `/pt` | `pt`
330330

331331

332+
#### Detection via GeoIP server module
333+
334+
Since 1.7.0 language can also be detected via the webserver's GeoIP module.
335+
Note though that this only happens if no valid language was found in the
336+
browser settings.
337+
338+
For this feature to work the related GeoIp module must already be installed and
339+
it must provide the country code in a server variable in `$_SERVER`. You can
340+
configure the key in `$geoIpServerVar`. The default is `HTTP_X_GEO_COUNTRY`.
341+
342+
To enable this feature, you have to provide a list of GeoIp country codes and
343+
index them by the corresponding language that should be set:
344+
345+
```php
346+
'geoIpLanguageCountries' => [
347+
'de' => ['DEU', 'AUT'],
348+
'pt' => ['PRT', 'BRA'],
349+
],
350+
```
351+
352+
332353
### Excluding Routes / URLs
333354

334355
You may want to disable the language processing for some routes and URLs with the

UrlManager.php

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,17 @@ class UrlManager extends BaseUrlManager
141141
public $geoIpServerVar = 'HTTP_X_GEO_COUNTRY';
142142

143143
/**
144-
* @var array list of GeoIP countries indexed by corresponding language code
145-
* e.g. 'ru' => ['RUS','AZE','ARM','BLR','KAZ','KGZ','MDA','TJK','TKM','UZB','UKR']
146-
* will set app language to ru for listed countries.
147-
* The default is an empty list which disables GeoIP detection.
144+
* @var array list of GeoIP countries indexed by corresponding language
145+
* code. The default is an empty list which disables GeoIP detection.
146+
* Example:
147+
*
148+
* ~~~php
149+
* [
150+
* // Set app language to 'ru' for these GeoIp countries
151+
* 'ru' => ['RUS','AZE','ARM','BLR','KAZ','KGZ','MDA','TJK','TKM','UZB','UKR']
152+
*
153+
* ]
154+
* ~~~
148155
*/
149156
public $geoIpLanguageCountries = [];
150157

@@ -236,7 +243,7 @@ public function createUrl($params)
236243

237244
$isLanguageGiven = isset($params[$this->languageParam]);
238245
$language = $isLanguageGiven ? $params[$this->languageParam] : Yii::$app->language;
239-
$isDefaultLanguage = $language===$this->getDefaultLanguage();
246+
$isDefaultLanguage = $language === $this->getDefaultLanguage();
240247

241248
if ($isLanguageGiven) {
242249
unset($params[$this->languageParam]);
@@ -337,7 +344,7 @@ protected function processLocaleUrl($normalized)
337344
$parts = [];
338345
foreach ($this->languages as $k => $v) {
339346
$value = is_string($k) ? $k : $v;
340-
if (substr($value, -2)==='-*') {
347+
if (substr($value, -2) === '-*') {
341348
$lng = substr($value, 0, -2);
342349
$parts[] = "$lng\-[a-z]{2,3}";
343350
$parts[] = $lng;
@@ -356,13 +363,13 @@ protected function processLocaleUrl($normalized)
356363
// lowercase language, uppercase country
357364
list($language,$country) = $this->matchCode($code);
358365
if ($country!==null) {
359-
if ($code==="$language-$country" && !$this->keepUppercaseLanguageCode) {
366+
if ($code === "$language-$country" && !$this->keepUppercaseLanguageCode) {
360367
$this->redirectToLanguage(strtolower($code)); // Redirect ll-CC to ll-cc
361368
} else {
362369
$language = "$language-$country";
363370
}
364371
}
365-
if ($language===null) {
372+
if ($language === null) {
366373
$language = $code;
367374
}
368375
}
@@ -374,7 +381,7 @@ protected function processLocaleUrl($normalized)
374381

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

379386
if ($reset || $normalized) {
380387
$this->redirectToLanguage('');
@@ -384,33 +391,18 @@ protected function processLocaleUrl($normalized)
384391
if ($this->enableLanguagePersistence) {
385392
$language = $this->loadPersistedLanguage();
386393
}
387-
if ($language===null && $this->enableLanguageDetection) {
388-
foreach ($this->_request->getAcceptableLanguages() as $acceptable) {
389-
list($language,$country) = $this->matchCode($acceptable);
390-
if ($language!==null) {
391-
$language = $country===null ? $language : "$language-$country";
392-
Yii::trace("Detected browser language '$language'.", __METHOD__);
393-
break;
394-
}
395-
}
396-
}
397-
if ($language===null && isset($_SERVER[$this->geoIpServerVar])) {
398-
foreach ($this->geoIpLanguageCountries as $key => $codes) {
399-
if (in_array($_SERVER[$this->geoIpServerVar], $codes)) {
400-
$language = $key;
401-
break;
402-
}
403-
}
394+
if ($language === null) {
395+
$language = $this->detectLanguage();
404396
}
405-
if ($language===null || $language===$this->_defaultLanguage) {
397+
if ($language === null || $language === $this->_defaultLanguage) {
406398
if (!$this->enableDefaultLanguageUrlCode) {
407399
return;
408400
} else {
409401
$language = $this->_defaultLanguage;
410402
}
411403
}
412404
// #35: Only redirect if a valid language was found
413-
if ($this->matchCode($language)===[null, null]) {
405+
if ($this->matchCode($language) === [null, null]) {
414406
return;
415407
}
416408

@@ -469,13 +461,40 @@ protected function loadPersistedLanguage()
469461
$language = Yii::$app->session->get($this->languageSessionKey);
470462
$language!==null && Yii::trace("Found persisted language '$language' in session.", __METHOD__);
471463
}
472-
if ($language===null) {
464+
if ($language === null) {
473465
$language = $this->_request->getCookies()->getValue($this->languageCookieName);
474466
$language!==null && Yii::trace("Found persisted language '$language' in cookie.", __METHOD__);
475467
}
476468
return $language;
477469
}
478470

471+
/**
472+
* @return string|null the language detected from request headers or via
473+
* GeoIp module
474+
*/
475+
protected function detectLanguage()
476+
{
477+
if ($this->enableLanguageDetection) {
478+
foreach ($this->_request->getAcceptableLanguages() as $acceptable) {
479+
list($language,$country) = $this->matchCode($acceptable);
480+
if ($language!==null) {
481+
$language = $country === null ? $language : "$language-$country";
482+
Yii::trace("Detected browser language '$language'.", __METHOD__);
483+
return $language;
484+
}
485+
}
486+
}
487+
if (isset($_SERVER[$this->geoIpServerVar])) {
488+
foreach ($this->geoIpLanguageCountries as $key => $codes) {
489+
$country = $_SERVER[$this->geoIpServerVar];
490+
if (in_array($country, $codes)) {
491+
Yii::trace("Detected GeoIp language '$key'.", __METHOD__);
492+
return $key;
493+
}
494+
}
495+
}
496+
}
497+
479498
/**
480499
* Tests whether the given code matches any of the configured languages.
481500
*
@@ -536,7 +555,7 @@ protected function matchCode($code)
536555
$language = $code;
537556
$country = null;
538557
$parts = explode('-', $code);
539-
if (count($parts)===2) {
558+
if (count($parts) === 2) {
540559
$language = $parts[0];
541560
$country = strtoupper($parts[1]);
542561
}
@@ -588,7 +607,7 @@ protected function redirectToLanguage($language)
588607
array_unshift($params, $route);
589608
$url = $this->createUrl($params);
590609
// Required to prevent double slashes on generated URLs
591-
if ($this->suffix==='/' && $route==='' && count($params)===1) {
610+
if ($this->suffix === '/' && $route === '' && count($params) === 1) {
592611
$url = rtrim($url, '/').'/';
593612
}
594613
// Prevent redirects to same URL which could happen in certain

0 commit comments

Comments
 (0)