1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* HiPanel core package |
5
|
|
|
* |
6
|
|
|
* @link https://hipanel.com/ |
7
|
|
|
* @package hipanel-core |
8
|
|
|
* @license BSD-3-Clause |
9
|
|
|
* @copyright Copyright (c) 2014-2016, HiQDev (http://hiqdev.com/) |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace hipanel\base; |
13
|
|
|
|
14
|
|
|
use Yii; |
15
|
|
|
use yii\helpers\Url; |
16
|
|
|
use yii\web\Cookie; |
17
|
|
|
use yii\web\UrlManager; |
18
|
|
|
|
19
|
|
|
class LanguageUrlManager extends UrlManager |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* @var array list of available language codes. More specific patterns should come first, e.g. 'en_us' |
23
|
|
|
* before 'en'. This can also contain mapping of <url_value> => <language>, e.g. 'english' => 'en'. |
24
|
|
|
*/ |
25
|
|
|
public $languages = []; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var string the name of the session key that is used to store the language. Default is '_language'. |
29
|
|
|
*/ |
30
|
|
|
public $languageSessionKey = '_language'; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var string the name of the language cookie. Default is '_language'. |
34
|
|
|
*/ |
35
|
|
|
public $languageCookieName = '_language'; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var string the language that was initially set in the application configuration |
39
|
|
|
*/ |
40
|
|
|
protected $_defaultLanguage; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var int number of seconds how long the language information should be stored in cookie, |
44
|
|
|
* if `$enableLanguagePersistence` is true. Set to `false` to disable the language cookie completely. |
45
|
|
|
* Default is 30 days. |
46
|
|
|
*/ |
47
|
|
|
public $languageCookieDuration = 2592000; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @var integer expiration date of the cookie storing the language of the site. |
51
|
|
|
*/ |
52
|
|
|
public $expireDays = 30; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @var string if a parameter with this name is passed to any `createUrl()` method, the created URL |
56
|
|
|
* will use the language specified there. URLs created this way can be used to switch to a different |
57
|
|
|
* language. If no such parameter is used, the currently detected application language is used. |
58
|
|
|
*/ |
59
|
|
|
public $languageParam = 'language'; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var \Closure function to execute after changing the language of the site. |
63
|
|
|
*/ |
64
|
|
|
public $callback; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var string Name of the cookie. |
68
|
|
|
*/ |
69
|
|
|
public $cookieName = 'language'; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var \yii\web\Request |
73
|
|
|
*/ |
74
|
|
|
protected $_request; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* {@inheritdoc} |
78
|
|
|
*/ |
79
|
|
|
public function init() |
80
|
|
|
{ |
81
|
|
|
$this->initLanguage(); |
82
|
|
|
return parent::init(); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Setting the language of the site. |
87
|
|
|
*/ |
88
|
|
|
public function initLanguage() |
89
|
|
|
{ |
90
|
|
|
$language = Yii::$app->request->get($this->languageParam); |
91
|
|
|
if ($language) { |
92
|
|
|
if (isset($this->languages[$language])) { |
93
|
|
|
$language = $this->languages[$language]; |
94
|
|
|
} |
95
|
|
|
if ($this->_isValidLanguage($language)) { |
96
|
|
|
return $this->saveLanguage($language); |
97
|
|
|
} elseif (!Yii::$app->request->isAjax) { |
98
|
|
|
return $this->_redirect(); |
99
|
|
|
} |
100
|
|
|
} elseif (Yii::$app->request->cookies->has($this->cookieName)) { |
101
|
|
|
if ($this->_isValidLanguage(Yii::$app->request->cookies->getValue($this->cookieName))) { |
102
|
|
|
// Yii::$app->language = Yii::$app->request->cookies->getValue($this->cookieName); |
|
|
|
|
103
|
|
|
$this->applyLanguage(Yii::$app->request->cookies->getValue($this->cookieName)); |
104
|
|
|
return; |
105
|
|
|
} else { |
106
|
|
|
Yii::$app->response->cookies->remove($this->cookieName); |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
$this->detectLanguage(); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Saving language into cookie and database. |
114
|
|
|
* @param string $language - The language to save. |
115
|
|
|
* @return static |
116
|
|
|
*/ |
117
|
|
|
public function saveLanguage($language) |
118
|
|
|
{ |
119
|
|
|
// Yii::$app->language = $language; |
|
|
|
|
120
|
|
|
$this->applyLanguage($language); |
121
|
|
|
$this->saveLanguageIntoCookie($language); |
122
|
|
|
if (is_callable($this->callback)) { |
123
|
|
|
call_user_func($this->callback); |
124
|
|
|
} |
125
|
|
|
if (Yii::$app->request->isAjax) { |
126
|
|
|
Yii::$app->end(); |
127
|
|
|
} |
128
|
|
|
return $this->_redirect(); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Determine language based on UserAgent. |
133
|
|
|
*/ |
134
|
|
|
public function detectLanguage() |
135
|
|
|
{ |
136
|
|
|
$acceptableLanguages = Yii::$app->getRequest()->getAcceptableLanguages(); |
137
|
|
|
foreach ($acceptableLanguages as $language) { |
138
|
|
|
if ($this->_isValidLanguage($language)) { |
139
|
|
|
// Yii::$app->language = $language; |
|
|
|
|
140
|
|
|
$this->applyLanguage($language); |
141
|
|
|
$this->saveLanguageIntoCookie($language); |
142
|
|
|
return; |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
foreach ($acceptableLanguages as $language) { |
146
|
|
|
$pattern = preg_quote(substr($language, 0, 2), '/'); |
147
|
|
|
foreach ($this->languages as $key => $value) { |
148
|
|
|
if (preg_match('/^' . $pattern . '/', $value) || preg_match('/^' . $pattern . '/', $key)) { |
149
|
|
|
// Yii::$app->language = $this->_isValidLanguage($key) ? $key : $value; |
|
|
|
|
150
|
|
|
$this->applyLanguage($this->_isValidLanguage($key) ? $key : $value); |
151
|
|
|
$this->saveLanguageIntoCookie(Yii::$app->language); |
152
|
|
|
return; |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Save language into cookie. |
160
|
|
|
* @param string $language |
161
|
|
|
*/ |
162
|
|
|
public function saveLanguageIntoCookie($language) |
163
|
|
|
{ |
164
|
|
|
$cookie = new \yii\web\Cookie([ |
165
|
|
|
'name' => $this->cookieName, |
166
|
|
|
'value' => $language, |
167
|
|
|
'expire' => time() + 86400 * $this->expireDays, |
168
|
|
|
]); |
169
|
|
|
Yii::$app->response->cookies->add($cookie); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Redirects the browser to the referer URL. |
174
|
|
|
* @return static |
175
|
|
|
*/ |
176
|
|
|
private function _redirect() |
177
|
|
|
{ |
178
|
|
|
$redirect = Yii::$app->request->absoluteUrl === Yii::$app->request->referrer ? '/' : Yii::$app->request->referrer; |
179
|
|
|
return Yii::$app->response->redirect($redirect); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Determines whether the language received as a parameter can be processed. |
184
|
|
|
* @param string $language |
185
|
|
|
* @return boolean |
186
|
|
|
*/ |
187
|
|
|
private function _isValidLanguage($language) |
188
|
|
|
{ |
189
|
|
|
return is_string($language) && (isset($this->languages[$language]) || in_array($language, $this->languages, true)); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
protected function applyLanguage($language) |
193
|
|
|
{ |
194
|
|
|
Yii::$app->language = $language; |
195
|
|
|
Yii::$app->formatter->locale = $language; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
|
199
|
|
|
|
200
|
|
|
|
201
|
|
|
// /** |
202
|
|
|
// * @inheritdoc |
203
|
|
|
// */ |
204
|
|
|
// public function parseRequest($request) |
205
|
|
|
// { |
206
|
|
|
|
207
|
|
|
// $this->detectLanguage($request); |
|
|
|
|
208
|
|
|
|
209
|
|
|
// return parent::parseRequest($request); |
|
|
|
|
210
|
|
|
// } |
211
|
|
|
|
212
|
|
|
// protected function setUpLanguage($language) |
|
|
|
|
213
|
|
|
// { |
214
|
|
|
// Yii::$app->language = $language; |
215
|
|
|
// } |
216
|
|
|
|
217
|
|
|
// /** |
|
|
|
|
218
|
|
|
// * Checks for a language or locale parameter in the URL and rewrites the pathInfo if found. |
219
|
|
|
// * If no parameter is found it will try to detect the language from persistent storage (session / |
220
|
|
|
// * cookie) or from browser settings. |
221
|
|
|
// * |
222
|
|
|
// * @var \yii\web\Request $request |
223
|
|
|
// */ |
224
|
|
|
// protected function detectLanguage($request) |
225
|
|
|
// { |
226
|
|
|
// $this->_request = $request; |
227
|
|
|
// $pathInfo = $request->getPathInfo(); |
228
|
|
|
// $parts = []; |
229
|
|
|
// foreach ($this->languages as $k => $v) { |
230
|
|
|
// $value = is_string($k) ? $k : $v; |
231
|
|
|
// if (substr($value, -2)==='-*') { |
232
|
|
|
// $lng = substr($value, 0, -2); |
233
|
|
|
// $parts[] = "$lng\-[a-z]{2,3}"; |
234
|
|
|
// $parts[] = $lng; |
235
|
|
|
// } else { |
236
|
|
|
// $parts[] = $value; |
237
|
|
|
// } |
238
|
|
|
// } |
239
|
|
|
// $pattern = implode('|', $parts); |
240
|
|
|
// if ($request->get($this->languageParam) && preg_match("#^($pattern)\b(/?)#i", $request->get($this->languageParam), $m)) { |
241
|
|
|
// $request->setPathInfo(mb_substr($pathInfo, mb_strlen($m[1].$m[2]))); |
242
|
|
|
// $code = $m[1]; |
243
|
|
|
// if (isset($this->languages[$code])) { |
244
|
|
|
// // Replace alias with language code |
245
|
|
|
// $language = $this->languages[$code]; |
246
|
|
|
// } else { |
247
|
|
|
// list($language,$country) = $this->matchCode($code); |
248
|
|
|
// if ($country!==null) { |
249
|
|
|
// if ($code==="$language-$country") { |
250
|
|
|
// $this->redirectToLanguage(''); |
251
|
|
|
// } else { |
252
|
|
|
// $language = "$language-$country"; |
253
|
|
|
// } |
254
|
|
|
// } |
255
|
|
|
// if ($language===null) { |
256
|
|
|
// $language = $code; |
257
|
|
|
// } |
258
|
|
|
// } |
259
|
|
|
// Yii::$app->language = $language; |
260
|
|
|
// Yii::$app->session[$this->languageSessionKey] = $language; |
261
|
|
|
// if ($this->languageCookieDuration) { |
262
|
|
|
// $cookie = new Cookie([ |
263
|
|
|
// 'name' => $this->languageCookieName, |
264
|
|
|
// 'httpOnly' => true |
265
|
|
|
// ]); |
266
|
|
|
// $cookie->value = $language; |
267
|
|
|
// $cookie->expire = time() + (int) $this->languageCookieDuration; |
268
|
|
|
// Yii::$app->getResponse()->getCookies()->add($cookie); |
269
|
|
|
// } |
270
|
|
|
// $this->redirectToLanguage(''); |
271
|
|
|
// } else { |
272
|
|
|
// $language = null; |
273
|
|
|
// $language = Yii::$app->session->get($this->languageSessionKey); |
274
|
|
|
// if ($language===null) { |
275
|
|
|
// $language = $request->getCookies()->getValue($this->languageCookieName); |
276
|
|
|
// } |
277
|
|
|
// if ($language===null) { |
278
|
|
|
// foreach ($request->getAcceptableLanguages() as $acceptable) { |
279
|
|
|
// list($language,$country) = $this->matchCode($acceptable); |
280
|
|
|
// if ($language!==null) { |
281
|
|
|
// $language = $country===null ? $language : "$language-$country"; |
282
|
|
|
// break; |
283
|
|
|
// } |
284
|
|
|
// } |
285
|
|
|
// } |
286
|
|
|
// if ($language===null || $language===$this->_defaultLanguage) { |
287
|
|
|
// return; |
288
|
|
|
// } |
289
|
|
|
// // #35: Only redirect if a valid language was found |
290
|
|
|
// if ($this->matchCode($language)===[null, null]) { |
291
|
|
|
// return; |
292
|
|
|
// } |
293
|
|
|
|
294
|
|
|
// $key = array_search($language, $this->languages); |
|
|
|
|
295
|
|
|
// if ($key && is_string($key)) { |
296
|
|
|
// $language = $key; |
297
|
|
|
// } |
298
|
|
|
|
299
|
|
|
// $this->setUpLanguage($language); |
|
|
|
|
300
|
|
|
// $this->redirectToLanguage(''); |
301
|
|
|
// } |
302
|
|
|
// } |
303
|
|
|
|
304
|
|
|
// /** |
305
|
|
|
// * Tests whether the given code matches any of the configured languages. |
306
|
|
|
// * |
307
|
|
|
// * If the code is a single language code, and matches either |
308
|
|
|
// * |
309
|
|
|
// * - an exact language as configured (ll) |
310
|
|
|
// * - a language with a country wildcard (ll-*) |
311
|
|
|
// * |
312
|
|
|
// * this language code is returned. |
313
|
|
|
// * |
314
|
|
|
// * If the code also contains a country code, and matches either |
315
|
|
|
// * |
316
|
|
|
// * - an exact language/country code as configured (ll-CC) |
317
|
|
|
// * - a language with a country wildcard (ll-*) |
318
|
|
|
// * |
319
|
|
|
// * the code with uppercase country is returned. If only the language part matches |
320
|
|
|
// * a configured language, that language is returned. |
321
|
|
|
// * |
322
|
|
|
// * @param string $code the code to match |
323
|
|
|
// * @return array of [language, country] where both can be null if no match |
324
|
|
|
// */ |
325
|
|
|
// protected function matchCode($code) |
326
|
|
|
// { |
327
|
|
|
// $language = $code; |
328
|
|
|
// $country = null; |
329
|
|
|
// $parts = explode('-', $code); |
330
|
|
|
// if (count($parts)===2) { |
331
|
|
|
// $language = $parts[0]; |
332
|
|
|
// $country = strtoupper($parts[1]); |
333
|
|
|
// } |
334
|
|
|
|
335
|
|
|
// if (in_array($code, $this->languages)) { |
|
|
|
|
336
|
|
|
// return [$language, $country]; |
337
|
|
|
// } elseif ( |
338
|
|
|
// $country && in_array("$language-$country", $this->languages) || |
339
|
|
|
// in_array("$language-*", $this->languages) |
340
|
|
|
// ) { |
341
|
|
|
// return [$language, $country]; |
342
|
|
|
// } elseif (in_array($language, $this->languages)) { |
343
|
|
|
// return [$language, null]; |
344
|
|
|
// } else { |
345
|
|
|
// return [null, null]; |
346
|
|
|
// } |
347
|
|
|
// } |
348
|
|
|
|
349
|
|
|
// /** |
350
|
|
|
// * Redirect to the current URL with given language code applied |
351
|
|
|
// * |
352
|
|
|
// * @param string $language the language code to add. Can also be empty to not add any language code. |
353
|
|
|
// */ |
354
|
|
|
// protected function redirectToLanguage($language) |
355
|
|
|
// { |
356
|
|
|
// // Examples: |
357
|
|
|
// // 1) /baseurl/index.php/some/page?q=foo |
358
|
|
|
// // 2) /baseurl/some/page?q=foo |
359
|
|
|
// // 3) |
360
|
|
|
// // 4) /some/page?q=foo |
361
|
|
|
// if ($this->showScriptName) { |
362
|
|
|
// // 1) /baseurl/index.php |
363
|
|
|
// // 2) /baseurl/index.php |
364
|
|
|
// // 3) /index.php |
365
|
|
|
// // 4) /index.php |
366
|
|
|
// $redirectUrl = $this->_request->getScriptUrl(); |
367
|
|
|
// } else { |
368
|
|
|
// // 1) /baseurl |
369
|
|
|
// // 2) /baseurl |
370
|
|
|
// // 3) |
371
|
|
|
// // 4) |
372
|
|
|
// $redirectUrl = $this->_request->getBaseUrl(); |
373
|
|
|
// } |
374
|
|
|
// if ($language) { |
375
|
|
|
// $redirectUrl .= '/'.$language; |
376
|
|
|
// } |
377
|
|
|
// // 1) some/page |
378
|
|
|
// // 2) some/page |
379
|
|
|
// // 3) |
380
|
|
|
// // 4) some/page |
381
|
|
|
// $pathInfo = $this->_request->getPathInfo(); |
382
|
|
|
// if ($pathInfo) { |
383
|
|
|
// $redirectUrl .= '/'.$pathInfo; |
384
|
|
|
// } |
385
|
|
|
// if ($redirectUrl === '') { |
386
|
|
|
// $redirectUrl = '/'; |
387
|
|
|
// } |
388
|
|
|
// // 1) q=foo |
389
|
|
|
// // 2) q=foo |
390
|
|
|
// // 3) |
391
|
|
|
// // 4) q=foo |
392
|
|
|
// $queryString = $this->_request->getQueryString(); |
393
|
|
|
// if ($queryString) { |
394
|
|
|
// $redirectUrl .= '?'.$queryString; |
395
|
|
|
// } |
396
|
|
|
// Yii::$app->getResponse()->redirect($redirectUrl); |
397
|
|
|
// if (YII_ENV_TEST) { |
398
|
|
|
// // Response::redirect($url) above will call `Url::to()` internally. So to really |
399
|
|
|
// // test for the same final redirect URL here, we need to call Url::to(), too. |
400
|
|
|
// throw new \yii\base\Exception(\yii\helpers\Url::to($redirectUrl)); |
401
|
|
|
// } else { |
402
|
|
|
// Yii::$app->end(); |
403
|
|
|
// } |
404
|
|
|
// } |
405
|
|
|
} |
406
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.