Completed
Push — hash-nonce ( 07e2e8 )
by Sam
08:52
created

i18n::register_translator()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 3
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
require_once 'Zend/Translate.php';
3
require_once 'i18nRailsYamlAdapter.php';
4
require_once 'i18nSSLegacyAdapter.php';
5
6
/**
7
 * Base-class for storage and retrieval of translated entities.
8
 *
9
 * Please see the 'translatable' module for managing translations of database-content.
10
 *
11
 * <b>Usage</b>
12
 *
13
 * PHP:
14
 * <code>
15
 * _t('MyNamespace.MYENTITY', 'My default natural language value');
16
 * _t('MyNamespace.MYENTITY', 'My default natural language value', 'My explanatory context');
17
 * sprintf(_t('MyNamespace.MYENTITY', 'Counting %s things'), 42);
18
 * </code>
19
 *
20
 * Templates:
21
 * <code>
22
 * <%t MyNamespace.MYENTITY 'My default natural language value' %>
23
 * <%t MyNamespace.MYENTITY 'Counting %s things' s=$ThingsCount %>
24
 * <%t MyNamespace.MYENTITY 'Counting {count} things' count=$ThingsCount %>
25
 * </code>
26
 *
27
 * Javascript (see framework/javascript/i18n.js):
28
 * <code>
29
 * ss.i18n._t('MyEntity.MyNamespace','My default natural language value');
30
 * </code>
31
 *
32
 * File-based i18n-translations always have a "locale" (e.g. 'en_US').
33
 * Common language names (e.g. 'en') are mainly used in the 'translatable' module
34
 * database-entities.
35
 *
36
 * <b>Text Collection</b>
37
 *
38
 * Features a "textcollector-mode" that parses all files with a certain extension
39
 * (currently *.php and *.ss) for new translatable strings. Textcollector will write
40
 * updated string-tables to their respective folders inside the module, and automatically
41
 * namespace entities to the classes/templates they are found in (e.g. $lang['en_US']['AssetAdmin']['UPLOADFILES']).
42
 *
43
 * Caution: Does not apply any character-set conversion, it is assumed that all content
44
 * is stored and represented in UTF-8 (Unicode). Please make sure your files are created with the correct
45
 * character-set, and your HTML-templates render UTF-8.
46
 *
47
 * Caution: The language file has to be stored in the same module path as the "filename namespaces"
48
 * on the entities. So an entity stored in $lang['en_US']['AssetAdmin']['DETAILSTAB'] has to
49
 * in the language file cms/lang/en_US.php, as the referenced file (AssetAdmin.php) is stored
50
 * in the "cms" module.
51
 *
52
 * <b>Locales</b>
53
 *
54
 * For the i18n class, a "locale" consists of a language code plus a region code separated by an underscore,
55
 * for example "de_AT" for German language ("de") in the region Austria ("AT").
56
 * See http://www.w3.org/International/articles/language-tags/ for a detailed description.
57
 *
58
 * @see http://doc.silverstripe.org/i18n
59
 * @see http://www.w3.org/TR/i18n-html-tech-lang
60
 *
61
 * @author Bernat Foj Capell <[email protected]>
62
 * @package framework
63
 * @subpackage misc
64
 */
65
class i18n extends Object implements TemplateGlobalProvider, Flushable {
66
67
	/**
68
	 * This static variable is used to store the current defined locale.
69
	 */
70
	protected static $current_locale = '';
71
72
	/**
73
	 * @config
74
	 * @var string
75
	 */
76
	private static $default_locale = 'en_US';
77
78
	/**
79
	 * @config
80
	 * @var boolean
81
	 */
82
	private static $js_i18n = true;
83
84
	/**
85
	 * @config
86
	 * @var string
87
	 */
88
	private static $date_format = 'yyyy-MM-dd';
89
90
	/**
91
	 * @config
92
	 * @var string
93
	 */
94
	private static $time_format = 'H:mm';
95
96
	/**
97
	 * @var array Array of priority keys to instances of Zend_Translate, mapped by name.
98
	 */
99
	protected static $translators;
100
101
	/**
102
	 * Triggered early in the request when someone requests a flush.
103
	 */
104
	public static function flush() {
105
		$cache = self::get_cache();
106
		$backend = $cache->getBackend();
107
108 View Code Duplication
		if(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
109
			$backend instanceof Zend_Cache_Backend_ExtendedInterface
0 ignored issues
show
Bug introduced by
The class Zend_Cache_Backend_ExtendedInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
110
			&& ($capabilities = $backend->getCapabilities())
111
			&& $capabilities['tags']
112
		) {
113
			$cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, $cache->getTags());
114
		} else {
115
			$cache->clean(Zend_Cache::CLEANING_MODE_ALL);
116
		}
117
	}
118
119
	/**
120
	 * Return an instance of the cache used for i18n data.
121
	 * @return Zend_Cache
122
	 */
123
	public static function get_cache() {
124
		return SS_Cache::factory('i18n', 'Output', array('lifetime' => null, 'automatic_serialization' => true));
125
	}
126
127
	/**
128
	 * Use javascript i18n through the ss.i18n class (enabled by default).
129
	 * If set to TRUE, includes javascript requirements for the base library
130
	 * (framework/javascript/i18n.js) and all necessary lang files (e.g. framework/lang/de_DE.js)
131
	 * plus fallbacks to the default locale (e.g. framework/lang/en_US.js).
132
	 * If set to FALSE, only includes a stub implementation
133
	 * which is necessary. Mainly disabled to save bandwidth
134
	 * in a frontend context when website is in single language.
135
	 *
136
	 * Caution: This flag gets overwritten in {@link LeftAndMain::init()} to enforce javascript
137
	 * i18n for the CMS interfaces.
138
	 *
139
	 * @see Requirements::process_i18n_javascript()
140
	 *
141
	 * @deprecated 4.0 Use the "i18n.js_i18n" config setting instead
142
	 * @param bool $bool
143
	 */
144
	public static function set_js_i18n($bool) {
145
		Deprecation::notice('4.0', 'Use the "i18n.js_i18n" config setting instead');
146
		Config::inst()->update('i18n', 'js_i18n', $bool);
147
	}
148
149
	/**
150
	 * @deprecated 4.0 Use the "i18n.js_i18n" config setting instead
151
	 * @return bool
152
	 */
153
	public static function get_js_i18n() {
154
		Deprecation::notice('4.0', 'Use the "i18n.js_i18n" config setting instead');
155
		return Config::inst()->get('i18n', 'js_i18n');
156
	}
157
158
	/**
159
	 * @deprecated 4.0 Use the "i18n.date_format" config setting instead
160
	 * @param string ISO date format
161
	 */
162
	public static function set_date_format($format) {
163
		Deprecation::notice('4.0', 'Use the "i18n.date_format" config setting instead');
164
		Config::inst()->update('i18n', 'date_format', $format);
165
	}
166
167
	/**
168
	 * @deprecated since version 4.0
169
	 * @return string ISO date format
170
	 */
171
	public static function get_date_format() {
172
		Deprecation::notice('4.0', 'Use the "i18n.date_format" config setting instead');
173
		return Config::inst()->get('i18n', 'date_format');
174
	}
175
176
	/**
177
	 * @deprecated 4.0 Use the "i18n.time_format" config setting instead
178
	 * @param string ISO time format
179
	 */
180
	public static function set_time_format($format) {
181
		Deprecation::notice('4.0', 'Use the "i18n.time_format" config setting instead');
182
		Config::inst()->update('i18n', 'time_format', $format);
183
	}
184
185
	/**
186
	 * @deprecated since version 4.0
187
	 * @return string ISO time format
188
	 */
189
	public static function get_time_format() {
190
		Deprecation::notice('4.0', 'Use the "i18n.time_format" config setting instead');
191
		return Config::inst()->get('i18n', 'time_format');
192
	}
193
194
	/**
195
	 * An exhaustive list of possible locales (code => language and country)
196
	 *
197
	 * @config
198
	 * @var array
199
	 */
200
	private static $all_locales = array (
201
		'aa_DJ' => 'Afar (Djibouti)',
202
		'ab_GE' => 'Abkhazian (Georgia)',
203
		'abr_GH' => 'Abron (Ghana)',
204
		'ace_ID' => 'Achinese (Indonesia)',
205
		'ady_RU' => 'Adyghe (Russia)',
206
		'af_ZA' => 'Afrikaans (South Africa)',
207
		'ak_GH' => 'Akan (Ghana)',
208
		'am_ET' => 'Amharic (Ethiopia)',
209
		'ar_AE' => 'Arabic (United Arab Emirates)',
210
		'ar_BH' => 'Arabic (Bahrain)',
211
		'ar_DZ' => 'Arabic (Algeria)',
212
		'ar_EG' => 'Arabic (Egypt)',
213
		'ar_EH' => 'Arabic (Western Sahara)',
214
		'ar_IQ' => 'Arabic (Iraq)',
215
		'ar_JO' => 'Arabic (Jordan)',
216
		'ar_KW' => 'Arabic (Kuwait)',
217
		'ar_LB' => 'Arabic (Lebanon)',
218
		'ar_LY' => 'Arabic (Libya)',
219
		'ar_MA' => 'Arabic (Morocco)',
220
		'ar_MR' => 'Arabic (Mauritania)',
221
		'ar_OM' => 'Arabic (Oman)',
222
		'ar_PS' => 'Arabic (Palestinian Territory)',
223
		'ar_QA' => 'Arabic (Qatar)',
224
		'ar_SA' => 'Arabic (Saudi Arabia)',
225
		'ar_SD' => 'Arabic (Sudan)',
226
		'ar_SY' => 'Arabic (Syria)',
227
		'ar_TD' => 'Arabic (Chad)',
228
		'ar_TN' => 'Arabic (Tunisia)',
229
		'ar_YE' => 'Arabic (Yemen)',
230
		'as_IN' => 'Assamese (India)',
231
		'ast_ES' => 'Asturian (Spain)',
232
		'auv_FR' => 'Auvergnat (France)',
233
		'av_RU' => 'Avaric (Russia)',
234
		'awa_IN' => 'Awadhi (India)',
235
		'ay_BO' => 'Aymara (Bolivia)',
236
		'ay_PE' => 'Aymara (Peru)',
237
		'az_AZ' => 'Azerbaijani (Azerbaijan)',
238
		'az_IR' => 'Azerbaijani (Iran)',
239
		'ba_RU' => 'Bashkir (Russia)',
240
		'ban_ID' => 'Balinese (Indonesia)',
241
		'bcc_PK' => 'Balochi, Southern (Pakistan)',
242
		'bcl_PH' => 'Bicolano, Central (Philippines)',
243
		'be_BY' => 'Belarusian (Belarus)',
244
		'bew_ID' => 'Betawi (Indonesia)',
245
		'bg_BG' => 'Bulgarian (Bulgaria)',
246
		'bgc_IN' => 'Haryanvi (India)',
247
		'bgn_PK' => 'Balochi, Western (Pakistan)',
248
		'bgp_PK' => 'Balochi, Easter (Pakistan)',
249
		'bhb_IN' => 'Bhili (India)',
250
		'bhi_IN' => 'Bhilali (India)',
251
		'bhk_PH' => 'Bicolano, Albay (Philippines)',
252
		'bho_IN' => 'Bhojpuri (India)',
253
		'bho_MU' => 'Bhojpuri (Mauritius)',
254
		'bho_NP' => 'Bhojpuri (Nepal)',
255
		'bi_VU' => 'Bislama (Vanuatu)',
256
		'bjj_IN' => 'Kanauji (India)',
257
		'bjn_ID' => 'Banjar (Indonesia)',
258
		'bm_ML' => 'Bambara (Mali)',
259
		'bn_BD' => 'Bengali (Bangladesh)',
260
		'bn_IN' => 'Bengali (India)',
261
		'bo_CN' => 'Tibetan (China)',
262
		'bqi_IR' => 'Bakhtiari (Iran)',
263
		'brh_PK' => 'Brahui (Pakistan)',
264
		'bs_BA' => 'Bosnian (Bosnia and Herzegovina)',
265
		'btk_ID' => 'Batak (Indonesia)',
266
		'buc_YT' => 'Bushi (Mayotte)',
267
		'bug_ID' => 'Buginese (Indonesia)',
268
		'ca_AD' => 'Catalan (Andorra)',
269
		'ca_ES' => 'Catalan (Spain)',
270
		'ce_RU' => 'Chechen (Russia)',
271
		'ceb_PH' => 'Cebuano (Philippines)',
272
		'cgg_UG' => 'Chiga (Uganda)',
273
		'ch_GU' => 'Chamorro (Guam)',
274
		'chk_FM' => 'Chuukese (Micronesia)',
275
		'crk_CA' => 'Cree, Plains (Canada)',
276
		'cs_CZ' => 'Czech (Czech Republic)',
277
		'cwd_CA' => 'Cree, Woods (Canada)',
278
		'cy_GB' => 'Welsh (United Kingdom)',
279
		'da_DK' => 'Danish (Denmark)',
280
		'da_GL' => 'Danish (Greenland)',
281
		'dcc_IN' => 'Deccan (India)',
282
		'de_AT' => 'German (Austria)',
283
		'de_BE' => 'German (Belgium)',
284
		'de_CH' => 'German (Switzerland)',
285
		'de_DE' => 'German (Germany)',
286
		'de_LI' => 'German (Liechtenstein)',
287
		'de_LU' => 'German (Luxembourg)',
288
		'dgo_IN' => 'Dogri (India)',
289
		'dhd_IN' => 'Dhundari (India)',
290
		'diq_TR' => 'Dimli (Turkey)',
291
		'dje_NE' => 'Zarma (Niger)',
292
		'dv_MV' => 'Divehi (Maldives)',
293
		'dz_BT' => 'Dzongkha (Bhutan)',
294
		'ee_GH' => 'Ewe (Ghana)',
295
		'el_CY' => 'Greek (Cyprus)',
296
		'el_GR' => 'Greek (Greece)',
297
		'en_AS' => 'English (American Samoa)',
298
		'en_AU' => 'English (Australia)',
299
		'en_BM' => 'English (Bermuda)',
300
		'en_BS' => 'English (Bahamas)',
301
		'en_CA' => 'English (Canada)',
302
		'en_DE' => 'English (Germany)',
303
		'en_ES' => 'English (Spain)',
304
		'en_FR' => 'English (France)',
305
		'en_GB' => 'English (United Kingdom)',
306
		'en_HK' => 'English (Hong Kong SAR China)',
307
		'en_IE' => 'English (Ireland)',
308
		'en_IN' => 'English (India)',
309
		'en_IT' => 'English (Italy)',
310
		'en_JM' => 'English (Jamaica)',
311
		'en_KE' => 'English (Kenya)',
312
		'en_LR' => 'English (Liberia)',
313
		'en_MM' => 'English (Myanmar)',
314
		'en_MW' => 'English (Malawi)',
315
		'en_MY' => 'English (Malaysia)',
316
		'en_NL' => 'English (Netherlands)',
317
		'en_NZ' => 'English (New Zealand)',
318
		'en_PH' => 'English (Philippines)',
319
		'en_SG' => 'English (Singapore)',
320
		'en_TT' => 'English (Trinidad and Tobago)',
321
		'en_US' => 'English (United States)',
322
		'en_ZA' => 'English (South Africa)',
323
		'eo_XX' => 'Esperanto',
324
		'es_419' => 'Spanish (Latin America)',
325
		'es_AR' => 'Spanish (Argentina)',
326
		'es_BO' => 'Spanish (Bolivia)',
327
		'es_CL' => 'Spanish (Chile)',
328
		'es_CO' => 'Spanish (Colombia)',
329
		'es_CR' => 'Spanish (Costa Rica)',
330
		'es_CU' => 'Spanish (Cuba)',
331
		'es_DO' => 'Spanish (Dominican Republic)',
332
		'es_EC' => 'Spanish (Ecuador)',
333
		'es_ES' => 'Spanish (Spain)',
334
		'es_GQ' => 'Spanish (Equatorial Guinea)',
335
		'es_GT' => 'Spanish (Guatemala)',
336
		'es_HN' => 'Spanish (Honduras)',
337
		'es_MX' => 'Spanish (Mexico)',
338
		'es_NI' => 'Spanish (Nicaragua)',
339
		'es_PA' => 'Spanish (Panama)',
340
		'es_PE' => 'Spanish (Peru)',
341
		'es_PH' => 'Spanish (Philippines)',
342
		'es_PR' => 'Spanish (Puerto Rico)',
343
		'es_PY' => 'Spanish (Paraguay)',
344
		'es_SV' => 'Spanish (El Salvador)',
345
		'es_US' => 'Spanish (United States)',
346
		'es_UY' => 'Spanish (Uruguay)',
347
		'es_VE' => 'Spanish (Venezuela)',
348
		'et_EE' => 'Estonian (Estonia)',
349
		'eu_ES' => 'Basque (Spain)',
350
		'fa_AF' => 'Persian (Afghanistan)',
351
		'fa_IR' => 'Persian (Iran)',
352
		'fa_PK' => 'Persian (Pakistan)',
353
		'fan_GQ' => 'Fang (Equatorial Guinea)',
354
		'fi_FI' => 'Finnish (Finland)',
355
		'fi_SE' => 'Finnish (Sweden)',
356
		'fil_PH' => 'Filipino (Philippines)',
357
		'fj_FJ' => 'Fijian (Fiji)',
358
		'fo_FO' => 'Faroese (Faroe Islands)',
359
		'fon_BJ' => 'Fon (Benin)',
360
		'fr_002' => 'French (Africa)',
361
		'fr_BE' => 'French (Belgium)',
362
		'fr_CA' => 'French (Canada)',
363
		'fr_CH' => 'French (Switzerland)',
364
		'fr_DZ' => 'French (Algeria)',
365
		'fr_FR' => 'French (France)',
366
		'fr_GF' => 'French (French Guiana)',
367
		'fr_GP' => 'French (Guadeloupe)',
368
		'fr_HT' => 'French (Haiti)',
369
		'fr_KM' => 'French (Comoros)',
370
		'fr_MA' => 'French (Morocco)',
371
		'fr_MQ' => 'French (Martinique)',
372
		'fr_MU' => 'French (Mauritius)',
373
		'fr_NC' => 'French (New Caledonia)',
374
		'fr_PF' => 'French (French Polynesia)',
375
		'fr_PM' => 'French (Saint Pierre and Miquelon)',
376
		'fr_RE' => 'French (Reunion)',
377
		'fr_SC' => 'French (Seychelles)',
378
		'fr_SN' => 'French (Senegal)',
379
		'fr_US' => 'French (United States)',
380
		'fuv_NG' => 'Fulfulde (Nigeria)',
381
		'ga_GB' => 'Irish (United Kingdom)',
382
		'ga_IE' => 'Irish (Ireland)',
383
		'gaa_GH' => 'Ga (Ghana)',
384
		'gbm_IN' => 'Garhwali (India)',
385
		'gcr_GF' => 'Guianese Creole French (French Guiana)',
386
		'gd_GB' => 'Scottish Gaelic (United Kingdom)',
387
		'gil_KI' => 'Gilbertese (Kiribati)',
388
		'gl_ES' => 'Galician (Spain)',
389
		'glk_IR' => 'Gilaki (Iran)',
390
		'gn_PY' => 'Guarani (Paraguay)',
391
		'gno_IN' => 'Gondi, Northern (India)',
392
		'gsw_CH' => 'Swiss German (Switzerland)',
393
		'gsw_LI' => 'Swiss German (Liechtenstein)',
394
		'gu_IN' => 'Gujarati (India)',
395
		'guz_KE' => 'Gusii (Kenya)',
396
		'ha_NE' => 'Hausa (Niger)',
397
		'ha_NG' => 'Hausa (Nigeria)',
398
		'haw_US' => 'Hawaiian (United States)',
399
		'haz_AF' => 'Hazaragi (Afghanistan)',
400
		'he_IL' => 'Hebrew (Israel)',
401
		'hi_IN' => 'Hindi (India)',
402
		'hil_PH' => 'Hiligaynon (Philippines)',
403
		'hne_IN' => 'Chhattisgarhi (India)',
404
		'hno_PK' => 'Hindko, Northern (Pakistan)',
405
		'hoc_IN' => 'Ho (India)',
406
		'hr_AT' => 'Croatian (Austria)',
407
		'hr_BA' => 'Croatian (Bosnia and Herzegovina)',
408
		'hr_HR' => 'Croatian (Croatia)',
409
		'ht_HT' => 'Haitian (Haiti)',
410
		'hu_AT' => 'Hungarian (Austria)',
411
		'hu_HU' => 'Hungarian (Hungary)',
412
		'hu_RO' => 'Hungarian (Romania)',
413
		'hu_RS' => 'Hungarian (Serbia)',
414
		'hy_AM' => 'Armenian (Armenia)',
415
		'id_ID' => 'Indonesian (Indonesia)',
416
		'ig_NG' => 'Igbo (Nigeria)',
417
		'ilo_PH' => 'Iloko (Philippines)',
418
		'inh_RU' => 'Ingush (Russia)',
419
		'is_IS' => 'Icelandic (Iceland)',
420
		'it_CH' => 'Italian (Switzerland)',
421
		'it_FR' => 'Italian (France)',
422
		'it_HR' => 'Italian (Croatia)',
423
		'it_IT' => 'Italian (Italy)',
424
		'it_SM' => 'Italian (San Marino)',
425
		'it_US' => 'Italian (United States)',
426
		'iu_CA' => 'Inuktitut (Canada)',
427
		'ja_JP' => 'Japanese (Japan)',
428
		'jv_ID' => 'Javanese (Indonesia)',
429
		'ka_GE' => 'Georgian (Georgia)',
430
		'kam_KE' => 'Kamba (Kenya)',
431
		'kbd_RU' => 'Kabardian (Russia)',
432
		'kfy_IN' => 'Kumauni (India)',
433
		'kha_IN' => 'Khasi (India)',
434
		'khn_IN' => 'Khandesi (India)',
435
		'ki_KE' => 'Kikuyu (Kenya)',
436
		'kj_NA' => 'Kuanyama (Namibia)',
437
		'kk_CN' => 'Kazakh (China)',
438
		'kk_KZ' => 'Kazakh (Kazakhstan)',
439
		'kl_DK' => 'Kalaallisut (Denmark)',
440
		'kl_GL' => 'Kalaallisut (Greenland)',
441
		'kln_KE' => 'Kalenjin (Kenya)',
442
		'km_KH' => 'Khmer (Cambodia)',
443
		'kn_IN' => 'Kannada (India)',
444
		'ko_KR' => 'Korean (Korea)',
445
		'koi_RU' => 'Komi-Permyak (Russia)',
446
		'kok_IN' => 'Konkani (India)',
447
		'kos_FM' => 'Kosraean (Micronesia)',
448
		'kpv_RU' => 'Komi-Zyrian (Russia)',
449
		'krc_RU' => 'Karachay-Balkar (Russia)',
450
		'kru_IN' => 'Kurukh (India)',
451
		'ks_IN' => 'Kashmiri (India)',
452
		'ku_IQ' => 'Kurdish (Iraq)',
453
		'ku_IR' => 'Kurdish (Iran)',
454
		'ku_SY' => 'Kurdish (Syria)',
455
		'ku_TR' => 'Kurdish (Turkey)',
456
		'kum_RU' => 'Kumyk (Russia)',
457
		'kxm_TH' => 'Khmer, Northern (Thailand)',
458
		'ky_KG' => 'Kirghiz (Kyrgyzstan)',
459
		'la_VA' => 'Latin (Vatican)',
460
		'lah_PK' => 'Lahnda (Pakistan)',
461
		'lb_LU' => 'Luxembourgish (Luxembourg)',
462
		'lbe_RU' => 'Lak (Russia)',
463
		'lc_XX' => 'LOLCAT',
464
		'lez_RU' => 'Lezghian (Russia)',
465
		'lg_UG' => 'Ganda (Uganda)',
466
		'lij_IT' => 'Ligurian (Italy)',
467
		'lij_MC' => 'Ligurian (Monaco)',
468
		'ljp_ID' => 'Lampung (Indonesia)',
469
		'lmn_IN' => 'Lambadi (India)',
470
		'ln_CD' => 'Lingala (Congo - Kinshasa)',
471
		'ln_CG' => 'Lingala (Congo - Brazzaville)',
472
		'lo_LA' => 'Lao (Laos)',
473
		'lrc_IR' => 'Luri, Northern (Iran)',
474
		'lt_LT' => 'Lithuanian (Lithuania)',
475
		'luo_KE' => 'Luo (Kenya)',
476
		'luy_KE' => 'Luyia (Kenya)',
477
		'lv_LV' => 'Latvian (Latvia)',
478
		'mad_ID' => 'Madurese (Indonesia)',
479
		'mai_IN' => 'Maithili (India)',
480
		'mai_NP' => 'Maithili (Nepal)',
481
		'mak_ID' => 'Makasar (Indonesia)',
482
		'mdf_RU' => 'Moksha (Russia)',
483
		'mdh_PH' => 'Maguindanao (Philippines)',
484
		'mer_KE' => 'Meru (Kenya)',
485
		'mfa_TH' => 'Malay, Pattani (Thailand)',
486
		'mfe_MU' => 'Morisyen (Mauritius)',
487
		'mg_MG' => 'Malagasy (Madagascar)',
488
		'mh_MH' => 'Marshallese (Marshall Islands)',
489
		'mi_NZ' => 'te reo Māori (New Zealand)',
490
		'min_ID' => 'Minangkabau (Indonesia)',
491
		'mk_MK' => 'Macedonian (Macedonia)',
492
		'ml_IN' => 'Malayalam (India)',
493
		'mn_CN' => 'Mongolian (China)',
494
		'mn_MN' => 'Mongolian (Mongolia)',
495
		'mni_IN' => 'Manipuri (India)',
496
		'mr_IN' => 'Marathi (India)',
497
		'ms_BN' => 'Malay (Brunei)',
498
		'ms_CC' => 'Malay (Cocos Islands)',
499
		'ms_ID' => 'Malay (Indonesia)',
500
		'ms_MY' => 'Malay (Malaysia)',
501
		'ms_SG' => 'Malay (Singapore)',
502
		'mt_MT' => 'Maltese (Malta)',
503
		'mtr_IN' => 'Mewari (India)',
504
		'mup_IN' => 'Malvi (India)',
505
		'muw_IN' => 'Mundari (India)',
506
		'my_MM' => 'Burmese (Myanmar)',
507
		'myv_RU' => 'Erzya (Russia)',
508
		'na_NR' => 'Nauru (Nauru)',
509
		'nb_NO' => 'Norwegian Bokmal (Norway)',
510
		'nb_SJ' => 'Norwegian Bokmal (Svalbard and Jan Mayen)',
511
		'nd_ZW' => 'North Ndebele (Zimbabwe)',
512
		'ndc_MZ' => 'Ndau (Mozambique)',
513
		'ne_IN' => 'Nepali (India)',
514
		'ne_NP' => 'Nepali (Nepal)',
515
		'ng_NA' => 'Ndonga (Namibia)',
516
		'ngl_MZ' => 'Lomwe (Mozambique)',
517
		'niu_NU' => 'Niuean (Niue)',
518
		'nl_AN' => 'Dutch (Netherlands Antilles)',
519
		'nl_AW' => 'Dutch (Aruba)',
520
		'nl_BE' => 'Dutch (Belgium)',
521
		'nl_NL' => 'Dutch (Netherlands)',
522
		'nl_SR' => 'Dutch (Suriname)',
523
		'nn_NO' => 'Norwegian Nynorsk (Norway)',
524
		'nb_NO' => 'Norwegian',
525
		'nod_TH' => 'Thai, Northern (Thailand)',
526
		'noe_IN' => 'Nimadi (India)',
527
		'nso_ZA' => 'Northern Sotho (South Africa)',
528
		'ny_MW' => 'Nyanja (Malawi)',
529
		'ny_ZM' => 'Nyanja (Zambia)',
530
		'nyn_UG' => 'Nyankole (Uganda)',
531
		'om_ET' => 'Oromo (Ethiopia)',
532
		'or_IN' => 'Oriya (India)',
533
		'pa_IN' => 'Punjabi (India)',
534
		'pag_PH' => 'Pangasinan (Philippines)',
535
		'pap_AN' => 'Papiamento (Netherlands Antilles)',
536
		'pap_AW' => 'Papiamento (Aruba)',
537
		'pau_PW' => 'Palauan (Palau)',
538
		'pl_PL' => 'Polish (Poland)',
539
		'pl_UA' => 'Polish (Ukraine)',
540
		'pon_FM' => 'Pohnpeian (Micronesia)',
541
		'ps_AF' => 'Pashto (Afghanistan)',
542
		'ps_PK' => 'Pashto (Pakistan)',
543
		'pt_AO' => 'Portuguese (Angola)',
544
		'pt_BR' => 'Portuguese (Brazil)',
545
		'pt_CV' => 'Portuguese (Cape Verde)',
546
		'pt_GW' => 'Portuguese (Guinea-Bissau)',
547
		'pt_MZ' => 'Portuguese (Mozambique)',
548
		'pt_PT' => 'Portuguese (Portugal)',
549
		'pt_ST' => 'Portuguese (Sao Tome and Principe)',
550
		'pt_TL' => 'Portuguese (East Timor)',
551
		'qu_BO' => 'Quechua (Bolivia)',
552
		'qu_PE' => 'Quechua (Peru)',
553
		'rcf_RE' => 'R�union Creole French (Reunion)',
554
		'rej_ID' => 'Rejang (Indonesia)',
555
		'rif_MA' => 'Tarifit (Morocco)',
556
		'rjb_IN' => 'Rajbanshi (India)',
557
		'rm_CH' => 'Rhaeto-Romance (Switzerland)',
558
		'rmt_IR' => 'Domari (Iran)',
559
		'rn_BI' => 'Rundi (Burundi)',
560
		'ro_MD' => 'Romanian (Moldova)',
561
		'ro_RO' => 'Romanian (Romania)',
562
		'ro_RS' => 'Romanian (Serbia)',
563
		'ru_BY' => 'Russian (Belarus)',
564
		'ru_KG' => 'Russian (Kyrgyzstan)',
565
		'ru_KZ' => 'Russian (Kazakhstan)',
566
		'ru_RU' => 'Russian (Russia)',
567
		'ru_SJ' => 'Russian (Svalbard and Jan Mayen)',
568
		'ru_UA' => 'Russian (Ukraine)',
569
		'rw_RW' => 'Kinyarwanda (Rwanda)',
570
		'sa_IN' => 'Sanskrit (India)',
571
		'sah_RU' => 'Yakut (Russia)',
572
		'sas_ID' => 'Sasak (Indonesia)',
573
		'sat_IN' => 'Santali (India)',
574
		'sck_IN' => 'Sadri (India)',
575
		'sco_GB' => 'Scots (United Kingdom)',
576
		'sco_SCO' => 'Scots',
577
		'sd_IN' => 'Sindhi (India)',
578
		'sd_PK' => 'Sindhi (Pakistan)',
579
		'se_NO' => 'Northern Sami (Norway)',
580
		'sg_CF' => 'Sango (Central African Republic)',
581
		'si_LK' => 'Sinhalese (Sri Lanka)',
582
		'sid_ET' => 'Sidamo (Ethiopia)',
583
		'sk_RS' => 'Slovak (Serbia)',
584
		'sk_SK' => 'Slovak (Slovakia)',
585
		'sl_AT' => 'Slovenian (Austria)',
586
		'sl_SI' => 'Slovenian (Slovenia)',
587
		'sm_AS' => 'Samoan (American Samoa)',
588
		'sm_WS' => 'Samoan (Samoa)',
589
		'sn_ZW' => 'Shona (Zimbabwe)',
590
		'so_DJ' => 'Somali (Djibouti)',
591
		'so_ET' => 'Somali (Ethiopia)',
592
		'so_SO' => 'Somali (Somalia)',
593
		'sou_TH' => 'Thai, Southern (Thailand)',
594
		'sq_AL' => 'Albanian (Albania)',
595
		'sr_BA' => 'Serbian (Bosnia and Herzegovina)',
596
		'sr_ME' => 'Serbian (Montenegro)',
597
		'sr_RS' => 'Serbian (Serbia)',
598
		'ss_SZ' => 'Swati (Swaziland)',
599
		'ss_ZA' => 'Swati (South Africa)',
600
		'st_LS' => 'Southern Sotho (Lesotho)',
601
		'st_ZA' => 'Southern Sotho (South Africa)',
602
		'su_ID' => 'Sundanese (Indonesia)',
603
		'sv_AX' => 'Swedish (Aland Islands)',
604
		'sv_FI' => 'Swedish (Finland)',
605
		'sv_SE' => 'Swedish (Sweden)',
606
		'sw_KE' => 'Swahili (Kenya)',
607
		'sw_SO' => 'Swahili (Somalia)',
608
		'sw_TZ' => 'Swahili (Tanzania)',
609
		'sw_UG' => 'Swahili (Uganda)',
610
		'swb_KM' => 'Comorian (Comoros)',
611
		'swb_YT' => 'Comorian (Mayotte)',
612
		'swv_IN' => 'Shekhawati (India)',
613
		'ta_IN' => 'Tamil (India)',
614
		'ta_LK' => 'Tamil (Sri Lanka)',
615
		'ta_MY' => 'Tamil (Malaysia)',
616
		'ta_SG' => 'Tamil (Singapore)',
617
		'tcy_IN' => 'Tulu (India)',
618
		'te_IN' => 'Telugu (India)',
619
		'tet_TL' => 'Tetum (East Timor)',
620
		'tg_TJ' => 'Tajik (Tajikistan)',
621
		'th_TH' => 'Thai (Thailand)',
622
		'ti_ER' => 'Tigrinya (Eritrea)',
623
		'ti_ET' => 'Tigrinya (Ethiopia)',
624
		'tk_IR' => 'Turkmen (Iran)',
625
		'tk_TM' => 'Turkmen (Turkmenistan)',
626
		'tkl_TK' => 'Tokelau (Tokelau)',
627
		'tl_PH' => 'Tagalog (Philippines)',
628
		'tl_US' => 'Tagalog (United States)',
629
		'tn_BW' => 'Tswana (Botswana)',
630
		'tn_ZA' => 'Tswana (South Africa)',
631
		'to_TO' => 'Tonga (Tonga)',
632
		'tr_CY' => 'Turkish (Cyprus)',
633
		'tr_DE' => 'Turkish (Germany)',
634
		'tr_MK' => 'Turkish (Macedonia)',
635
		'tr_TR' => 'Turkish (Turkey)',
636
		'ts_MZ' => 'Tsonga (Mozambique)',
637
		'ts_ZA' => 'Tsonga (South Africa)',
638
		'tsg_PH' => 'Tausug (Philippines)',
639
		'tt_RU' => 'Tatar (Russia)',
640
		'tts_TH' => 'Thai, Northeastern (Thailand)',
641
		'tvl_TV' => 'Tuvalu (Tuvalu)',
642
		'tw_GH' => 'Twi (Ghana)',
643
		'ty_PF' => 'Tahitian (French Polynesia)',
644
		'tyv_RU' => 'Tuvinian (Russia)',
645
		'tzm_MA' => 'Tamazight, Central Atlas (Morocco)',
646
		'udm_RU' => 'Udmurt (Russia)',
647
		'ug_CN' => 'Uighur (China)',
648
		'uk_UA' => 'Ukrainian (Ukraine)',
649
		'uli_FM' => 'Ulithian (Micronesia)',
650
		'ur_IN' => 'Urdu (India)',
651
		'ur_PK' => 'Urdu (Pakistan)',
652
		'uz_AF' => 'Uzbek (Afghanistan)',
653
		'uz_UZ' => 'Uzbek (Uzbekistan)',
654
		've_ZA' => 'Venda (South Africa)',
655
		'vi_US' => 'Vietnamese (United States)',
656
		'vi_VN' => 'Vietnamese (Vietnam)',
657
		'vmw_MZ' => 'Waddar (Mozambique)',
658
		'wal_ET' => 'Walamo (Ethiopia)',
659
		'war_PH' => 'Waray (Philippines)',
660
		'wbq_IN' => 'Waddar (India)',
661
		'wbr_IN' => 'Wagdi (India)',
662
		'wo_MR' => 'Wolof (Mauritania)',
663
		'wo_SN' => 'Wolof (Senegal)',
664
		'wtm_IN' => 'Mewati (India)',
665
		'xh_ZA' => 'Xhosa (South Africa)',
666
		'xnr_IN' => 'Kangri (India)',
667
		'xog_UG' => 'Soga (Uganda)',
668
		'yap_FM' => 'Yapese (Micronesia)',
669
		'yo_NG' => 'Yoruba (Nigeria)',
670
		'za_CN' => 'Zhuang (China)',
671
		'zh_CN' => 'Chinese (China)',
672
		'zh_HK' => 'Chinese (Hong Kong SAR China)',
673
		'zh_MO' => 'Chinese (Macao SAR China)',
674
		'zh_SG' => 'Chinese (Singapore)',
675
		'zh_TW' => 'Chinese (Taiwan)',
676
		'zh_US' => 'Chinese (United States)',
677
		'zh_cmn' => 'Chinese (Mandarin)',
678
		'zh_yue' => 'Chinese (Cantonese)',
679
		'zu_ZA' => 'Zulu (South Africa)'
680
	);
681
682
	/**
683
	 * @config
684
	 * @var array $common_languages A list of commonly used languages, in the form
685
	 * langcode => array( EnglishName, NativeName)
686
	 */
687
	private static $common_languages = array(
688
		'af' => array(
689
			'name' => 'Afrikaans',
690
			'native' => 'Afrikaans'
691
		),
692
		'sq' => array(
693
			'name' => 'Albanian',
694
			'native' => 'shqip'
695
		),
696
		'ar' => array(
697
			'name' => 'Arabic',
698
			'native' => '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'
699
		),
700
		'eu' => array(
701
			'name' => 'Basque',
702
			'native' => 'euskera'
703
		),
704
		'be' => array(
705
			'name' => 'Belarusian',
706
			'native' =>
707
				'&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'
708
		),
709
		'bn' => array(
710
			'name' => 'Bengali',
711
			'native' => '&#2476;&#2494;&#2434;&#2482;&#2494;'
712
		),
713
		'bg' => array(
714
			'name' => 'Bulgarian',
715
			'native' => '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'
716
		),
717
		'ca' => array(
718
			'name' => 'Catalan',
719
			'native' => 'catal&agrave;'
720
		),
721
		'zh_yue' => array(
722
			'name' => 'Chinese (Cantonese)',
723
			'native' => '&#24291;&#26481;&#35441; [&#24191;&#19996;&#35805;]'
724
		),
725
		'zh_cmn' => array(
726
			'name' => 'Chinese (Mandarin)',
727
			'native' => '&#26222;&#36890;&#35441; [&#26222;&#36890;&#35805;]'
728
		),
729
		'hr' => array(
730
			'name' => 'Croatian',
731
			'native' => 'Hrvatski'
732
		),
733
		'zh' => array(
734
			'name' => 'Chinese',
735
			'native' => '&#20013;&#22269;&#30340;'
736
		),
737
		'cs' => array(
738
			'name' => 'Czech',
739
			'native' => '&#x010D;e&#353;tina'
740
		),
741
		'cy' => array(
742
			'name' => 'Welsh',
743
			'native' => 'Welsh/Cymraeg'
744
		),
745
		'da' => array(
746
			'name' => 'Danish',
747
			'native' => 'dansk'
748
		),
749
		'nl' => array(
750
			'name' => 'Dutch',
751
			'native' => 'Nederlands'
752
		),
753
		'en' => array(
754
			'name' => 'English',
755
			'native' => 'English'
756
		),
757
		'eo' => array(
758
			'name' => 'Esperanto',
759
			'native' => 'Esperanto'
760
		),
761
		'et' => array(
762
			'name' => 'Estonian',
763
			'native' => 'eesti keel'
764
		),
765
		'fo' => array(
766
			'name' => 'Faroese',
767
			'native' => 'F&oslash;royska'
768
		),
769
		'fi' => array(
770
			'name' => 'Finnish',
771
			'native' => 'suomi'
772
		),
773
		'fr' => array(
774
			'name' => 'French',
775
			'native' => 'fran&ccedil;ais'
776
		),
777
		'gd' => array(
778
			'name' => 'Gaelic',
779
			'native' => 'Gaeilge'
780
		),
781
		'gl' => array(
782
			'name' => 'Galician',
783
			'native' => 'Galego'
784
		),
785
		'de' => array(
786
			'name' => 'German',
787
			'native' => 'Deutsch'
788
		),
789
		'el' => array(
790
			'name' => 'Greek',
791
			'native' => '&#949;&#955;&#955;&#951;&#957;&#953;&#954;&#940;'
792
		),
793
		'gu' => array(
794
			'name' => 'Gujarati',
795
			'native' => '&#2711;&#2753;&#2716;&#2736;&#2750;&#2724;&#2752;'
796
		),
797
		'ha' => array(
798
			'name' => 'Hausa',
799
			'native' => '&#1581;&#1614;&#1608;&#1618;&#1587;&#1614;'
800
		),
801
		'he' => array(
802
			'name' => 'Hebrew',
803
			'native' => '&#1506;&#1489;&#1512;&#1497;&#1514;'
804
		),
805
		'hi' => array(
806
			'name' => 'Hindi',
807
			'native' => '&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;'
808
		),
809
		'hu' => array(
810
			'name' => 'Hungarian',
811
			'native' => 'magyar'
812
		),
813
		'is' => array(
814
			'name' => 'Icelandic',
815
			'native' => '&Iacute;slenska'
816
		),
817
		'io' => array(
818
			'name' => 'Ido',
819
			'native' => 'Ido'
820
		),
821
		'id' => array(
822
			'name' => 'Indonesian',
823
			'native' => 'Bahasa Indonesia'
824
		),
825
		'ga' => array(
826
			'name' => 'Irish',
827
			'native' => 'Irish'
828
		),
829
		'it' => array(
830
			'name' => 'Italian',
831
			'native' => 'italiano'
832
		),
833
		'ja' => array(
834
			'name' => 'Japanese',
835
			'native' => '&#26085;&#26412;&#35486;'
836
		),
837
		'jv' => array(
838
			'name' => 'Javanese',
839
			'native' => 'basa Jawa'
840
		),
841
		'ko' => array(
842
			'name' => 'Korean',
843
			'native' => '&#54620;&#44397;&#50612; [&#38867;&#22283;&#35486;]'
844
		),
845
		'ku' => array(
846
			'name' => 'Kurdish',
847
			'native' => 'Kurd&iacute;'
848
		),
849
		'lv' => array(
850
			'name' => 'Latvian',
851
			'native' => 'latvie&#353;u'
852
		),
853
		'lt' => array(
854
			'name' => 'Lithuanian',
855
			'native' => 'lietuvi&#353;kai'
856
		),
857
		'lmo' => array(
858
			'name' => 'Lombard',
859
			'native' => 'Lombardo'
860
		),
861
		'mk' => array(
862
			'name' => 'Macedonian',
863
			'native' => '&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080;'
864
		),
865
		'mi' => array(
866
			'name' => 'te reo Māori',
867
			'native' => 'te reo Māori'
868
		),
869
		'ms' => array(
870
			'name' => 'Malay',
871
			'native' => 'Bahasa melayu'
872
		),
873
		'mt' => array(
874
			'name' => 'Maltese',
875
			'native' => 'Malti'
876
		),
877
		'mr' => array(
878
			'name' => 'Marathi',
879
			'native' => '&#2350;&#2352;&#2366;&#2336;&#2368;'
880
		),
881
		'ne' => array(
882
			'name' => 'Nepali',
883
			'native' => '&#2344;&#2375;&#2346;&#2366;&#2354;&#2368;'
884
		),
885
		'nb' => array(
886
			'name' => 'Norwegian',
887
			'native' => 'Norsk'
888
		),
889
		'om' => array(
890
			'name' => 'Oromo',
891
			'native' => 'Afaan Oromo'
892
		),
893
		'fa' => array(
894
			'name' => 'Persian',
895
			'native' => '&#1601;&#1575;&#1585;&#1587;&#1609;'
896
		),
897
		'pl' => array(
898
			'name' => 'Polish',
899
			'native' => 'polski'
900
		),
901
		'pt_PT' => array(
902
			'name' => 'Portuguese (Portugal)',
903
			'native' => 'portugu&ecirc;s (Portugal)'
904
		),
905
		'pt_BR' => array(
906
			'name' => 'Portuguese (Brazil)',
907
			'native' => 'portugu&ecirc;s (Brazil)'
908
		),
909
		'pa' => array(
910
			'name' => 'Punjabi',
911
			'native' => '&#2602;&#2672;&#2588;&#2622;&#2604;&#2624;'
912
		),
913
		'qu' => array(
914
			'name' => 'Quechua',
915
			'native' => 'Quechua'
916
		),
917
		'rm' => array(
918
			'name' => 'Romansh',
919
			'native' => 'rumantsch'
920
		),
921
		'ro' => array(
922
			'name' => 'Romanian',
923
			'native' => 'rom&acirc;n'
924
		),
925
		'ru' => array(
926
			'name' => 'Russian',
927
			'native' => '&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;'
928
		),
929
		'sco' => array(
930
			'name' => 'Scots',
931
			'native' => 'Scoats leid, Lallans'
932
		),
933
		'sr' => array(
934
			'name' => 'Serbian',
935
			'native' => '&#1089;&#1088;&#1087;&#1089;&#1082;&#1080;'
936
		),
937
		'sk' => array(
938
			'name' => 'Slovak',
939
			'native' => 'sloven&#269;ina'
940
		),
941
		'sl' => array(
942
			'name' => 'Slovenian',
943
			'native' => 'sloven&#353;&#269;ina'
944
		),
945
		'es' => array(
946
			'name' => 'Spanish',
947
			'native' => 'espa&ntilde;ol'
948
		),
949
		'sv' => array(
950
			'name' => 'Swedish',
951
			'native' => 'Svenska'
952
		),
953
		'tl' => array(
954
			'name' => 'Tagalog',
955
			'native' => 'Tagalog'
956
		),
957
		'ta' => array(
958
			'name' => 'Tamil',
959
			'native' => '&#2980;&#2990;&#3007;&#2996;&#3021;'
960
		),
961
		'te' => array(
962
			'name' => 'Telugu',
963
			'native' => '&#3108;&#3142;&#3122;&#3137;&#3095;&#3137;'
964
		),
965
		'to' => array(
966
			'name' => 'Tonga',
967
			'native' => 'chiTonga'
968
		),
969
		'ts' => array(
970
			'name' => 'Tsonga',
971
			'native' => 'xiTshonga'
972
		),
973
		'tn' => array(
974
			'name' => 'Tswana',
975
			'native' => 'seTswana'
976
		),
977
		'tr' => array(
978
			'name' => 'Turkish',
979
			'native' => 'T&uuml;rk&ccedil;e'
980
		),
981
		'tk' => array(
982
			'name' => 'Turkmen',
983
			'native' => '&#1090;&#1199;&#1088;&#1082;m&#1077;&#1085;&#1095;&#1077;'
984
		),
985
		'tw' => array(
986
			'name' => 'Twi',
987
			'native' => 'twi'
988
		),
989
		'uk' => array(
990
			'name' => 'Ukrainian',
991
			'native' => '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;'
992
		),
993
		'ur' => array(
994
			'name' => 'Urdu',
995
			'native' => '&#1575;&#1585;&#1583;&#1608;'
996
		),
997
		'uz' => array(
998
			'name' => 'Uzbek',
999
			'native' => '&#1118;&#1079;&#1073;&#1077;&#1082;'
1000
		),
1001
		've' => array(
1002
			'name' => 'Venda',
1003
			'native' => 'tshiVen&#x1E13;a'
1004
		),
1005
		'vi' => array(
1006
			'name' => 'Vietnamese',
1007
			'native' => 'ti&#7871;ng vi&#7879;t'
1008
		),
1009
		'wa' => array(
1010
			'name' => 'Walloon',
1011
			'native' => 'walon'
1012
		),
1013
		'wo' => array(
1014
			'name' => 'Wolof',
1015
			'native' => 'Wollof'
1016
		),
1017
		'xh' => array(
1018
			'name' => 'Xhosa',
1019
			'native' => 'isiXhosa'
1020
		),
1021
		'yi' => array(
1022
			'name' => 'Yiddish',
1023
			'native' => '&#1522;&#1460;&#1491;&#1497;&#1513;'
1024
		),
1025
		'zu' => array(
1026
			'name' => 'Zulu',
1027
			'native' => 'isiZulu'
1028
		)
1029
	);
1030
1031
	/**
1032
	 * @config
1033
	 * @var array $common_locales
1034
	 * Sorted alphabtically by the common language name,
1035
	 * not the locale key.
1036
	 */
1037
	private static $common_locales = array(
1038
		'af_ZA' => array(
1039
			'name' => 'Afrikaans',
1040
			'native' => 'Afrikaans'
1041
		),
1042
		'sq_AL' => array(
1043
			'name' => 'Albanian',
1044
			'native' => 'shqip'
1045
		),
1046
		'ar_EG' => array(
1047
			'name' => 'Arabic',
1048
			'native' => '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'
1049
		),
1050
		'eu_ES' => array(
1051
			'name' => 'Basque',
1052
			'native' => 'euskera'
1053
		),
1054
		'be_BY' => array(
1055
			'name' => 'Belarusian',
1056
			'native' =>
1057
				'&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'
1058
		),
1059
		'bn_BD' => array(
1060
			'name' => 'Bengali',
1061
			'native' => '&#2476;&#2494;&#2434;&#2482;&#2494;'
1062
		),
1063
		'bg_BG' => array(
1064
			'name' => 'Bulgarian',
1065
			'native' => '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'
1066
		),
1067
		'ca_ES' => array(
1068
			'name' => 'Catalan',
1069
			'native' => 'catal&agrave;'
1070
		),
1071
		'zh_CN' => array(
1072
			'name' => 'Chinese',
1073
			'native' => '中国的'
1074
		),
1075
		'zh_yue' => array(
1076
			'name' => 'Chinese (Cantonese)',
1077
			'native' => '&#24291;&#26481;&#35441; [&#24191;&#19996;&#35805;]'
1078
		),
1079
		'zh_cmn' => array(
1080
			'name' => 'Chinese (Mandarin)',
1081
			'native' => '&#26222;&#36890;&#35441; [&#26222;&#36890;&#35805;]'
1082
		),
1083
		'hr_HR' => array(
1084
			'name' => 'Croatian',
1085
			'native' => 'Hrvatski'
1086
		),
1087
		'cs_CZ' => array(
1088
			'name' => 'Czech',
1089
			'native' => '&#x010D;e&#353;tina'
1090
		),
1091
		'cy_GB' => array(
1092
			'name' => 'Welsh',
1093
			'native' => 'Welsh/Cymraeg'
1094
		),
1095
		'da_DK' => array(
1096
			'name' => 'Danish',
1097
			'native' => 'dansk'
1098
		),
1099
		'nl_NL' => array(
1100
			'name' => 'Dutch',
1101
			'native' => 'Nederlands'
1102
		),
1103
		'nl_BE' => array(
1104
			'name' => 'Dutch (Belgium)',
1105
			'native' => 'Nederlands (Belgi&euml;)'
1106
		),
1107
		'en_NZ' => array(
1108
			'name' => 'English (NZ)',
1109
			'native' => 'English (NZ)'
1110
		),
1111
		'en_US' => array(
1112
			'name' => 'English (US)',
1113
			'native' => 'English (US)'
1114
		),
1115
		'en_GB' => array(
1116
			'name' => 'English (UK)',
1117
			'native' => 'English (UK)'
1118
		),
1119
		'eo_XX' => array(
1120
			'name' => 'Esperanto',
1121
			'native' => 'Esperanto'
1122
		),
1123
		'et_EE' => array(
1124
			'name' => 'Estonian',
1125
			'native' => 'eesti keel'
1126
		),
1127
		'fo_FO' => array(
1128
			'name' => 'Faroese',
1129
			'native' => 'F&oslash;royska'
1130
		),
1131
		'fi_FI' => array(
1132
			'name' => 'Finnish',
1133
			'native' => 'suomi'
1134
		),
1135
		'fr_FR' => array(
1136
			'name' => 'French',
1137
			'native' => 'fran&ccedil;ais'
1138
		),
1139
		'fr_BE' => array(
1140
			'name' => 'French (Belgium)',
1141
			'native' => 'Fran&ccedil;ais (Belgique)'
1142
		),
1143
		'gd_GB' => array(
1144
			'name' => 'Gaelic',
1145
			'native' => 'Gaeilge'
1146
		),
1147
		'gl_ES' => array(
1148
			'name' => 'Galician',
1149
			'native' => 'Galego'
1150
		),
1151
		'de_DE' => array(
1152
			'name' => 'German',
1153
			'native' => 'Deutsch'
1154
		),
1155
		'el_GR' => array(
1156
			'name' => 'Greek',
1157
			'native' => '&#949;&#955;&#955;&#951;&#957;&#953;&#954;&#940;'
1158
		),
1159
		'gu_IN' => array(
1160
			'name' => 'Gujarati',
1161
			'native' => '&#2711;&#2753;&#2716;&#2736;&#2750;&#2724;&#2752;'
1162
		),
1163
		'ha_NG' => array(
1164
			'name' => 'Hausa',
1165
			'native' => '&#1581;&#1614;&#1608;&#1618;&#1587;&#1614;'
1166
		),
1167
		'he_IL' => array(
1168
			'name' => 'Hebrew',
1169
			'native' => '&#1506;&#1489;&#1512;&#1497;&#1514;'
1170
		),
1171
		'hi_IN' => array(
1172
			'name' => 'Hindi',
1173
			'native' => '&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;'
1174
		),
1175
		'hu_HU' => array(
1176
			'name' => 'Hungarian',
1177
			'native' => 'magyar'
1178
		),
1179
		'is_IS' => array(
1180
			'name' => 'Icelandic',
1181
			'native' => '&Iacute;slenska'
1182
		),
1183
		'id_ID' => array(
1184
			'name' => 'Indonesian',
1185
			'native' => 'Bahasa Indonesia'
1186
		),
1187
		'ga_IE' => array(
1188
			'name' => 'Irish',
1189
			'native' => 'Irish'
1190
		),
1191
		'it_IT' => array(
1192
			'name' => 'Italian',
1193
			'native' => 'italiano'
1194
		),
1195
		'ja_JP' => array(
1196
			'name' => 'Japanese',
1197
			'native' => '&#26085;&#26412;&#35486;'
1198
		),
1199
		'jv_ID' => array(
1200
			'name' => 'Javanese',
1201
			'native' => 'basa Jawa'
1202
		),
1203
		'ko_KR' => array(
1204
			'name' => 'Korean',
1205
			'native' => '&#54620;&#44397;&#50612; [&#38867;&#22283;&#35486;]'
1206
		),
1207
		'ku_IQ' => array(
1208
			'name' => 'Kurdish',
1209
			'native' => 'Kurd&iacute;'
1210
		),
1211
		'lv_LV' => array(
1212
			'name' => 'Latvian',
1213
			'native' => 'latvie&#353;u'
1214
		),
1215
		'lt_LT' => array(
1216
			'name' => 'Lithuanian',
1217
			'native' => 'lietuvi&#353;kai'
1218
		),
1219
		'mk_MK' => array(
1220
			'name' => 'Macedonian',
1221
			'native' => '&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080;'
1222
		),
1223
		'mi_NZ' => array(
1224
			'name' => 'te reo Māori',
1225
			'native' => 'te reo Māori'
1226
		),
1227
		'ms_MY' => array(
1228
			'name' => 'Malay',
1229
			'native' => 'Bahasa melayu'
1230
		),
1231
		'mt_MT' => array(
1232
			'name' => 'Maltese',
1233
			'native' => 'Malti'
1234
		),
1235
		'mr_IN' => array(
1236
			'name' => 'Marathi',
1237
			'native' => '&#2350;&#2352;&#2366;&#2336;&#2368;'
1238
		),
1239
		'ne_NP' => array(
1240
			'name' => 'Nepali',
1241
			'native' => '&#2344;&#2375;&#2346;&#2366;&#2354;&#2368;'
1242
		),
1243
		'nb_NO' => array(
1244
			'name' => 'Norwegian',
1245
			'native' => 'Norsk'
1246
		),
1247
		'om_ET' => array(
1248
			'name' => 'Oromo',
1249
			'native' => 'Afaan Oromo'
1250
		),
1251
		'fa_IR' => array(
1252
			'name' => 'Persian',
1253
			'native' => '&#1601;&#1575;&#1585;&#1587;&#1609;'
1254
		),
1255
		'pl_PL' => array(
1256
			'name' => 'Polish',
1257
			'native' => 'polski'
1258
		),
1259
		'pt_PT' => array(
1260
			'name' => 'Portuguese (Portugal)',
1261
			'native' => 'portugu&ecirc;s (Portugal)'
1262
		),
1263
		'pt_BR' => array(
1264
			'name' => 'Portuguese (Brazil)',
1265
			'native' => 'portugu&ecirc;s (Brazil)'
1266
		),
1267
		'pa_IN' => array(
1268
			'name' => 'Punjabi',
1269
			'native' => '&#2602;&#2672;&#2588;&#2622;&#2604;&#2624;'
1270
		),
1271
		'qu_PE' => array(
1272
			'name' => 'Quechua',
1273
			'native' => 'Quechua'
1274
		),
1275
		'rm_CH' => array(
1276
			'name' => 'Romansh',
1277
			'native' => 'rumantsch'
1278
		),
1279
		'ro_RO' => array(
1280
			'name' => 'Romanian',
1281
			'native' => 'rom&acirc;n'
1282
		),
1283
		'ru_RU' => array(
1284
			'name' => 'Russian',
1285
			'native' => '&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;'
1286
		),
1287
		'sco_SCO' => array(
1288
			'name' => 'Scots',
1289
			'native' => 'Scoats leid, Lallans'
1290
		),
1291
		'sr_RS' => array(
1292
			'name' => 'Serbian',
1293
			'native' => '&#1089;&#1088;&#1087;&#1089;&#1082;&#1080;'
1294
		),
1295
		'sk_SK' => array(
1296
			'name' => 'Slovak',
1297
			'native' => 'sloven&#269;ina'
1298
		),
1299
		'sl_SI' => array(
1300
			'name' => 'Slovenian',
1301
			'native' => 'sloven&#353;&#269;ina'
1302
		),
1303
		'es_ES' => array(
1304
			'name' => 'Spanish',
1305
			'native' => 'espa&ntilde;ol'
1306
		),
1307
		'sv_SE' => array(
1308
			'name' => 'Swedish',
1309
			'native' => 'Svenska'
1310
		),
1311
		'tl_PH' => array(
1312
			'name' => 'Tagalog',
1313
			'native' => 'Tagalog'
1314
		),
1315
		'ta_IN' => array(
1316
			'name' => 'Tamil',
1317
			'native' => '&#2980;&#2990;&#3007;&#2996;&#3021;'
1318
		),
1319
		'te_IN' => array(
1320
			'name' => 'Telugu',
1321
			'native' => '&#3108;&#3142;&#3122;&#3137;&#3095;&#3137;'
1322
		),
1323
		'to_TO' => array(
1324
			'name' => 'Tonga',
1325
			'native' => 'chiTonga'
1326
		),
1327
		'ts_ZA' => array(
1328
			'name' => 'Tsonga',
1329
			'native' => 'xiTshonga'
1330
		),
1331
		'tn_ZA' => array(
1332
			'name' => 'Tswana',
1333
			'native' => 'seTswana'
1334
		),
1335
		'tr_TR' => array(
1336
			'name' => 'Turkish',
1337
			'native' => 'T&uuml;rk&ccedil;e'
1338
		),
1339
		'tk_TM' => array(
1340
			'name' => 'Turkmen',
1341
			'native' => '&#1090;&#1199;&#1088;&#1082;m&#1077;&#1085;&#1095;&#1077;'
1342
		),
1343
		'tw_GH' => array(
1344
			'name' => 'Twi',
1345
			'native' => 'twi'
1346
		),
1347
		'uk_UA' => array(
1348
			'name' => 'Ukrainian',
1349
			'native' => '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;'
1350
		),
1351
		'ur_PK' => array(
1352
			'name' => 'Urdu',
1353
			'native' => '&#1575;&#1585;&#1583;&#1608;'
1354
		),
1355
		'uz_UZ' => array(
1356
			'name' => 'Uzbek',
1357
			'native' => '&#1118;&#1079;&#1073;&#1077;&#1082;'
1358
		),
1359
		've_ZA' => array(
1360
			'name' => 'Venda',
1361
			'native' => 'tshiVen&#x1E13;a'
1362
		),
1363
		'vi_VN' => array(
1364
			'name' => 'Vietnamese',
1365
			'native' => 'ti&#7871;ng vi&#7879;t'
1366
		),
1367
		'wo_SN' => array(
1368
			'name' => 'Wolof',
1369
			'native' => 'Wollof'
1370
		),
1371
		'xh_ZA' => array(
1372
			'name' => 'Xhosa',
1373
			'native' => 'isiXhosa'
1374
		),
1375
		'zu_ZA' => array(
1376
			'name' => 'Zulu',
1377
			'native' => 'isiZulu'
1378
		),
1379
	);
1380
1381
	/**
1382
	 * @config
1383
	 * @var array
1384
	 */
1385
	private static $tinymce_lang = array(
1386
		'ar_EG' => 'ar',
1387
		'ca_AD' => 'ca',
1388
		'ca_ES' => 'ca',
1389
		'cs_CZ' => 'cs',
1390
		'cy_GB' => 'cy',
1391
		'da_DK' => 'da',
1392
		'da_GL' => 'da',
1393
		'de_AT' => 'de',
1394
		'de_BE' => 'de',
1395
		'de_CH' => 'de',
1396
		'de_DE' => 'de',
1397
		'de_LI' => 'de',
1398
		'de_LU' => 'de',
1399
		'de_BR' => 'de',
1400
		'de_US' => 'de',
1401
		'el_CY' => 'el',
1402
		'el_GR' => 'el',
1403
		'es_AR' => 'es',
1404
		'es_BO' => 'es',
1405
		'es_CL' => 'es',
1406
		'es_CO' => 'es',
1407
		'es_CR' => 'es',
1408
		'es_CU' => 'es',
1409
		'es_DO' => 'es',
1410
		'es_EC' => 'es',
1411
		'es_ES' => 'es',
1412
		'es_GQ' => 'es',
1413
		'es_GT' => 'es',
1414
		'es_HN' => 'es',
1415
		'es_MX' => 'es',
1416
		'es_NI' => 'es',
1417
		'es_PA' => 'es',
1418
		'es_PE' => 'es',
1419
		'es_PH' => 'es',
1420
		'es_PR' => 'es',
1421
		'es_PY' => 'es',
1422
		'es_SV' => 'es',
1423
		'es_UY' => 'es',
1424
		'es_VE' => 'es',
1425
		'es_AD' => 'es',
1426
		'es_BZ' => 'es',
1427
		'es_US' => 'es',
1428
		'fa_AF' => 'fa',
1429
		'fa_IR' => 'fa',
1430
		'fa_PK' => 'fa',
1431
		'fi_FI' => 'fi',
1432
		'fi_SE' => 'fi',
1433
		'fr_BE' => 'fr',
1434
		'fr_BF' => 'fr',
1435
		'fr_BI' => 'fr',
1436
		'fr_BJ' => 'fr',
1437
		'fr_CA' => 'fr_ca',
1438
		'fr_CF' => 'fr',
1439
		'fr_CG' => 'fr',
1440
		'fr_CH' => 'fr',
1441
		'fr_CI' => 'fr',
1442
		'fr_CM' => 'fr',
1443
		'fr_DJ' => 'fr',
1444
		'fr_DZ' => 'fr',
1445
		'fr_FR' => 'fr',
1446
		'fr_GA' => 'fr',
1447
		'fr_GF' => 'fr',
1448
		'fr_GN' => 'fr',
1449
		'fr_GP' => 'fr',
1450
		'fr_HT' => 'fr',
1451
		'fr_KM' => 'fr',
1452
		'fr_LU' => 'fr',
1453
		'fr_MA' => 'fr',
1454
		'fr_MC' => 'fr',
1455
		'fr_MG' => 'fr',
1456
		'fr_ML' => 'fr',
1457
		'fr_MQ' => 'fr',
1458
		'fr_MU' => 'fr',
1459
		'fr_NC' => 'fr',
1460
		'fr_NE' => 'fr',
1461
		'fr_PF' => 'fr',
1462
		'fr_PM' => 'fr',
1463
		'fr_RE' => 'fr',
1464
		'fr_RW' => 'fr',
1465
		'fr_SC' => 'fr',
1466
		'fr_SN' => 'fr',
1467
		'fr_SY' => 'fr',
1468
		'fr_TD' => 'fr',
1469
		'fr_TG' => 'fr',
1470
		'fr_TN' => 'fr',
1471
		'fr_VU' => 'fr',
1472
		'fr_WF' => 'fr',
1473
		'fr_YT' => 'fr',
1474
		'fr_GB' => 'fr',
1475
		'fr_US' => 'fr',
1476
		'he_IL' => 'he',
1477
		'hu_HU' => 'hu',
1478
		'hu_AT' => 'hu',
1479
		'hu_RO' => 'hu',
1480
		'hu_RS' => 'hu',
1481
		'is_IS' => 'is',
1482
		'it_CH' => 'it',
1483
		'it_IT' => 'it',
1484
		'it_SM' => 'it',
1485
		'it_FR' => 'it',
1486
		'it_HR' => 'it',
1487
		'it_US' => 'it',
1488
		'it_VA' => 'it',
1489
		'ja_JP' => 'ja',
1490
		'ko_KP' => 'ko',
1491
		'ko_KR' => 'ko',
1492
		'ko_CN' => 'ko',
1493
		'mi_NZ' => 'mi_NZ',
1494
		'nb_NO' => 'nb',
1495
		'nb_SJ' => 'nb',
1496
		'nl_AN' => 'nl',
1497
		'nl_AW' => 'nl',
1498
		'nl_BE' => 'nl',
1499
		'nl_NL' => 'nl',
1500
		'nl_SR' => 'nl',
1501
		'nn_NO' => 'nn',
1502
		'pl_PL' => 'pl',
1503
		'pl_UA' => 'pl',
1504
		'pt_AO' => 'pt',
1505
		'pt_BR' => 'pt',
1506
		'pt_CV' => 'pt',
1507
		'pt_GW' => 'pt',
1508
		'pt_MZ' => 'pt',
1509
		'pt_PT' => 'pt',
1510
		'pt_ST' => 'pt',
1511
		'pt_TL' => 'pt',
1512
		'ro_MD' => 'ro',
1513
		'ro_RO' => 'ro',
1514
		'ro_RS' => 'ro',
1515
		'ru_BY' => 'ru',
1516
		'ru_KG' => 'ru',
1517
		'ru_KZ' => 'ru',
1518
		'ru_RU' => 'ru',
1519
		'ru_SJ' => 'ru',
1520
		'ru_UA' => 'ru',
1521
		'si_LK' => 'si',
1522
		'sk_SK' => 'sk',
1523
		'sk_RS' => 'sk',
1524
		'sq_AL' => 'sq',
1525
		'sr_BA' => 'sr',
1526
		'sr_ME' => 'sr',
1527
		'sr_RS' => 'sr',
1528
		'sv_FI' => 'sv',
1529
		'sv_SE' => 'sv',
1530
		'tr_CY' => 'tr',
1531
		'tr_TR' => 'tr',
1532
		'tr_DE' => 'tr',
1533
		'tr_MK' => 'tr',
1534
		'uk_UA' => 'uk',
1535
		'vi_VN' => 'vi',
1536
		'vi_US' => 'vi',
1537
		'zh_CN' => 'zh-cn',
1538
		'zh_HK' => 'zh-cn',
1539
		'zh_MO' => 'zh-cn',
1540
		'zh_SG' => 'zh-cn',
1541
		'zh_TW' => 'zh-tw',
1542
		'zh_ID' => 'zh-cn',
1543
		'zh_MY' => 'zh-cn',
1544
		'zh_TH' => 'zh-cn',
1545
		'zh_US' => 'zn-cn',
1546
1547
	);
1548
1549
	/**
1550
	 * @config
1551
	 * @var array $likely_subtags Provides you "likely locales"
1552
	 * for a given "short" language code. This is a guess,
1553
	 * as we can't disambiguate from e.g. "en" to "en_US" - it
1554
	 * could also mean "en_UK".
1555
	 * @see http://www.unicode.org/cldr/data/charts/supplemental/likely_subtags.html
1556
	 */
1557
	private static $likely_subtags = array(
1558
		'aa' => 'aa_ET',
1559
		'ab' => 'ab_GE',
1560
		'ady' => 'ady_RU',
1561
		'af' => 'af_ZA',
1562
		'ak' => 'ak_GH',
1563
		'am' => 'am_ET',
1564
		'ar' => 'ar_EG',
1565
		'as' => 'as_IN',
1566
		'ast' => 'ast_ES',
1567
		'av' => 'av_RU',
1568
		'ay' => 'ay_BO',
1569
		'az' => 'az_AZ',
1570
		'az_Cyrl' => 'az_AZ',
1571
		'az_Arab' => 'az_IR',
1572
		'az_IR' => 'az_IR',
1573
		'ba' => 'ba_RU',
1574
		'be' => 'be_BY',
1575
		'bg' => 'bg_BG',
1576
		'bi' => 'bi_VU',
1577
		'bn' => 'bn_BD',
1578
		'bo' => 'bo_CN',
1579
		'bs' => 'bs_BA',
1580
		'ca' => 'ca_ES',
1581
		'ce' => 'ce_RU',
1582
		'ceb' => 'ceb_PH',
1583
		'ch' => 'ch_GU',
1584
		'chk' => 'chk_FM',
1585
		'crk' => 'crk_CA',
1586
		'cs' => 'cs_CZ',
1587
		'cwd' => 'cwd_CA',
1588
		'cy' => 'cy_GB',
1589
		'da' => 'da_DK',
1590
		'de' => 'de_DE',
1591
		'dv' => 'dv_MV',
1592
		'dz' => 'dz_BT',
1593
		'ee' => 'ee_GH',
1594
		'efi' => 'efi_NG',
1595
		'el' => 'el_GR',
1596
		'en' => 'en_US',
1597
		'es' => 'es_ES',
1598
		'et' => 'et_EE',
1599
		'eu' => 'eu_ES',
1600
		'eo' => 'eo_XX',
1601
		'fa' => 'fa_IR',
1602
		'fi' => 'fi_FI',
1603
		'fil' => 'fil_PH',
1604
		'fj' => 'fj_FJ',
1605
		'fo' => 'fo_FO',
1606
		'fr' => 'fr_FR',
1607
		'fur' => 'fur_IT',
1608
		'fy' => 'fy_NL',
1609
		'ga' => 'ga_IE',
1610
		'gaa' => 'gaa_GH',
1611
		'gd' => 'gd_GB',
1612
		'gil' => 'gil_KI',
1613
		'gl' => 'gl_ES',
1614
		'gn' => 'gn_PY',
1615
		'gu' => 'gu_IN',
1616
		'ha' => 'ha_NG',
1617
		'ha_Arab' => 'ha_SD',
1618
		'ha_SD' => 'ha_SD',
1619
		'haw' => 'haw_US',
1620
		'he' => 'he_IL',
1621
		'hi' => 'hi_IN',
1622
		'hil' => 'hil_PH',
1623
		'ho' => 'ho_PG',
1624
		'hr' => 'hr_HR',
1625
		'ht' => 'ht_HT',
1626
		'hu' => 'hu_HU',
1627
		'hy' => 'hy_AM',
1628
		'id' => 'id_ID',
1629
		'ig' => 'ig_NG',
1630
		'ii' => 'ii_CN',
1631
		'ilo' => 'ilo_PH',
1632
		'inh' => 'inh_RU',
1633
		'is' => 'is_IS',
1634
		'it' => 'it_IT',
1635
		'iu' => 'iu_CA',
1636
		'ja' => 'ja_JP',
1637
		'jv' => 'jv_ID',
1638
		'ka' => 'ka_GE',
1639
		'kaj' => 'kaj_NG',
1640
		'kam' => 'kam_KE',
1641
		'kbd' => 'kbd_RU',
1642
		'kha' => 'kha_IN',
1643
		'kk' => 'kk_KZ',
1644
		'kl' => 'kl_GL',
1645
		'km' => 'km_KH',
1646
		'kn' => 'kn_IN',
1647
		'ko' => 'ko_KR',
1648
		'koi' => 'koi_RU',
1649
		'kok' => 'kok_IN',
1650
		'kos' => 'kos_FM',
1651
		'kpe' => 'kpe_LR',
1652
		'kpv' => 'kpv_RU',
1653
		'krc' => 'krc_RU',
1654
		'ks' => 'ks_IN',
1655
		'ku' => 'ku_IQ',
1656
		'ku_Latn' => 'ku_TR',
1657
		'ku_TR' => 'ku_TR',
1658
		'kum' => 'kum_RU',
1659
		'kxm' => 'kxm_TH',
1660
		'ky' => 'ky_KG',
1661
		'la' => 'la_VA',
1662
		'lah' => 'lah_PK',
1663
		'lb' => 'lb_LU',
1664
		'lbe' => 'lbe_RU',
1665
		'lez' => 'lez_RU',
1666
		'ln' => 'ln_CD',
1667
		'lo' => 'lo_LA',
1668
		'lt' => 'lt_LT',
1669
		'lv' => 'lv_LV',
1670
		'mai' => 'mai_IN',
1671
		'mdf' => 'mdf_RU',
1672
		'mdh' => 'mdh_PH',
1673
		'mg' => 'mg_MG',
1674
		'mh' => 'mh_MH',
1675
		'mi' => 'mi_NZ',
1676
		'mk' => 'mk_MK',
1677
		'ml' => 'ml_IN',
1678
		'mn' => 'mn_MN',
1679
		'mn_CN' => 'mn_CN',
1680
		'mn_Mong' => 'mn_CN',
1681
		'mr' => 'mr_IN',
1682
		'ms' => 'ms_MY',
1683
		'mt' => 'mt_MT',
1684
		'my' => 'my_MM',
1685
		'myv' => 'myv_RU',
1686
		'na' => 'na_NR',
1687
		'nb' => 'nb_NO',
1688
		'ne' => 'ne_NP',
1689
		'niu' => 'niu_NU',
1690
		'nl' => 'nl_NL',
1691
		'nn' => 'nn_NO',
1692
		'nr' => 'nr_ZA',
1693
		'nso' => 'nso_ZA',
1694
		'ny' => 'ny_MW',
1695
		'om' => 'om_ET',
1696
		'or' => 'or_IN',
1697
		'os' => 'os_GE',
1698
		'pa' => 'pa_IN',
1699
		'pa_Arab' => 'pa_PK',
1700
		'pa_PK' => 'pa_PK',
1701
		'pag' => 'pag_PH',
1702
		'pap' => 'pap_AN',
1703
		'pau' => 'pau_PW',
1704
		'pl' => 'pl_PL',
1705
		'pon' => 'pon_FM',
1706
		'ps' => 'ps_AF',
1707
		'pt' => 'pt_BR',
1708
		'qu' => 'qu_PE',
1709
		'rm' => 'rm_CH',
1710
		'rn' => 'rn_BI',
1711
		'ro' => 'ro_RO',
1712
		'ru' => 'ru_RU',
1713
		'rw' => 'rw_RW',
1714
		'sa' => 'sa_IN',
1715
		'sah' => 'sah_RU',
1716
		'sat' => 'sat_IN',
1717
		'sd' => 'sd_IN',
1718
		'se' => 'se_NO',
1719
		'sg' => 'sg_CF',
1720
		'si' => 'si_LK',
1721
		'sid' => 'sid_ET',
1722
		'sk' => 'sk_SK',
1723
		'sl' => 'sl_SI',
1724
		'sm' => 'sm_WS',
1725
		'sn' => 'sn_ZW',
1726
		'so' => 'so_SO',
1727
		'sq' => 'sq_AL',
1728
		'sr' => 'sr_RS',
1729
		'ss' => 'ss_ZA',
1730
		'st' => 'st_ZA',
1731
		'su' => 'su_ID',
1732
		'sv' => 'sv_SE',
1733
		'sw' => 'sw_TZ',
1734
		'swb' => 'swb_KM',
1735
		'ta' => 'ta_IN',
1736
		'te' => 'te_IN',
1737
		'tet' => 'tet_TL',
1738
		'tg' => 'tg_TJ',
1739
		'th' => 'th_TH',
1740
		'ti' => 'ti_ET',
1741
		'tig' => 'tig_ER',
1742
		'tk' => 'tk_TM',
1743
		'tkl' => 'tkl_TK',
1744
		'tl' => 'tl_PH',
1745
		'tn' => 'tn_ZA',
1746
		'to' => 'to_TO',
1747
		'tpi' => 'tpi_PG',
1748
		'tr' => 'tr_TR',
1749
		'trv' => 'trv_TW',
1750
		'ts' => 'ts_ZA',
1751
		'tsg' => 'tsg_PH',
1752
		'tt' => 'tt_RU',
1753
		'tts' => 'tts_TH',
1754
		'tvl' => 'tvl_TV',
1755
		'tw' => 'tw_GH',
1756
		'ty' => 'ty_PF',
1757
		'tyv' => 'tyv_RU',
1758
		'udm' => 'udm_RU',
1759
		'ug' => 'ug_CN',
1760
		'uk' => 'uk_UA',
1761
		'uli' => 'uli_FM',
1762
		'und' => 'en_US',
1763
		'und_AD' => 'ca_AD',
1764
		'und_AE' => 'ar_AE',
1765
		'und_AF' => 'fa_AF',
1766
		'und_AL' => 'sq_AL',
1767
		'und_AM' => 'hy_AM',
1768
		'und_AN' => 'pap_AN',
1769
		'und_AO' => 'pt_AO',
1770
		'und_AR' => 'es_AR',
1771
		'und_AS' => 'sm_AS',
1772
		'und_AT' => 'de_AT',
1773
		'und_AW' => 'nl_AW',
1774
		'und_AX' => 'sv_AX',
1775
		'und_AZ' => 'az_AZ',
1776
		'und_Arab' => 'ar_EG',
1777
		'und_Arab_CN' => 'ug_CN',
1778
		'und_Arab_DJ' => 'ar_DJ',
1779
		'und_Arab_ER' => 'ar_ER',
1780
		'und_Arab_IL' => 'ar_IL',
1781
		'und_Arab_IN' => 'ur_IN',
1782
		'und_Arab_PK' => 'ur_PK',
1783
		'und_Armn' => 'hy_AM',
1784
		'und_BA' => 'bs_BA',
1785
		'und_BD' => 'bn_BD',
1786
		'und_BE' => 'nl_BE',
1787
		'und_BF' => 'fr_BF',
1788
		'und_BG' => 'bg_BG',
1789
		'und_BH' => 'ar_BH',
1790
		'und_BI' => 'rn_BI',
1791
		'und_BJ' => 'fr_BJ',
1792
		'und_BL' => 'fr_BL',
1793
		'und_BN' => 'ms_BN',
1794
		'und_BO' => 'es_BO',
1795
		'und_BR' => 'pt_BR',
1796
		'und_BT' => 'dz_BT',
1797
		'und_BY' => 'be_BY',
1798
		'und_Beng' => 'bn_BD',
1799
		'und_CD' => 'fr_CD',
1800
		'und_CF' => 'sg_CF',
1801
		'und_CG' => 'ln_CG',
1802
		'und_CH' => 'de_CH',
1803
		'und_CI' => 'fr_CI',
1804
		'und_CL' => 'es_CL',
1805
		'und_CM' => 'fr_CM',
1806
		'und_CN' => 'zh_CN',
1807
		'und_CO' => 'es_CO',
1808
		'und_CR' => 'es_CR',
1809
		'und_CU' => 'es_CU',
1810
		'und_CV' => 'pt_CV',
1811
		'und_CY' => 'el_CY',
1812
		'und_CZ' => 'cs_CZ',
1813
		'und_Cans' => 'cwd_CA',
1814
		'und_Cyrl' => 'ru_RU',
1815
		'und_Cyrl_BA' => 'sr_BA',
1816
		'und_Cyrl_GE' => 'ab_GE',
1817
		'und_DE' => 'de_DE',
1818
		'und_DJ' => 'aa_DJ',
1819
		'und_DK' => 'da_DK',
1820
		'und_DO' => 'es_DO',
1821
		'und_DZ' => 'ar_DZ',
1822
		'und_Deva' => 'hi_IN',
1823
		'und_EC' => 'es_EC',
1824
		'und_EE' => 'et_EE',
1825
		'und_EG' => 'ar_EG',
1826
		'und_EH' => 'ar_EH',
1827
		'und_ER' => 'ti_ER',
1828
		'und_ES' => 'es_ES',
1829
		'und_ET' => 'am_ET',
1830
		'und_Ethi' => 'am_ET',
1831
		'und_FI' => 'fi_FI',
1832
		'und_FJ' => 'fj_FJ',
1833
		'und_FM' => 'chk_FM',
1834
		'und_FO' => 'fo_FO',
1835
		'und_FR' => 'fr_FR',
1836
		'und_GA' => 'fr_GA',
1837
		'und_GE' => 'ka_GE',
1838
		'und_GF' => 'fr_GF',
1839
		'und_GH' => 'ak_GH',
1840
		'und_GL' => 'kl_GL',
1841
		'und_GN' => 'fr_GN',
1842
		'und_GP' => 'fr_GP',
1843
		'und_GQ' => 'fr_GQ',
1844
		'und_GR' => 'el_GR',
1845
		'und_GT' => 'es_GT',
1846
		'und_GU' => 'ch_GU',
1847
		'und_GW' => 'pt_GW',
1848
		'und_Geor' => 'ka_GE',
1849
		'und_Grek' => 'el_GR',
1850
		'und_Gujr' => 'gu_IN',
1851
		'und_Guru' => 'pa_IN',
1852
		'und_HK' => 'zh_HK',
1853
		'und_HN' => 'es_HN',
1854
		'und_HR' => 'hr_HR',
1855
		'und_HT' => 'ht_HT',
1856
		'und_HU' => 'hu_HU',
1857
		'und_Hani' => 'zh_CN',
1858
		'und_Hans' => 'zh_CN',
1859
		'und_Hant' => 'zh_TW',
1860
		'und_Hebr' => 'he_IL',
1861
		'und_ID' => 'id_ID',
1862
		'und_IL' => 'he_IL',
1863
		'und_IN' => 'hi_IN',
1864
		'und_IQ' => 'ar_IQ',
1865
		'und_IR' => 'fa_IR',
1866
		'und_IS' => 'is_IS',
1867
		'und_IT' => 'it_IT',
1868
		'und_JO' => 'ar_JO',
1869
		'und_JP' => 'ja_JP',
1870
		'und_Jpan' => 'ja_JP',
1871
		'und_KG' => 'ky_KG',
1872
		'und_KH' => 'km_KH',
1873
		'und_KM' => 'ar_KM',
1874
		'und_KP' => 'ko_KP',
1875
		'und_KR' => 'ko_KR',
1876
		'und_KW' => 'ar_KW',
1877
		'und_KZ' => 'ru_KZ',
1878
		'und_Khmr' => 'km_KH',
1879
		'und_Knda' => 'kn_IN',
1880
		'und_Kore' => 'ko_KR',
1881
		'und_LA' => 'lo_LA',
1882
		'und_LB' => 'ar_LB',
1883
		'und_LI' => 'de_LI',
1884
		'und_LK' => 'si_LK',
1885
		'und_LS' => 'st_LS',
1886
		'und_LT' => 'lt_LT',
1887
		'und_LU' => 'fr_LU',
1888
		'und_LV' => 'lv_LV',
1889
		'und_LY' => 'ar_LY',
1890
		'und_Laoo' => 'lo_LA',
1891
		'und_Latn_CN' => 'ii_CN',
1892
		'und_Latn_CY' => 'tr_CY',
1893
		'und_Latn_DZ' => 'fr_DZ',
1894
		'und_Latn_ET' => 'om_ET',
1895
		'und_Latn_KM' => 'fr_KM',
1896
		'und_Latn_MA' => 'fr_MA',
1897
		'und_Latn_MK' => 'sq_MK',
1898
		'und_Latn_SY' => 'fr_SY',
1899
		'und_Latn_TD' => 'fr_TD',
1900
		'und_Latn_TN' => 'fr_TN',
1901
		'und_MA' => 'ar_MA',
1902
		'und_MC' => 'fr_MC',
1903
		'und_MD' => 'ro_MD',
1904
		'und_ME' => 'sr_ME',
1905
		'und_MF' => 'fr_MF',
1906
		'und_MG' => 'mg_MG',
1907
		'und_MH' => 'mh_MH',
1908
		'und_MK' => 'mk_MK',
1909
		'und_ML' => 'fr_ML',
1910
		'und_MM' => 'my_MM',
1911
		'und_MN' => 'mn_MN',
1912
		'und_MO' => 'zh_MO',
1913
		'und_MQ' => 'fr_MQ',
1914
		'und_MR' => 'ar_MR',
1915
		'und_MT' => 'mt_MT',
1916
		'und_MV' => 'dv_MV',
1917
		'und_MW' => 'ny_MW',
1918
		'und_MX' => 'es_MX',
1919
		'und_MY' => 'ms_MY',
1920
		'und_MZ' => 'pt_MZ',
1921
		'und_Mlym' => 'ml_IN',
1922
		'und_Mong' => 'mn_CN',
1923
		'und_Mymr' => 'my_MM',
1924
		'und_NC' => 'fr_NC',
1925
		'und_NE' => 'ha_NE',
1926
		'und_NG' => 'ha_NG',
1927
		'und_NI' => 'es_NI',
1928
		'und_NL' => 'nl_NL',
1929
		'und_NO' => 'nb_NO',
1930
		'und_NP' => 'ne_NP',
1931
		'und_NR' => 'na_NR',
1932
		'und_NU' => 'niu_NU',
1933
		'und_OM' => 'ar_OM',
1934
		'und_Orya' => 'or_IN',
1935
		'und_PA' => 'es_PA',
1936
		'und_PE' => 'es_PE',
1937
		'und_PF' => 'ty_PF',
1938
		'und_PG' => 'tpi_PG',
1939
		'und_PH' => 'fil_PH',
1940
		'und_PK' => 'ur_PK',
1941
		'und_PL' => 'pl_PL',
1942
		'und_PM' => 'fr_PM',
1943
		'und_PR' => 'es_PR',
1944
		'und_PS' => 'ar_PS',
1945
		'und_PT' => 'pt_PT',
1946
		'und_PW' => 'pau_PW',
1947
		'und_PY' => 'gn_PY',
1948
		'und_QA' => 'ar_QA',
1949
		'und_RE' => 'fr_RE',
1950
		'und_RO' => 'ro_RO',
1951
		'und_RS' => 'sr_RS',
1952
		'und_RU' => 'ru_RU',
1953
		'und_RW' => 'rw_RW',
1954
		'und_SA' => 'ar_SA',
1955
		'und_SD' => 'ar_SD',
1956
		'und_SE' => 'sv_SE',
1957
		'und_SI' => 'sl_SI',
1958
		'und_SJ' => 'nb_SJ',
1959
		'und_SK' => 'sk_SK',
1960
		'und_SM' => 'it_SM',
1961
		'und_SN' => 'fr_SN',
1962
		'und_SO' => 'so_SO',
1963
		'und_SR' => 'nl_SR',
1964
		'und_ST' => 'pt_ST',
1965
		'und_SV' => 'es_SV',
1966
		'und_SY' => 'ar_SY',
1967
		'und_Sinh' => 'si_LK',
1968
		'und_TD' => 'ar_TD',
1969
		'und_TG' => 'ee_TG',
1970
		'und_TH' => 'th_TH',
1971
		'und_TJ' => 'tg_TJ',
1972
		'und_TK' => 'tkl_TK',
1973
		'und_TL' => 'tet_TL',
1974
		'und_TM' => 'tk_TM',
1975
		'und_TN' => 'ar_TN',
1976
		'und_TO' => 'to_TO',
1977
		'und_TR' => 'tr_TR',
1978
		'und_TV' => 'tvl_TV',
1979
		'und_TW' => 'zh_TW',
1980
		'und_Taml' => 'ta_IN',
1981
		'und_Telu' => 'te_IN',
1982
		'und_Thaa' => 'dv_MV',
1983
		'und_Thai' => 'th_TH',
1984
		'und_Tibt' => 'bo_CN',
1985
		'und_UA' => 'uk_UA',
1986
		'und_UY' => 'es_UY',
1987
		'und_UZ' => 'uz_UZ',
1988
		'und_VA' => 'la_VA',
1989
		'und_VE' => 'es_VE',
1990
		'und_VN' => 'vi_VN',
1991
		'und_VU' => 'fr_VU',
1992
		'und_WF' => 'fr_WF',
1993
		'und_WS' => 'sm_WS',
1994
		'und_YE' => 'ar_YE',
1995
		'und_YT' => 'fr_YT',
1996
		'und_ZW' => 'sn_ZW',
1997
		'ur' => 'ur_PK',
1998
		'uz' => 'uz_UZ',
1999
		'uz_AF' => 'uz_AF',
2000
		'uz_Arab' => 'uz_AF',
2001
		've' => 've_ZA',
2002
		'vi' => 'vi_VN',
2003
		'wal' => 'wal_ET',
2004
		'war' => 'war_PH',
2005
		'wo' => 'wo_SN',
2006
		'xh' => 'xh_ZA',
2007
		'yap' => 'yap_FM',
2008
		'yo' => 'yo_NG',
2009
		'za' => 'za_CN',
2010
		'zh' => 'zh_CN',
2011
		'zh_HK' => 'zh_HK',
2012
		'zh_Hani' => 'zh_CN',
2013
		'zh_Hant' => 'zh_TW',
2014
		'zh_MO' => 'zh_MO',
2015
		'zh_TW' => 'zh_TW',
2016
		'zu' => 'zu_ZA',
2017
	);
2018
2019
	/**
2020
	 * This is the main translator function. Returns the string defined by $class and $entity according to the
2021
	 * currently set locale.
2022
	 *
2023
	 * @param string $entity Entity that identifies the string. It must be in the form "Namespace.Entity" where
2024
	 *                       Namespace will be usually the class name where this string is used and Entity identifies
2025
	 *                       the string inside the namespace.
2026
	 * @param string $string The original string itself. In a usual call this is a mandatory parameter, but if you are
2027
	 *                       reusing a string which has already been "declared" (using another call to this function,
2028
	 *                       with the same class and entity), you can omit it.
2029
	 * @param string $context (optional) If the string can be difficult to translate by any reason, you can help
2030
	 *                        translators with some more info using this param
2031
	 * @param string injectionArray (optional) array of key value pairs that are used to replace corresponding
2032
	 *                              expressions in {curly brackets} in the $string. The injection array can also be
2033
	 *                              used as the their argument to the _t() function
2034
	 * @return string The translated string, according to the currently set locale {@link i18n::set_locale()}
2035
	 */
2036
	public static function _t($entity, $string = "", $context = "", $injection = "") {
0 ignored issues
show
Unused Code introduced by
The parameter $injection is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2037
		if(is_numeric($context) && in_array($context, array(PR_LOW, PR_MEDIUM, PR_HIGH))) {
2038
			Deprecation::notice(
2039
				'3.0',
2040
				'The $priority argument to _t() is deprecated, please use module inclusion priorities instead',
2041
				Deprecation::SCOPE_GLOBAL
2042
			);
2043
		}
2044
2045
		//fetch the injection array out of the parameters (if it is present)
2046
		$argList = func_get_args();
2047
		$argNum = func_num_args();
2048
		//_t($entity, $string = "", $context (optional), $injectionArray (optional))
2049
		$injectionArray = null;
2050
		for($i = 0; $i < $argNum; $i++) {
2051
			if (is_array($argList[$i])) {   //we have reached the injectionArray
2052
				$injectionArray = $argList[$i]; //any array in the args will be the injection array
2053
			}
2054
		}
2055
2056
		// get current locale (either default or user preference)
2057
		$locale = i18n::get_locale();
2058
		$lang = i18n::get_lang_from_locale($locale);
2059
2060
		// Only call getter if static isn't already defined (for performance reasons)
2061
		$translatorsByPrio = self::$translators;
2062
		if(!$translatorsByPrio) $translatorsByPrio = self::get_translators();
0 ignored issues
show
Bug Best Practice introduced by
The expression $translatorsByPrio of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2063
2064
		$returnValue = (is_string($string)) ? $string : ''; // Fall back to default string argument
2065
2066
		foreach($translatorsByPrio as $priority => $translators) {
2067
			foreach($translators as $name => $translator) {
2068
				$adapter = $translator->getAdapter();
2069
2070
				// at this point, we need to ensure the language and locale are loaded
2071
				// as include_by_locale() doesn't load a fallback.
2072
2073
				// TODO Remove reliance on global state, by refactoring into an i18nTranslatorManager
2074
				// which is instanciated by core with a $clean instance variable.
2075
2076
				if(!$adapter->isAvailable($lang)) {
2077
					i18n::include_by_locale($lang);
2078
				}
2079
2080
				if(!$adapter->isAvailable($locale)) {
2081
					i18n::include_by_locale($locale);
2082
				}
2083
2084
				$translation = $adapter->translate($entity, $locale);
2085
2086
					// Return translation only if we found a match thats not the entity itself (Zend fallback)
2087
				if($translation && $translation != $entity) {
2088
						$returnValue = $translation;
2089
					break 2;
2090
					}
2091
				}
2092
			}
2093
2094
		// inject the variables from injectionArray (if present)
2095
		if($injectionArray) {
2096
			$regex = '/\{[\w\d]*\}/i';
2097
			if(!preg_match($regex, $returnValue)) {
2098
				// Legacy mode: If no injection placeholders are found,
2099
				// replace sprintf placeholders in fixed order.
2100
				// Fail silently in case the translation is outdated
2101
				preg_match_all('/%[s,d]/', $returnValue, $returnValueArgs);
2102
				if($returnValueArgs) foreach($returnValueArgs[0] as $i => $returnValueArg) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $returnValueArgs of type string[][] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2103
					if($i >= count($injectionArray)) {
2104
						$injectionArray[] = '';
2105
					}
2106
				}
2107
				$replaced = vsprintf($returnValue, array_values($injectionArray));
2108
				if($replaced) $returnValue = $replaced;
2109
			} else if(!ArrayLib::is_associative($injectionArray)) {
2110
				// Legacy mode: If injection placeholders are found,
2111
				// but parameters are passed without names, replace them in fixed order.
2112
				$returnValue = preg_replace_callback(
2113
					$regex,
2114
					function($matches) use(&$injectionArray) {
0 ignored issues
show
Unused Code introduced by
The parameter $matches is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2115
						return $injectionArray ? array_shift($injectionArray) : '';
2116
					},
2117
					$returnValue
2118
				);
2119
			} else {
2120
				// Standard placeholder replacement with named injections and variable order.
2121
				foreach($injectionArray as $variable => $injection) {
2122
					$placeholder = '{'.$variable.'}';
2123
					$returnValue = str_replace($placeholder, $injection, $returnValue, $count);
2124
					if(!$count) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $count of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2125
						SS_Log::log(sprintf(
2126
							"Couldn't find placeholder '%s' in translation string '%s' (id: '%s')",
2127
							$placeholder,
2128
							$returnValue,
2129
							$entity
2130
						), SS_Log::NOTICE);
2131
					}
2132
				}
2133
			}
2134
		}
2135
2136
		return $returnValue;
2137
	}
2138
2139
2140
	/**
2141
	 * @return array Array of priority keys to instances of Zend_Translate, mapped by name.
2142
	 */
2143
	public static function get_translators() {
2144
		if(!Zend_Translate::getCache()) {
2145
			Zend_Translate::setCache(self::get_cache());
2146
		}
2147
2148
		if(!self::$translators) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::$translators of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2149
			$defaultPriority = 10;
2150
			self::$translators[$defaultPriority] = array(
2151
				'core' => new Zend_Translate(array(
2152
					'adapter' => 'i18nRailsYamlAdapter',
2153
					'locale' => Config::inst()->get('i18n', 'default_locale'),
2154
					'disableNotices' => true,
2155
				))
2156
			);
2157
2158
			i18n::include_by_locale('en');
2159
			i18n::include_by_locale('en_US');
2160
		}
2161
2162
		return self::$translators;
2163
	}
2164
2165
	/**
2166
	 * @param String
2167
	 * @return Zend_Translate
2168
	 */
2169
	public static function get_translator($name) {
2170
		foreach(self::get_translators() as $priority => $translators) {
2171
			if(isset($translators[$name])) return $translators[$name];
2172
		}
2173
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by i18n::get_translator of type Zend_Translate.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2174
	}
2175
2176
	/**
2177
	 * @param Zend_Translate Needs to implement {@link i18nTranslateAdapterInterface}
2178
	 * @param String If left blank will override the default translator.
2179
	 * @param Int
2180
	 */
2181
	public static function register_translator($translator, $name, $priority = 10) {
2182
		if (!is_int($priority)) throw new InvalidArgumentException("register_translator expects an int priority");
2183
2184
		// Ensure it's not there. If it is, we're replacing it. It may exist in a different priority.
2185
		self::unregister_translator($name);
2186
2187
		// Add our new translator
2188
		if(!isset(self::$translators[$priority])) self::$translators[$priority] = array();
2189
		self::$translators[$priority][$name] = $translator;
2190
2191
		// Resort array, ensuring highest priority comes first
2192
		krsort(self::$translators);
2193
2194
		i18n::include_by_locale('en_US');
2195
		i18n::include_by_locale('en');
2196
	}
2197
2198
	/**
2199
	 * @param String
2200
	 */
2201
	public static function unregister_translator($name) {
2202
		foreach (self::get_translators() as $priority => $translators) {
2203
			if (isset($translators[$name])) unset(self::$translators[$priority][$name]);
2204
		}
2205
	}
2206
2207
	/**
2208
	 * Get a list of commonly used languages
2209
	 *
2210
	 * @param boolean $native Use native names for languages instead of English ones
2211
	 * @return list of languages in the form 'code' => 'name'
2212
	 */
2213 View Code Duplication
	public static function get_common_languages($native = false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2214
		$languages = array();
2215
		foreach (Config::inst()->get('i18n', 'common_languages') as $code => $name) {
0 ignored issues
show
Bug introduced by
The expression \Config::inst()->get('i18n', 'common_languages') of type array|integer|double|string|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2216
			$languages[$code] = ($native ? $name['native'] : $name['name']);
2217
		}
2218
		return $languages;
2219
	}
2220
2221
	/**
2222
	 * Get a list of commonly used locales
2223
	 *
2224
	 * @param boolean $native Use native names for locale instead of English ones
2225
	 * @return list of languages in the form 'code' => 'name'
2226
	 */
2227 View Code Duplication
	public static function get_common_locales($native = false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2228
		$languages = array();
2229
		foreach (Config::inst()->get('i18n', 'common_locales') as $code => $name) {
0 ignored issues
show
Bug introduced by
The expression \Config::inst()->get('i18n', 'common_locales') of type array|integer|double|string|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2230
			$languages[$code] = ($native ? $name['native'] : $name['name']);
2231
		}
2232
		return $languages;
2233
	}
2234
2235
	/**
2236
	 * Get a list of locales (code => language and country)
2237
	 *
2238
	 * @deprecated since version 4.0
2239
	 * @return list of languages in the form 'code' => 'name'
2240
	 */
2241
	public static function get_locale_list() {
2242
		Deprecation::notice('4.0', 'Use the "i18n.all_locales" config setting instead');
2243
		return (array)Config::inst()->get('i18n', 'all_locales');
2244
	}
2245
2246
	/**
2247
	 * Matches a given locale with the closest translation available in the system
2248
	 *
2249
	 * @param string $locale locale code
2250
	 * @return string Locale of closest available translation, if available
2251
	 */
2252
	public static function get_closest_translation($locale) {
2253
2254
		// Check if exact match
2255
		$pool = self::get_existing_translations();
2256
		if(isset($pool[$locale])) return $locale;
2257
2258
		// Fallback to best locale for common language
2259
		$lang = self::get_lang_from_locale($locale);
2260
		$candidate = self::get_locale_from_lang($lang);
2261
		if(isset($pool[$candidate])) return $candidate;
2262
	}
2263
2264
	/**
2265
	 * Searches the root-directory for module-directories
2266
	 * (identified by having a _config.php on their first directory-level).
2267
	 * Finds locales by filename convention ("<locale>.<extension>", e.g. "de_AT.yml").
2268
	 *
2269
	 * @return array
2270
	 */
2271
	public static function get_existing_translations() {
2272
		$locales = array();
2273
2274
		// TODO Inspect themes
2275
		$modules = SS_ClassLoader::instance()->getManifest()->getModules();
2276
2277
		foreach($modules as $module) {
2278
			if(!file_exists("{$module}/lang/")) continue;
2279
2280
			$allLocales = Config::inst()->get('i18n', 'all_locales');
2281
			$moduleLocales = scandir("{$module}/lang/");
2282
			foreach($moduleLocales as $moduleLocale) {
2283
				$locale = pathinfo($moduleLocale, PATHINFO_FILENAME);
2284
				$ext = pathinfo($moduleLocale, PATHINFO_EXTENSION);
2285
				if($locale && in_array($ext, array('php','yml'))) {
2286
					// Normalize locale to include likely region tag, avoid repetition in locale labels
2287
					// TODO Replace with CLDR list of actually available languages/regions
2288
					// Only allow explicitly registered locales, otherwise we'll get into trouble
2289
					// if the locale doesn't exist in Zend's CLDR data
2290
					$fullLocale = self::get_locale_from_lang($locale);
2291
					if(isset($allLocales[$fullLocale])) {
2292
						$locales[$fullLocale] = $allLocales[$fullLocale];
2293
					}
2294
				}
2295
			}
2296
		}
2297
2298
		// sort by title (not locale)
2299
		asort($locales);
2300
2301
		return $locales;
2302
	}
2303
2304
	/**
2305
	 * Get a name from a language code (two characters, e.g. "en").
2306
	 *
2307
	 * @see get_locale_name()
2308
	 *
2309
	 * @param mixed $code Language code
2310
	 * @param boolean $native If true, the native name will be returned
2311
	 * @return Name of the language
2312
	 */
2313
	public static function get_language_name($code, $native = false) {
2314
		$langs = Config::inst()->get('i18n', 'common_languages');
2315
		if($native) {
2316
			return (isset($langs[$code]['native'])) ? $langs[$code]['native'] : false;
2317
		} else {
2318
			return (isset($langs[$code]['name'])) ? $langs[$code]['name'] : false;
2319
		}
2320
	}
2321
2322
	/**
2323
	 * Get a name from a locale code (xx_YY).
2324
	 *
2325
	 * @see get_language_name()
2326
	 *
2327
	 * @param mixed $code locale code
2328
	 * @return Name of the locale
2329
	 */
2330
	public static function get_locale_name($code) {
2331
		$langs = self::config()->all_locales;
0 ignored issues
show
Documentation introduced by
The property all_locales does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2332
		return isset($langs[$code]) ? $langs[$code] : false;
2333
	}
2334
2335
	/**
2336
	 * Get a code from an English language name
2337
	 *
2338
	 * @param mixed $name Name of the language
2339
	 * @return Language code (if the name is not found, it'll return the passed name)
2340
	 */
2341
	public static function get_language_code($name) {
2342
		$code = array_search($name,self::get_common_languages());
2343
		return ($code ? $code : $name);
2344
	}
2345
2346
	/**
2347
	 * Get the current tinyMCE language
2348
	 *
2349
	 * @return Language
2350
	 */
2351
	public static function get_tinymce_lang() {
2352
		$lang = Config::inst()->get('i18n', 'tinymce_lang');
2353
		if(isset($lang[self::get_locale()])) {
2354
			return $lang[self::get_locale()];
2355
		}
2356
2357
		return 'en';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return 'en'; (string) is incompatible with the return type documented by i18n::get_tinymce_lang of type Language.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2358
	}
2359
2360
	/**
2361
	 * Searches the root-directory for module-directories
2362
	 * (identified by having a _config.php on their first directory-level
2363
	 * and a language-file with the default locale in the /lang-subdirectory).
2364
	 *
2365
	 * @return array
2366
	 */
2367
	public static function get_translatable_modules() {
2368
		$translatableModules = array();
2369
2370
		$baseDir = Director::baseFolder();
2371
		$modules = scandir($baseDir);
2372
		foreach($modules as $module) {
2373
			$moduleDir = $baseDir . DIRECTORY_SEPARATOR . $module;
2374
			if(
2375
				is_dir($moduleDir)
2376
				&& is_file($moduleDir . DIRECTORY_SEPARATOR . "_config.php")
2377
				&& is_file($moduleDir . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR
2378
					. Config::inst()->get('i18n', 'default_locale') . ".php")
2379
			) {
2380
				$translatableModules[] = $module;
2381
			}
2382
		}
2383
		return $translatableModules;
2384
	}
2385
2386
	/**
2387
	 * Returns the "short" language name from a locale,
2388
	 * e.g. "en_US" would return "en".
2389
	 *
2390
	 * @param string $locale E.g. "en_US"
2391
	 * @return string Short language code, e.g. "en"
2392
	 */
2393
	public static function get_lang_from_locale($locale) {
2394
		return preg_replace('/(_|-).*/', '', $locale);
2395
	}
2396
2397
	/**
2398
	 * Provides you "likely locales"
2399
	 * for a given "short" language code. This is a guess,
2400
	 * as we can't disambiguate from e.g. "en" to "en_US" - it
2401
	 * could also mean "en_UK". Based on the Unicode CLDR
2402
	 * project.
2403
	 * @see http://www.unicode.org/cldr/data/charts/supplemental/likely_subtags.html
2404
	 *
2405
	 * @param string $lang Short language code, e.g. "en"
2406
	 * @return string Long locale, e.g. "en_US"
2407
	 */
2408
	public static function get_locale_from_lang($lang) {
2409
		$subtags = Config::inst()->get('i18n', 'likely_subtags');
2410
		if(preg_match('/\-|_/', $lang)) {
2411
			return str_replace('-', '_', $lang);
2412
		} else if(isset($subtags[$lang])) {
2413
			return $subtags[$lang];
2414
		} else {
2415
			return $lang . '_' . strtoupper($lang);
2416
		}
2417
	}
2418
2419
	/**
2420
	 * Gets a RFC 1766 compatible language code,
2421
	 * e.g. "en-US".
2422
	 *
2423
	 * @see http://www.ietf.org/rfc/rfc1766.txt
2424
	 * @see http://tools.ietf.org/html/rfc2616#section-3.10
2425
	 *
2426
	 * @param string $locale
2427
	 * @return string
2428
	 */
2429
	public static function convert_rfc1766($locale) {
2430
		return str_replace('_','-', $locale);
2431
	}
2432
2433
	/**
2434
	 * Given a PHP class name, finds the module where it's located.
2435
	 *
2436
	 * @param  string $name
2437
	 * @return string
2438
	 */
2439
	public static function get_owner_module($name) {
2440
		$manifest = SS_ClassLoader::instance()->getManifest();
2441
		$path     = $manifest->getItemPath($name);
2442
2443
		if (!$path) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $path of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2444
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by i18n::get_owner_module of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2445
		}
2446
2447
		$path = Director::makeRelative($path);
2448
		$path = str_replace('\\', '/', $path);
2449
2450
		$parts = explode('/', trim($path, '/'));
2451
		return array_shift($parts);
2452
	}
2453
2454
	/**
2455
	 * Validates a "long" locale format (e.g. "en_US")
2456
	 * by checking it against {@link $all_locales}.
2457
	 *
2458
	 * To add a locale to {@link $all_locales}, use the following example
2459
	 * in your mysite/_config.php:
2460
	 * <code>
2461
	 * i18n::$allowed_locales['xx_XX'] = '<Language name>';
2462
	 * </code>
2463
	 *
2464
	 * Note: Does not check for {@link $allowed_locales}.
2465
	 *
2466
	 * @return boolean
2467
	 */
2468
	public static function validate_locale($locale) {
2469
		// Convert en-US to en_US
2470
		$locale = str_replace('-', '_', $locale);
2471
		return (array_key_exists($locale, Config::inst()->get('i18n', 'all_locales')));
2472
	}
2473
2474
	/**
2475
	 * Set the current locale, used as the default for
2476
	 * any localized classes, such as {@link FormField} or {@link DBField}
2477
	 * instances. Locales can also be persisted in {@link Member->Locale},
2478
	 * for example in the {@link CMSMain} interface the Member locale
2479
	 * overrules the global locale value set here.
2480
	 *
2481
	 * @param string $locale Locale to be set. See
2482
	 *                       http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html for a list
2483
	 *                       of possible locales.
2484
	 */
2485
	public static function set_locale($locale) {
2486
		if ($locale) self::$current_locale = $locale;
2487
	}
2488
2489
	/**
2490
	 * Get the current locale.
2491
	 * Used by {@link Member::populateDefaults()}
2492
	 *
2493
	 * @return string Current locale in the system
2494
	 */
2495
	public static function get_locale() {
2496
		return (!empty(self::$current_locale)) ? self::$current_locale : Config::inst()->get('i18n', 'default_locale');
2497
	}
2498
2499
	/**
2500
	 * This is the "fallback locale", in case resources with the "current locale"
2501
	 * (set through {@link set_locale()}) can't be found.
2502
	 *
2503
	 * If you just want to globally read/write a different locale (e.g. in a CMS interface),
2504
	 * please use {@link get_locale()} and {@link set_locale()} instead.
2505
	 *
2506
	 * For example, {@link Requirements::add_i18n_javascript()} and {@link i18n::include_by_class()}
2507
	 * use this "fallback locale" value to include fallback language files.
2508
	 *
2509
	 * @deprecated since version 4.0; Use the "i18n.default_locale" config setting instead
2510
	 * @return String
2511
	 */
2512
	public static function default_locale() {
2513
		Deprecation::notice('4.0', 'Use the "i18n.default_locale" config setting instead');
2514
		return Config::inst()->get('i18n', 'default_locale');
2515
	}
2516
2517
	/**
2518
	 * See {@link default_locale()} for usage.
2519
	 *
2520
	 * @deprecated since version 4.0; Use the "i18n.default_locale" config setting instead
2521
	 * @param String $locale
2522
	 */
2523
	public static function set_default_locale($locale) {
2524
		Deprecation::notice('4.0', 'Use the "i18n.default_locale" config setting instead');
2525
		Config::inst()->update('i18n', 'default_locale', $locale);
2526
	}
2527
2528
	/**
2529
	 * Returns the script direction in format compatible with the HTML "dir" attribute.
2530
	 *
2531
	 * @see http://www.w3.org/International/tutorials/bidi-xhtml/
2532
	 * @param String $locale Optional locale incl. region (underscored)
2533
	 * @return String "rtl" or "ltr"
2534
	 */
2535
	public static function get_script_direction($locale = null) {
2536
		require_once 'Zend/Locale/Data.php';
2537
		if(!$locale) $locale = i18n::get_locale();
0 ignored issues
show
Bug Best Practice introduced by
The expression $locale of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2538
		try {
2539
			$dir = Zend_Locale_Data::getList($locale, 'layout');
2540
		} catch(Zend_Locale_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Zend_Locale_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
2541
			$dir = Zend_Locale_Data::getList(i18n::get_lang_from_locale($locale), 'layout');
2542
		}
2543
2544
		return ($dir && $dir['characters'] == 'right-to-left') ? 'rtl' : 'ltr';
2545
	}
2546
2547
	/**
2548
	 * Includes all available language files for a certain defined locale.
2549
	 *
2550
	 * @param string $locale All resources from any module in locale $locale will be loaded
2551
	 * @param Boolean $clean Clean old caches?
2552
	 */
2553
	public static function include_by_locale($locale, $clean = false) {
2554
		if($clean) {
2555
			self::flush();
2556
		}
2557
2558
		// Get list of module => path pairs, and then just the names
2559
		$modules = SS_ClassLoader::instance()->getManifest()->getModules();
2560
		$moduleNames = array_keys($modules);
2561
2562
		// Remove the "project" module from the list - we'll add it back specially later if needed
2563
		global $project;
2564
		if (($idx = array_search($project, $moduleNames)) !== false) array_splice($moduleNames, $idx, 1);
2565
2566
		// Get the order from the config syste,
2567
		$order = Config::inst()->get('i18n', 'module_priority');
2568
2569
		// Find all modules that don't have their order specified by the config system
2570
		$unspecified = array_diff($moduleNames, $order);
2571
2572
		// If the placeholder "other_modules" exists in the order array, replace it by the unspecified modules
2573
		if (($idx = array_search('other_modules', $order)) !== false) array_splice($order, $idx,  1, $unspecified);
2574
		// Otherwise just jam them on the front
2575
		else array_splice($order, 0, 0, $unspecified);
2576
2577
		// Put the project module back in at the begining if it wasn't specified by the config system
2578
		if (!in_array($project, $order)) array_unshift($order, $project);
2579
2580
		$sortedModules = array();
2581
		foreach ($order as $module) {
2582
			if (isset($modules[$module])) $sortedModules[$module] = $modules[$module];
2583
		}
2584
		$sortedModules = array_reverse($sortedModules, true);
2585
2586
		// Loop in reverse order, meaning the translator with the highest priority goes first
2587
		$translators = array_reverse(self::get_translators(), true);
2588
		foreach($translators as $priority => $translators) {
2589
			foreach($translators as $name => $translator) {
2590
				$adapter = $translator->getAdapter();
2591
2592
				// Load translations from modules
2593
				foreach($sortedModules as $module) {
2594
					$filename = $adapter->getFilenameForLocale($locale);
2595
					$filepath = "{$module}/lang/" . $filename;
2596
2597
					if($filename && !file_exists($filepath)) continue;
2598
					$adapter->addTranslation(
2599
						array('content' => $filepath, 'locale' => $locale)
2600
					);
2601
				}
2602
2603
				// Load translations from themes
2604
				// TODO Replace with theme listing once implemented in TemplateManifest
2605
				$themesBase = Director::baseFolder() . '/themes';
2606
				if(is_dir($themesBase)) {
2607
					foreach(scandir($themesBase) as $theme) {
2608
						if(
2609
							strpos($theme, Config::inst()->get('SSViewer', 'theme')) === 0
2610
							&& file_exists("{$themesBase}/{$theme}/lang/")
2611
						) {
2612
							$filename = $adapter->getFilenameForLocale($locale);
2613
							$filepath = "{$themesBase}/{$theme}/lang/" . $filename;
2614
							if($filename && !file_exists($filepath)) continue;
2615
							$adapter->addTranslation(
2616
								array('content' => $filepath, 'locale' => $locale)
2617
							);
2618
						}
2619
					}
2620
				}
2621
2622
				// Add empty translations to ensure the locales are "registered" with isAvailable(),
2623
				// and the next invocation of include_by_locale() doesn't cause a new reparse.
2624
				$adapter->addTranslation(
2625
					array(
2626
						// Cached by content hash, so needs to be locale dependent
2627
						'content' => array($locale => $locale),
2628
						'locale' => $locale,
2629
						'usetranslateadapter' => true
2630
					)
2631
				);
2632
			}
2633
		}
2634
	}
2635
2636
	/**
2637
	 * Given a class name (a "locale namespace"), will search for its module and, if available,
2638
	 * will load the resources for the currently defined locale.
2639
	 * If not available, the original English resource will be loaded instead (to avoid blanks)
2640
	 *
2641
	 * @param string $class Resources for this class will be included, according to the set locale.
2642
	 */
2643
	public static function include_by_class($class) {
2644
		$module = self::get_owner_module($class);
2645
2646
		$translators = array_reverse(self::get_translators(), true);
2647
		foreach($translators as $priority => $translators) {
2648
			foreach($translators as $name => $translator) {
2649
				$adapter = $translator->getAdapter();
2650
				$filename = $adapter->getFilenameForLocale(self::get_locale());
2651
				$filepath = "{$module}/lang/" . $filename;
2652
				if($filename && !file_exists($filepath)) continue;
2653
				$adapter->addTranslation(array(
2654
					'content' => $filepath,
2655
					'locale' => self::get_locale()
2656
				));
2657
			}
2658
		}
2659
	}
2660
2661
	public static function get_template_global_variables() {
2662
		return array(
2663
			'i18nLocale' => 'get_locale',
2664
			'get_locale',
2665
			'i18nScriptDirection' => 'get_script_direction',
2666
		);
2667
	}
2668
2669
}
2670