These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Yii\I18n; |
||
3 | |||
4 | /** |
||
5 | * Locale stores locale information created from BCP 47 formatted string |
||
6 | * https://tools.ietf.org/html/bcp47 |
||
7 | */ |
||
8 | final class Locale |
||
9 | { |
||
10 | /** |
||
11 | * @var string|null Two-letter ISO-639-2 language code |
||
12 | * @see http://www.loc.gov/standards/iso639-2/ |
||
13 | */ |
||
14 | private $language; |
||
15 | |||
16 | /** |
||
17 | * @var string|null extended language subtags |
||
18 | */ |
||
19 | private $extendedLanguage; |
||
20 | |||
21 | /** |
||
22 | * @var string|null |
||
23 | */ |
||
24 | private $extension; |
||
25 | |||
26 | /** |
||
27 | * @var string|null Four-letter ISO 15924 script code |
||
28 | * @see http://www.unicode.org/iso15924/iso15924-codes.html |
||
29 | */ |
||
30 | private $script; |
||
31 | |||
32 | /** |
||
33 | * @var string|null Two-letter ISO 3166-1 country code |
||
34 | * @see https://www.iso.org/iso-3166-country-codes.html |
||
35 | */ |
||
36 | private $region; |
||
37 | |||
38 | /** |
||
39 | * @var string|null variant of language conventions to use |
||
40 | */ |
||
41 | private $variant; |
||
42 | |||
43 | /** |
||
44 | * @var string|null ICU currency |
||
45 | */ |
||
46 | private $currency; |
||
47 | |||
48 | /** |
||
49 | * @var string|null ICU calendar |
||
50 | */ |
||
51 | private $calendar; |
||
52 | |||
53 | /** |
||
54 | * @var string ICU collation |
||
55 | */ |
||
56 | private $collation; |
||
57 | |||
58 | /** |
||
59 | * @var string|null ICU numbers |
||
60 | */ |
||
61 | private $numbers; |
||
62 | |||
63 | /** |
||
64 | * @var string|null |
||
65 | */ |
||
66 | private $grandfathered; |
||
67 | |||
68 | /** |
||
69 | * @var string|null |
||
70 | */ |
||
71 | private $private; |
||
72 | |||
73 | /** |
||
74 | * Locale constructor. |
||
75 | * @param string $localeString BCP 47 formatted locale string |
||
76 | * @see https://tools.ietf.org/html/bcp47 |
||
77 | * @throws \InvalidArgumentException |
||
78 | */ |
||
79 | public function __construct(string $localeString) |
||
80 | { |
||
81 | if (!preg_match(static::getBCP47Regex(), $localeString, $matches)) { |
||
0 ignored issues
–
show
|
|||
82 | throw new \InvalidArgumentException($localeString . ' is not valid BCP 47 formatted locale string'); |
||
83 | } |
||
84 | |||
85 | if (!empty($matches['language'])) { |
||
86 | $this->language = strtolower($matches['language']); |
||
87 | } |
||
88 | |||
89 | if (!empty($matches['region'])) { |
||
90 | $this->region = strtoupper($matches['region']); |
||
91 | } |
||
92 | |||
93 | if (!empty($matches['variant'])) { |
||
94 | $this->variant = $matches['variant']; |
||
95 | } |
||
96 | |||
97 | if (!empty($matches['extendedLanguage'])) { |
||
98 | $this->extendedLanguage = $matches['extendedLanguage']; |
||
99 | } |
||
100 | |||
101 | if (!empty($matches['extension'])) { |
||
102 | $this->extension = $matches['extension']; |
||
103 | } |
||
104 | |||
105 | if (!empty($matches['script'])) { |
||
106 | $this->script = ucfirst(strtolower($matches['script'])); |
||
107 | } |
||
108 | |||
109 | if (!empty($matches['grandfathered'])) { |
||
110 | $this->grandfathered = $matches['grandfathered']; |
||
111 | } |
||
112 | |||
113 | if (!empty($matches['private'])) { |
||
114 | $this->private = $matches['private']; |
||
115 | } |
||
116 | |||
117 | if (!empty($matches['keywords'])) { |
||
118 | foreach (explode(';', $matches['keywords']) as $pair) { |
||
119 | [$key, $value] = explode('=', $pair); |
||
0 ignored issues
–
show
|
|||
120 | |||
121 | if ($key === 'calendar') { |
||
122 | $this->calendar = $value; |
||
123 | } |
||
124 | |||
125 | if ($key === 'collation') { |
||
126 | $this->collation = $value; |
||
127 | } |
||
128 | |||
129 | if ($key === 'currency') { |
||
130 | $this->currency = $value; |
||
131 | } |
||
132 | |||
133 | if ($key === 'numbers') { |
||
134 | $this->numbers = $value; |
||
135 | } |
||
136 | } |
||
137 | } |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Returns Locale. |
||
142 | * @param Locale|string $locale |
||
143 | * @return self |
||
144 | * @throws \InvalidArgumentException |
||
145 | */ |
||
146 | public static function create($locale): self |
||
147 | { |
||
148 | if ($locale instanceof self) { |
||
149 | return $locale; |
||
150 | } |
||
151 | |||
152 | return new self((string)$locale); |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * @return string Four-letter ISO 15924 script code |
||
157 | * @see http://www.unicode.org/iso15924/iso15924-codes.html |
||
158 | */ |
||
159 | public function getScript(): ?string |
||
160 | { |
||
161 | return $this->script; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * @param null|string $script Four-letter ISO 15924 script code |
||
166 | * @see http://www.unicode.org/iso15924/iso15924-codes.html |
||
167 | * @return self |
||
168 | */ |
||
169 | public function withScript(?string $script): self |
||
170 | { |
||
171 | $clone = clone $this; |
||
172 | $clone->script = $script; |
||
173 | return $clone; |
||
174 | } |
||
175 | |||
176 | |||
177 | /** |
||
178 | * @return string variant of language conventions to use |
||
179 | */ |
||
180 | public function getVariant(): ?string |
||
181 | { |
||
182 | return $this->variant; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * @param null|string $variant variant of language conventions to use |
||
187 | * @return self |
||
188 | */ |
||
189 | public function withVariant(?string $variant): self |
||
190 | { |
||
191 | $clone = clone $this; |
||
192 | $clone->variant = $variant; |
||
193 | return $clone; |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * @return string|null Two-letter ISO-639-2 language code |
||
198 | * @see http://www.loc.gov/standards/iso639-2/ |
||
199 | */ |
||
200 | public function getLanguage(): string |
||
201 | { |
||
202 | return $this->language; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * @param null|string $language Two-letter ISO-639-2 language code |
||
207 | * @see http://www.loc.gov/standards/iso639-2/ |
||
208 | * @return self |
||
209 | */ |
||
210 | public function withLanguage(?string $language): self |
||
211 | { |
||
212 | $clone = clone $this; |
||
213 | $clone->language = $language; |
||
214 | return $clone; |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * @return null|string ICU calendar |
||
219 | */ |
||
220 | public function getCalendar(): ?string |
||
221 | { |
||
222 | return $this->calendar; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * @param null|string $calendar ICU calendar |
||
227 | * @return self |
||
228 | */ |
||
229 | public function withCalendar(?string $calendar): self |
||
230 | { |
||
231 | $clone = clone $this; |
||
232 | $clone->calendar = $calendar; |
||
233 | return $clone; |
||
234 | } |
||
235 | |||
236 | |||
237 | /** |
||
238 | * @return null|string ICU collation |
||
239 | */ |
||
240 | public function getCollation(): ?string |
||
241 | { |
||
242 | return $this->collation; |
||
243 | } |
||
244 | |||
245 | /** |
||
246 | * @param null|string $collation ICU collation |
||
247 | * @return self |
||
248 | */ |
||
249 | public function withCollation(?string $collation): self |
||
250 | { |
||
251 | $clone = clone $this; |
||
252 | $clone->collation = $collation; |
||
253 | return $clone; |
||
254 | } |
||
255 | |||
256 | /** |
||
257 | * @return null|string ICU numbers |
||
258 | */ |
||
259 | public function getNumbers(): ?string |
||
260 | { |
||
261 | return $this->numbers; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @param null|string $numbers ICU numbers |
||
266 | * @return self |
||
267 | */ |
||
268 | public function withNumbers(?string $numbers): self |
||
269 | { |
||
270 | $clone = clone $this; |
||
271 | $clone->numbers = $numbers; |
||
272 | return $clone; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * @return string Two-letter ISO 3166-1 country code |
||
277 | * @see https://www.iso.org/iso-3166-country-codes.html |
||
278 | */ |
||
279 | public function getRegion(): ?string |
||
280 | { |
||
281 | return $this->region; |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * @param null|string $region Two-letter ISO 3166-1 country code |
||
286 | * @see https://www.iso.org/iso-3166-country-codes.html |
||
287 | * @return self |
||
288 | */ |
||
289 | public function withRegion(?string $region): self |
||
290 | { |
||
291 | $clone = clone $this; |
||
292 | $clone->region = $region; |
||
293 | return $clone; |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * @return string ICU currency |
||
298 | */ |
||
299 | public function getCurrency(): ?string |
||
300 | { |
||
301 | return $this->currency; |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * @param null|string $currency ICU currency |
||
306 | * @return self |
||
307 | */ |
||
308 | public function withCurrency(?string $currency): self |
||
309 | { |
||
310 | $clone = clone $this; |
||
311 | $clone->currency = $currency; |
||
312 | |||
313 | return $clone; |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * @return null|string extended language subtags |
||
318 | */ |
||
319 | public function getExtendedLanguage(): ?string |
||
320 | { |
||
321 | return $this->extendedLanguage; |
||
322 | } |
||
323 | |||
324 | /** |
||
325 | * @param null|string $extendedLanguage extended language subtags |
||
326 | * @return self |
||
327 | */ |
||
328 | public function withExtendedLanguage(?string $extendedLanguage): self |
||
329 | { |
||
330 | $clone = clone $this; |
||
331 | $clone->extendedLanguage = $extendedLanguage; |
||
332 | |||
333 | return $clone; |
||
334 | } |
||
335 | |||
336 | |||
337 | /** |
||
338 | * @return null|string |
||
339 | */ |
||
340 | public function getPrivate(): ?string |
||
341 | { |
||
342 | return $this->private; |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * @param null|string $private |
||
347 | * @return self |
||
348 | */ |
||
349 | public function withPrivate(?string $private): self |
||
350 | { |
||
351 | $clone = clone $this; |
||
352 | $clone->private = $private; |
||
353 | |||
354 | return $clone; |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * @return string regular expression for parsing BCP 47 |
||
359 | * @see https://tools.ietf.org/html/bcp47 |
||
360 | */ |
||
361 | private static function getBCP47Regex(): string |
||
362 | { |
||
363 | $regular = '(?:art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)'; |
||
364 | $irregular = '(?:en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)'; |
||
365 | $grandfathered = '(?<grandfathered>' . $irregular . '|' . $regular . ')'; |
||
366 | $private = '(?<private>x(?:-[A-Za-z0-9]{1,8})+)'; |
||
367 | $singleton = '[0-9A-WY-Za-wy-z]'; |
||
368 | $extension = '(?<extension>' . $singleton . '(?:-[A-Za-z0-9]{2,8})+)'; |
||
369 | $variant = '(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3})'; |
||
370 | $region = '(?<region>[A-Za-z]{2}|[0-9]{3})'; |
||
371 | $script = '(?<script>[A-Za-z]{4})'; |
||
372 | $extendedLanguage = '(?<extendedLanguage>[A-Za-z]{3}(?:-[A-Za-z]{3}){0,2})'; |
||
373 | $language = '(?:(?<language>[A-Za-z]{4,8})|(?<language>[A-Za-z]{2,3})(?:-' . $extendedLanguage . ')?)'; |
||
374 | $icuKeywords = '(?:@(?<keywords>.*?))?'; |
||
375 | $languageTag = '(?:' . $language . '(?:-' . $script . ')?' . '(?:-' . $region . ')?' . '(?:-' . $variant . ')*' . '(?:-' . $extension . ')*' . '(?:-' . $private . ')?' . ')'; |
||
376 | return '/^(?J:' . $grandfathered . '|' . $languageTag . '|' . $private . ')' . $icuKeywords . '$/'; |
||
377 | } |
||
378 | |||
379 | public function __toString(): string |
||
380 | { |
||
381 | return $this->asString(); |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * @return string |
||
386 | */ |
||
387 | public function asString(): string |
||
388 | { |
||
389 | if ($this->grandfathered !== null) { |
||
390 | return $this->grandfathered; |
||
391 | } |
||
392 | |||
393 | $result = []; |
||
394 | if ($this->language !== null) { |
||
395 | $result[] = $this->language; |
||
396 | |||
397 | if ($this->extendedLanguage !== null) { |
||
398 | $result[] = $this->extendedLanguage; |
||
399 | } |
||
400 | |||
401 | if ($this->script !== null) { |
||
402 | $result[] = $this->script; |
||
403 | } |
||
404 | |||
405 | if ($this->region !== null) { |
||
406 | $result[] = $this->region; |
||
407 | } |
||
408 | |||
409 | if ($this->variant !== null) { |
||
410 | $result[] = $this->variant; |
||
411 | } |
||
412 | |||
413 | if ($this->extension !== null) { |
||
414 | $result[] = $this->extension; |
||
415 | } |
||
416 | } |
||
417 | |||
418 | if ($this->private !== null) { |
||
419 | $result[] = $this->private; |
||
420 | } |
||
421 | |||
422 | $keywords = []; |
||
423 | if ($this->currency !== null) { |
||
424 | $keywords[] = 'currency=' . $this->currency; |
||
425 | } |
||
426 | if ($this->collation !== null) { |
||
427 | $keywords[] = 'collation=' . $this->collation; |
||
428 | } |
||
429 | if ($this->calendar !== null) { |
||
430 | $keywords[] = 'calendar=' . $this->calendar; |
||
431 | } |
||
432 | if ($this->numbers !== null) { |
||
433 | $keywords[] = 'numbers=' . $this->numbers; |
||
434 | } |
||
435 | |||
436 | $string = implode('-', $result); |
||
437 | |||
438 | if ($keywords !== []) { |
||
439 | $string .= '@' . implode(';', $keywords); |
||
440 | } |
||
441 | |||
442 | return $string; |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Returns fallback locale |
||
447 | * |
||
448 | * @return self fallback locale |
||
449 | */ |
||
450 | public function getFallbackLocale(): self |
||
451 | { |
||
452 | if ($this->variant !== null) { |
||
453 | return $this->withVariant(null); |
||
454 | } |
||
455 | |||
456 | if ($this->region !== null) { |
||
457 | return $this->withRegion(null); |
||
458 | } |
||
459 | |||
460 | if ($this->script !== null) { |
||
461 | return $this->withScript(null); |
||
462 | } |
||
463 | |||
464 | return $this; |
||
465 | } |
||
466 | } |
||
467 |
Late static binding only has effect in subclasses. A
final
class cannot be extended anymore so late static binding cannot occurr. Consider replacingstatic::
withself::
.To learn more about late static binding, please refer to the PHP core documentation.