1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the Zemit Framework. |
5
|
|
|
* |
6
|
|
|
* (c) Zemit Team <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE.txt |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Zemit; |
13
|
|
|
|
14
|
|
|
use Zemit\Di\Injectable; |
15
|
|
|
use Zemit\Support\Options\Options; |
16
|
|
|
use Zemit\Support\Options\OptionsInterface; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Allow to manage and lookup the locale for the localisation |
20
|
|
|
*/ |
21
|
|
|
class Locale extends Injectable implements OptionsInterface |
22
|
|
|
{ |
23
|
|
|
use Options; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Router |
27
|
|
|
*/ |
28
|
|
|
public const MODE_ROUTE = 'route'; |
29
|
|
|
public const MODE_DEFAULT = 'default'; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Router -> http |
33
|
|
|
*/ |
34
|
|
|
public const MODE_HTTP = 'http'; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Router -> session -> http |
38
|
|
|
*/ |
39
|
|
|
public const MODE_SESSION = 'session'; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Router -> geoip -> http |
43
|
|
|
*/ |
44
|
|
|
public const MODE_GEOIP = 'geoip'; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Router -> session -> geoip -> http |
48
|
|
|
*/ |
49
|
|
|
public const MODE_SESSION_GEOIP = 'session_geoip'; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Locale mode |
53
|
|
|
* Locale::MODE_DEFAULT 'default' (Router -> http) |
54
|
|
|
* Locale::MODE_SESSION 'session' (Router -> session -> http) |
55
|
|
|
* Locale::MODE_GEOIP 'geoip' (Router -> geoip -> http) |
56
|
|
|
* Locale::MODE_SESSION_GEOIP 'session_geoip' (Router -> session -> geoip -> http) |
57
|
|
|
*/ |
58
|
|
|
public string $mode = self::MODE_DEFAULT; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* The actual locale that was picked |
62
|
|
|
* @var string|null |
63
|
|
|
*/ |
64
|
|
|
public $locale = null; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var mixed|null|string |
68
|
|
|
*/ |
69
|
|
|
public string $sessionKey = 'zemit-locale'; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Default locale to fall back |
73
|
|
|
*/ |
74
|
|
|
public string $default = 'en'; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* List of allowed locale |
78
|
|
|
*/ |
79
|
|
|
public array $allowed = ['en']; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Set options and prepare locale |
83
|
|
|
*/ |
84
|
|
|
public function initialize(): void |
85
|
|
|
{ |
86
|
|
|
$this->sessionKey = $this->getOption('sessionKey', $this->sessionKey); |
87
|
|
|
$this->setAllowed($this->getOption('allowed', $this->allowed)); |
88
|
|
|
$this->setDefault($this->getOption('default', $this->default)); |
89
|
|
|
$this->setMode($this->getOption('mode', $this->mode)); |
90
|
|
|
$this->prepare($this->getDefault()); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Alias of the getLocale() method |
95
|
|
|
*/ |
96
|
|
|
public function get(): ?string |
97
|
|
|
{ |
98
|
|
|
return $this->getLocale(); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Get the locale directly from the variable |
103
|
|
|
* without processing the defined mode |
104
|
|
|
*/ |
105
|
|
|
public function getLocale(): ?string |
106
|
|
|
{ |
107
|
|
|
return $this->locale; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Set the current locale value |
112
|
|
|
*/ |
113
|
|
|
public function setLocale(?string $locale = null): void |
114
|
|
|
{ |
115
|
|
|
$this->locale = $this->lookup($locale); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Get the default locale |
120
|
|
|
*/ |
121
|
|
|
public function getDefault(): ?string |
122
|
|
|
{ |
123
|
|
|
return $this->default; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Set the default locale value |
128
|
|
|
*/ |
129
|
|
|
public function setDefault(?string $locale = null): void |
130
|
|
|
{ |
131
|
|
|
$this->default = $locale; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Get the list of possible locale |
136
|
|
|
*/ |
137
|
|
|
public function getAllowed(): array |
138
|
|
|
{ |
139
|
|
|
return $this->allowed ?? []; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Set the allowed locale |
144
|
|
|
*/ |
145
|
|
|
public function setAllowed(array $allowed): void |
146
|
|
|
{ |
147
|
|
|
$this->allowed = array_values(array_unique($allowed)); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Get the defined mode |
152
|
|
|
*/ |
153
|
|
|
public function getMode(): string |
154
|
|
|
{ |
155
|
|
|
return $this->mode; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Set the mode |
160
|
|
|
*/ |
161
|
|
|
public function setMode(string $mode): void |
162
|
|
|
{ |
163
|
|
|
$this->mode = $mode; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Prepare and set and return the locale based on the defined mode |
168
|
|
|
*/ |
169
|
|
|
public function prepare(?string $default = null): ?string |
170
|
|
|
{ |
171
|
|
|
switch ($this->mode) { |
172
|
|
|
case self::MODE_SESSION: |
173
|
|
|
$locale = |
174
|
|
|
$this->getFromRoute() ?? |
175
|
|
|
$this->getFromSession() ?? |
176
|
|
|
$this->getFromHttp() ?? |
177
|
|
|
$default; |
178
|
|
|
break; |
179
|
|
|
|
180
|
|
|
case self::MODE_GEOIP: |
181
|
|
|
$locale = |
182
|
|
|
$this->getFromRoute() ?? |
183
|
|
|
$this->getFromGeoIP() ?? |
|
|
|
|
184
|
|
|
$this->getFromHttp() ?? |
185
|
|
|
$default; |
186
|
|
|
break; |
187
|
|
|
|
188
|
|
|
case self::MODE_SESSION_GEOIP: |
189
|
|
|
$locale = |
190
|
|
|
$this->getFromRoute() ?? |
191
|
|
|
$this->getFromSession() ?? |
192
|
|
|
$this->getFromGeoIP() ?? |
|
|
|
|
193
|
|
|
$this->getFromHttp() ?? |
194
|
|
|
$default; |
195
|
|
|
break; |
196
|
|
|
|
197
|
|
|
case self::MODE_HTTP: |
198
|
|
|
$locale = |
199
|
|
|
$this->getFromRoute() ?? |
200
|
|
|
$this->getFromHttp() ?? |
201
|
|
|
$default; |
202
|
|
|
break; |
203
|
|
|
|
204
|
|
|
case self::MODE_DEFAULT: |
205
|
|
|
case self::MODE_ROUTE: |
206
|
|
|
default: |
207
|
|
|
$locale = |
208
|
|
|
$this->getFromRoute() ?? |
209
|
|
|
$default; |
210
|
|
|
break; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
$locale ??= $this->locale; |
214
|
|
|
$this->setLocale($locale); |
215
|
|
|
$this->saveIntoSession($locale); |
216
|
|
|
return $this->getLocale(); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Retrieves the locale from the route |
221
|
|
|
*/ |
222
|
|
|
public function getFromRoute(?string $default = null): ?string |
223
|
|
|
{ |
224
|
|
|
return $this->lookup($this->router->getParams()['locale'] ?? $default); |
|
|
|
|
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Retrieves the locale from the dispatcher |
229
|
|
|
*/ |
230
|
|
|
public function getFromDispatcher(?string $default = null): ?string |
231
|
|
|
{ |
232
|
|
|
return $this->lookup($this->router->getParams()['locale'] ?? $default); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Retrieves the locale from the session |
237
|
|
|
*/ |
238
|
|
|
public function getFromSession(?string $default = null): ?string |
239
|
|
|
{ |
240
|
|
|
return $this->lookup($this->session->get($this->sessionKey, $default)); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Retrieves the locale from the geolocation |
245
|
|
|
* @todo not ready yet |
246
|
|
|
*/ |
247
|
|
|
public function getFromGeoIP(?string $default = null): ?string |
248
|
|
|
{ |
249
|
|
|
return $this->lookup($default); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* Retrieves the locale from the request |
254
|
|
|
* of getBestLanguage() header |
255
|
|
|
* or HTTP_ACCEPT_LANGUAGE header |
256
|
|
|
*/ |
257
|
|
|
public function getFromHttp(?String $default = null): ?string |
258
|
|
|
{ |
259
|
|
|
return |
260
|
|
|
$this->lookup($this->request->getBestLanguage()) ?? |
261
|
|
|
\Locale::acceptFromHttp($this->request->getHeader('HTTP_ACCEPT_LANGUAGE')) ?? |
262
|
|
|
$default; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* Save locale into session if mode contain session handling |
267
|
|
|
*/ |
268
|
|
|
public function saveIntoSession(?string $locale = null, bool $force = null): void |
269
|
|
|
{ |
270
|
|
|
$locale ??= $this->getLocale(); |
271
|
|
|
|
272
|
|
|
// save into session |
273
|
|
|
$force = $force || $this->mode === self::MODE_SESSION || $this->mode === self::MODE_SESSION_GEOIP; |
274
|
|
|
if ($force) { |
275
|
|
|
$this->session->set($this->sessionKey, $locale); |
276
|
|
|
} |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* @param string|null $locale The locale to use as the language range when matching. |
281
|
|
|
* @param array|null $allowed An array containing a list of language tags to compare to locale. Maximum 100 items allowed. |
282
|
|
|
* @param bool $canonicalize If true, the arguments will be converted to canonical form before matching. |
283
|
|
|
* @param string|null $default The locale to use if no match is found. |
284
|
|
|
* @return string|null The closest matching language tag or default value. |
285
|
|
|
*/ |
286
|
|
|
public function lookup(?string $locale = null, ?array $allowed = null, bool $canonicalize = false, ?string $default = null): ?string |
287
|
|
|
{ |
288
|
|
|
if (is_null($locale)) { |
289
|
|
|
return null; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
$allowed ??= $this->getAllowed(); |
293
|
|
|
|
294
|
|
|
// lookup first |
295
|
|
|
$lookup = \Locale::lookup($allowed, $locale, $canonicalize, $default); |
296
|
|
|
|
297
|
|
|
// base locale found without the region |
298
|
|
|
$force = false; |
299
|
|
|
if ($locale === $lookup || strlen($lookup) === 2) { |
300
|
|
|
$locale = $lookup; |
301
|
|
|
$force = true; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
// lookup for the first configured region based on the locale without region |
305
|
|
|
if (empty($lookup) || $force) { |
306
|
|
|
|
307
|
|
|
// matches all the possible regions from the locale |
308
|
|
|
$matches = array_filter($allowed, function ($haystack) use ($locale) { |
309
|
|
|
$needle = $locale . '_'; |
310
|
|
|
return stripos($haystack, $needle) === 0; |
311
|
|
|
}); |
312
|
|
|
|
313
|
|
|
// some matches |
314
|
|
|
if (count($matches)) { |
315
|
|
|
// lookup again with the first match |
316
|
|
|
$lookup = \Locale::lookup($matches, array_shift($matches), $canonicalize, $default); |
317
|
|
|
} |
318
|
|
|
else { |
319
|
|
|
// otherwise keep the lookup if set or set the default if not |
320
|
|
|
$lookup = empty($lookup) ? $default : $lookup; |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
return empty($lookup) ? null : $lookup; |
325
|
|
|
} |
326
|
|
|
} |
327
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.