Remove some useless comments
[phpmyadmin.git] / libraries / classes / LanguageManager.php
blob8e4ac1650c2822f6cd4a35f538f5b88a13df6fbb
1 <?php
3 declare(strict_types=1);
5 namespace PhpMyAdmin;
7 use PhpMyAdmin\Html\MySQLDocumentation;
8 use const E_USER_ERROR;
9 use function closedir;
10 use function count;
11 use function explode;
12 use function file_exists;
13 use function is_dir;
14 use function opendir;
15 use function preg_grep;
16 use function readdir;
17 use function strtolower;
18 use function trigger_error;
19 use function uasort;
20 use function ucfirst;
22 /**
23 * Language selection manager
25 class LanguageManager
27 /**
28 * Definition data for languages
30 * Each member contains:
31 * - Language code
32 * - English language name
33 * - Native language name
34 * - Match regular expression
35 * - MySQL locale
37 * @var array
39 private static $languageData = [
40 'af' => [
41 'af',
42 'Afrikaans',
43 '',
44 'af|afrikaans',
45 '',
47 'am' => [
48 'am',
49 'Amharic',
50 'አማርኛ',
51 'am|amharic',
52 '',
54 'ar' => [
55 'ar',
56 'Arabic',
57 '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;',
58 'ar|arabic',
59 'ar_AE',
61 'ar_ly' => [
62 'ar_LY',
63 'Arabic (Libya)',
64 'ليبي',
65 'ar[_-]ly|arabic (libya)|libian arabic',
66 'ar_LY',
68 'az' => [
69 'az',
70 'Azerbaijani',
71 'Az&#601;rbaycanca',
72 'az|azerbaijani',
73 '',
75 'bn' => [
76 'bn',
77 'Bangla',
78 'বাংলা',
79 'bn|bangla',
80 '',
82 'be' => [
83 'be',
84 'Belarusian',
85 '&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103;',
86 'be|belarusian',
87 'be_BY',
89 'be@latin' => [
90 'be@latin',
91 'Belarusian (latin)',
92 'Bie&#0322;aruskaja',
93 'be[-_]lat|be@latin|belarusian latin',
94 '',
96 'ber' => [
97 'ber',
98 'Berber',
99 'Tamaziɣt',
100 'ber|berber',
103 'bg' => [
104 'bg',
105 'Bulgarian',
106 '&#1041;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;',
107 'bg|bulgarian',
108 'bg_BG',
110 'bs' => [
111 'bs',
112 'Bosnian',
113 'Bosanski',
114 'bs|bosnian',
117 'br' => [
118 'br',
119 'Breton',
120 'Brezhoneg',
121 'br|breton',
124 'brx' => [
125 'brx',
126 'Bodo',
127 'बड़ो',
128 'brx|bodo',
131 'ca' => [
132 'ca',
133 'Catalan',
134 'Catal&agrave;',
135 'ca|catalan',
136 'ca_ES',
138 'ckb' => [
139 'ckb',
140 'Sorani',
141 'سۆرانی',
142 'ckb|sorani',
145 'cs' => [
146 'cs',
147 'Czech',
148 'Čeština',
149 'cs|czech',
150 'cs_CZ',
152 'cy' => [
153 'cy',
154 'Welsh',
155 'Cymraeg',
156 'cy|welsh',
159 'da' => [
160 'da',
161 'Danish',
162 'Dansk',
163 'da|danish',
164 'da_DK',
166 'de' => [
167 'de',
168 'German',
169 'Deutsch',
170 'de|german',
171 'de_DE',
173 'el' => [
174 'el',
175 'Greek',
176 '&Epsilon;&lambda;&lambda;&eta;&nu;&iota;&kappa;&#940;',
177 'el|greek',
180 'en' => [
181 'en',
182 'English',
184 'en|english',
185 'en_US',
187 'en_gb' => [
188 'en_GB',
189 'English (United Kingdom)',
191 'en[_-]gb|english (United Kingdom)',
192 'en_GB',
194 'eo' => [
195 'eo',
196 'Esperanto',
197 'Esperanto',
198 'eo|esperanto',
201 'es' => [
202 'es',
203 'Spanish',
204 'Espa&ntilde;ol',
205 'es|spanish',
206 'es_ES',
208 'et' => [
209 'et',
210 'Estonian',
211 'Eesti',
212 'et|estonian',
213 'et_EE',
215 'eu' => [
216 'eu',
217 'Basque',
218 'Euskara',
219 'eu|basque',
220 'eu_ES',
222 'fa' => [
223 'fa',
224 'Persian',
225 '&#1601;&#1575;&#1585;&#1587;&#1740;',
226 'fa|persian',
229 'fi' => [
230 'fi',
231 'Finnish',
232 'Suomi',
233 'fi|finnish',
234 'fi_FI',
236 'fil' => [
237 'fil',
238 'Filipino',
239 'Pilipino',
240 'fil|filipino',
243 'fr' => [
244 'fr',
245 'French',
246 'Fran&ccedil;ais',
247 'fr|french',
248 'fr_FR',
250 'fy' => [
251 'fy',
252 'Frisian',
253 'Frysk',
254 'fy|frisian',
257 'gl' => [
258 'gl',
259 'Galician',
260 'Galego',
261 'gl|galician',
262 'gl_ES',
264 'gu' => [
265 'gu',
266 'Gujarati',
267 'ગુજરાતી',
268 'gu|gujarati',
269 'gu_IN',
271 'he' => [
272 'he',
273 'Hebrew',
274 '&#1506;&#1489;&#1512;&#1497;&#1514;',
275 'he|hebrew',
276 'he_IL',
278 'hi' => [
279 'hi',
280 'Hindi',
281 '&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;',
282 'hi|hindi',
283 'hi_IN',
285 'hr' => [
286 'hr',
287 'Croatian',
288 'Hrvatski',
289 'hr|croatian',
290 'hr_HR',
292 'hu' => [
293 'hu',
294 'Hungarian',
295 'Magyar',
296 'hu|hungarian',
297 'hu_HU',
299 'hy' => [
300 'hy',
301 'Armenian',
302 'Հայերէն',
303 'hy|armenian',
306 'ia' => [
307 'ia',
308 'Interlingua',
310 'ia|interlingua',
313 'id' => [
314 'id',
315 'Indonesian',
316 'Bahasa Indonesia',
317 'id|indonesian',
318 'id_ID',
320 'ig' => [
321 'ig',
322 'Igbo',
323 'Asụsụ Igbo',
324 'ig|igbo',
327 'it' => [
328 'it',
329 'Italian',
330 'Italiano',
331 'it|italian',
332 'it_IT',
334 'ja' => [
335 'ja',
336 'Japanese',
337 '&#26085;&#26412;&#35486;',
338 'ja|japanese',
339 'ja_JP',
341 'ko' => [
342 'ko',
343 'Korean',
344 '&#54620;&#44397;&#50612;',
345 'ko|korean',
346 'ko_KR',
348 'ka' => [
349 'ka',
350 'Georgian',
351 '&#4325;&#4304;&#4320;&#4311;&#4323;&#4314;&#4312;',
352 'ka|georgian',
355 'kab' => [
356 'kab',
357 'Kabylian',
358 'Taqbaylit',
359 'kab|kabylian',
362 'kk' => [
363 'kk',
364 'Kazakh',
365 'Қазақ',
366 'kk|kazakh',
369 'km' => [
370 'km',
371 'Khmer',
372 'ខ្មែរ',
373 'km|khmer',
376 'kn' => [
377 'kn',
378 'Kannada',
379 'ಕನ್ನಡ',
380 'kn|kannada',
383 'ksh' => [
384 'ksh',
385 'Colognian',
386 'Kölsch',
387 'ksh|colognian',
390 'ku' => [
391 'ku',
392 'Kurdish',
393 'کوردی',
394 'ku|kurdish',
397 'ky' => [
398 'ky',
399 'Kyrgyz',
400 'Кыргызча',
401 'ky|kyrgyz',
404 'li' => [
405 'li',
406 'Limburgish',
407 'Lèmbörgs',
408 'li|limburgish',
411 'lt' => [
412 'lt',
413 'Lithuanian',
414 'Lietuvi&#371;',
415 'lt|lithuanian',
416 'lt_LT',
418 'lv' => [
419 'lv',
420 'Latvian',
421 'Latvie&scaron;u',
422 'lv|latvian',
423 'lv_LV',
425 'mk' => [
426 'mk',
427 'Macedonian',
428 'Macedonian',
429 'mk|macedonian',
430 'mk_MK',
432 'ml' => [
433 'ml',
434 'Malayalam',
435 'Malayalam',
436 'ml|malayalam',
439 'mn' => [
440 'mn',
441 'Mongolian',
442 '&#1052;&#1086;&#1085;&#1075;&#1086;&#1083;',
443 'mn|mongolian',
444 'mn_MN',
446 'ms' => [
447 'ms',
448 'Malay',
449 'Bahasa Melayu',
450 'ms|malay',
451 'ms_MY',
453 'my' => [
454 'my',
455 'Burmese',
456 'မြန်မာ',
457 'my|burmese',
460 'ne' => [
461 'ne',
462 'Nepali',
463 'नेपाली',
464 'ne|nepali',
467 'nb' => [
468 'nb',
469 'Norwegian',
470 'Norsk',
471 'nb|norwegian',
472 'nb_NO',
474 'nn' => [
475 'nn',
476 'Norwegian Nynorsk',
477 'Nynorsk',
478 'nn|nynorsk',
479 'nn_NO',
481 'nl' => [
482 'nl',
483 'Dutch',
484 'Nederlands',
485 'nl|dutch',
486 'nl_NL',
488 'pa' => [
489 'pa',
490 'Punjabi',
491 'ਪੰਜਾਬੀ',
492 'pa|punjabi',
495 'pl' => [
496 'pl',
497 'Polish',
498 'Polski',
499 'pl|polish',
500 'pl_PL',
502 'pt' => [
503 'pt',
504 'Portuguese',
505 'Portugu&ecirc;s',
506 'pt|portuguese',
507 'pt_PT',
509 'pt_br' => [
510 'pt_BR',
511 'Portuguese (Brazil)',
512 'Portugu&ecirc;s (Brasil)',
513 'pt[-_]br|portuguese (brazil)',
514 'pt_BR',
516 'rcf' => [
517 'rcf',
518 'R&eacute;union Creole',
519 'Kr&eacute;ol',
520 'rcf|creole (reunion)',
523 'ro' => [
524 'ro',
525 'Romanian',
526 'Rom&acirc;n&#259;',
527 'ro|romanian',
528 'ro_RO',
530 'ru' => [
531 'ru',
532 'Russian',
533 '&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;',
534 'ru|russian',
535 'ru_RU',
537 'si' => [
538 'si',
539 'Sinhala',
540 '&#3523;&#3538;&#3458;&#3524;&#3517;',
541 'si|sinhala',
544 'sk' => [
545 'sk',
546 'Slovak',
547 'Sloven&#269;ina',
548 'sk|slovak',
549 'sk_SK',
551 'sl' => [
552 'sl',
553 'Slovenian',
554 'Sloven&scaron;&#269;ina',
555 'sl|slovenian',
556 'sl_SI',
558 'sq' => [
559 'sq',
560 'Albanian',
561 'Shqip',
562 'sq|albanian',
563 'sq_AL',
565 'sr@latin' => [
566 'sr@latin',
567 'Serbian (latin)',
568 'Srpski',
569 'sr[-_]lat|sr@latin|serbian latin',
570 'sr_YU',
572 'sr' => [
573 'sr',
574 'Serbian',
575 '&#1057;&#1088;&#1087;&#1089;&#1082;&#1080;',
576 'sr|serbian',
577 'sr_YU',
579 'sv' => [
580 'sv',
581 'Swedish',
582 'Svenska',
583 'sv|swedish',
584 'sv_SE',
586 'ta' => [
587 'ta',
588 'Tamil',
589 'தமிழ்',
590 'ta|tamil',
591 'ta_IN',
593 'te' => [
594 'te',
595 'Telugu',
596 'తెలుగు',
597 'te|telugu',
598 'te_IN',
600 'th' => [
601 'th',
602 'Thai',
603 '&#3616;&#3634;&#3625;&#3634;&#3652;&#3607;&#3618;',
604 'th|thai',
605 'th_TH',
607 'tk' => [
608 'tk',
609 'Turkmen',
610 'Türkmençe',
611 'tk|turkmen',
614 'tr' => [
615 'tr',
616 'Turkish',
617 'T&uuml;rk&ccedil;e',
618 'tr|turkish',
619 'tr_TR',
621 'tt' => [
622 'tt',
623 'Tatarish',
624 'Tatar&ccedil;a',
625 'tt|tatarish',
628 'ug' => [
629 'ug',
630 'Uyghur',
631 'ئۇيغۇرچە',
632 'ug|uyghur',
635 'uk' => [
636 'uk',
637 'Ukrainian',
638 '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;',
639 'uk|ukrainian',
640 'uk_UA',
642 'ur' => [
643 'ur',
644 'Urdu',
645 'اُردوُ',
646 'ur|urdu',
647 'ur_PK',
649 'uz@latin' => [
650 'uz@latin',
651 'Uzbek (latin)',
652 'O&lsquo;zbekcha',
653 'uz[-_]lat|uz@latin|uzbek-latin',
656 'uz' => [
657 'uz',
658 'Uzbek (cyrillic)',
659 '&#1038;&#1079;&#1073;&#1077;&#1082;&#1095;&#1072;',
660 'uz[-_]cyr|uz@cyrillic|uzbek-cyrillic',
663 'vi' => [
664 'vi',
665 'Vietnamese',
666 'Tiếng Việt',
667 'vi|vietnamese',
668 'vi_VN',
670 'vls' => [
671 'vls',
672 'Flemish',
673 'West-Vlams',
674 'vls|flemish',
677 'zh_tw' => [
678 'zh_TW',
679 'Chinese traditional',
680 '&#20013;&#25991;',
681 'zh[-_](tw|hk)|chinese traditional',
682 'zh_TW',
684 // only TW and HK use traditional Chinese while others (CN, SG, MY)
685 // use simplified Chinese
686 'zh_cn' => [
687 'zh_CN',
688 'Chinese simplified',
689 '&#20013;&#25991;',
690 'zh(?![-_](tw|hk))([-_][[:alpha:]]{2,3})?|chinese simplified',
691 'zh_CN',
695 /** @var array */
696 private $availableLocales;
698 /** @var array */
699 private $availableLanguages = [];
701 /** @var bool */
702 private $langFailedConfig = false;
704 /** @var bool */
705 private $langFailedCookie = false;
707 /** @var bool */
708 private $langFailedRequest = false;
710 /** @var LanguageManager */
711 private static $instance;
714 * Returns LanguageManager singleton
716 * @return LanguageManager
718 public static function getInstance()
720 if (self::$instance === null) {
721 self::$instance = new LanguageManager();
724 return self::$instance;
728 * Returns list of available locales
730 * @return array
732 public function listLocaleDir()
734 $result = ['en'];
736 /* Check for existing directory */
737 if (! is_dir(LOCALE_PATH)) {
738 return $result;
741 /* Open the directory */
742 $handle = @opendir(LOCALE_PATH);
743 /* This can happen if the kit is English-only */
744 if ($handle === false) {
745 return $result;
748 /* Process all files */
749 while (($file = readdir($handle)) !== false) {
750 $path = LOCALE_PATH
751 . '/' . $file
752 . '/LC_MESSAGES/phpmyadmin.mo';
753 if ($file === '.'
754 || $file === '..'
755 || ! @file_exists($path)
757 continue;
760 $result[] = $file;
762 /* Close the handle */
763 closedir($handle);
765 return $result;
769 * Returns (cached) list of all available locales
771 * @return array of strings
773 public function availableLocales()
775 if (! $this->availableLocales) {
776 if (! isset($GLOBALS['PMA_Config']) || empty($GLOBALS['PMA_Config']->get('FilterLanguages'))) {
777 $this->availableLocales = $this->listLocaleDir();
778 } else {
779 $this->availableLocales = preg_grep(
780 '@' . $GLOBALS['PMA_Config']->get('FilterLanguages') . '@',
781 $this->listLocaleDir()
786 return $this->availableLocales;
790 * Checks whether there are some languages available
792 * @return bool
794 public function hasChoice()
796 return count($this->availableLanguages()) > 1;
800 * Returns (cached) list of all available languages
802 * @return Language[] array of Language objects
804 public function availableLanguages()
806 if (! $this->availableLanguages) {
807 $this->availableLanguages = [];
809 foreach ($this->availableLocales() as $lang) {
810 $lang = strtolower($lang);
811 if (isset(static::$languageData[$lang])) {
812 $data = static::$languageData[$lang];
813 $this->availableLanguages[$lang] = new Language(
814 $data[0],
815 $data[1],
816 $data[2],
817 $data[3],
818 $data[4]
820 } else {
821 $this->availableLanguages[$lang] = new Language(
822 $lang,
823 ucfirst($lang),
824 ucfirst($lang),
825 $lang,
832 return $this->availableLanguages;
836 * Returns (cached) list of all available languages sorted
837 * by name
839 * @return Language[] array of Language objects
841 public function sortedLanguages()
843 $this->availableLanguages();
844 uasort($this->availableLanguages, static function (Language $a, Language $b) {
845 return $a->cmp($b);
848 return $this->availableLanguages;
852 * Return Language object for given code
854 * @param string $code Language code
856 * @return Language|false Language object or false on failure
858 public function getLanguage($code)
860 $code = strtolower($code);
861 $langs = $this->availableLanguages();
862 if (isset($langs[$code])) {
863 return $langs[$code];
866 return false;
870 * Return currently active Language object
872 * @return Language Language object
874 public function getCurrentLanguage()
876 return $this->availableLanguages[strtolower($GLOBALS['lang'])];
880 * Activates language based on configuration, user preferences or
881 * browser
883 * @return Language
885 public function selectLanguage()
887 // check forced language
888 if (! empty($GLOBALS['PMA_Config']->get('Lang'))) {
889 $lang = $this->getLanguage($GLOBALS['PMA_Config']->get('Lang'));
890 if ($lang !== false) {
891 return $lang;
893 $this->langFailedConfig = true;
896 // Don't use REQUEST in following code as it might be confused by cookies
897 // with same name. Check user requested language (POST)
898 if (! empty($_POST['lang'])) {
899 $lang = $this->getLanguage($_POST['lang']);
900 if ($lang !== false) {
901 return $lang;
903 $this->langFailedRequest = true;
906 // check user requested language (GET)
907 if (! empty($_GET['lang'])) {
908 $lang = $this->getLanguage($_GET['lang']);
909 if ($lang !== false) {
910 return $lang;
912 $this->langFailedRequest = true;
915 // check previous set language
916 if (! empty($GLOBALS['PMA_Config']->getCookie('pma_lang'))) {
917 $lang = $this->getLanguage($GLOBALS['PMA_Config']->getCookie('pma_lang'));
918 if ($lang !== false) {
919 return $lang;
921 $this->langFailedCookie = true;
924 $langs = $this->availableLanguages();
926 // try to find out user's language by checking its HTTP_ACCEPT_LANGUAGE variable;
927 $accepted_languages = Core::getenv('HTTP_ACCEPT_LANGUAGE');
928 if ($accepted_languages) {
929 foreach (explode(',', $accepted_languages) as $header) {
930 foreach ($langs as $language) {
931 if ($language->matchesAcceptLanguage($header)) {
932 return $language;
938 // try to find out user's language by checking its HTTP_USER_AGENT variable
939 $user_agent = Core::getenv('HTTP_USER_AGENT');
940 if (! empty($user_agent)) {
941 foreach ($langs as $language) {
942 if ($language->matchesUserAgent($user_agent)) {
943 return $language;
948 // Didn't catch any valid lang : we use the default settings
949 if (isset($langs[$GLOBALS['PMA_Config']->get('DefaultLang')])) {
950 return $langs[$GLOBALS['PMA_Config']->get('DefaultLang')];
953 // Fallback to English
954 return $langs['en'];
958 * Displays warnings about invalid languages. This needs to be postponed
959 * to show messages at time when language is initialized.
961 * @return void
963 public function showWarnings()
965 // now, that we have loaded the language strings we can send the errors
966 if (! $this->langFailedConfig
967 && ! $this->langFailedCookie
968 && ! $this->langFailedRequest
970 return;
973 trigger_error(
974 __('Ignoring unsupported language code.'),
975 E_USER_ERROR
980 * Returns HTML code for the language selector
982 * @param Template $template Template instance
983 * @param bool $use_fieldset whether to use fieldset for selection
984 * @param bool $show_doc whether to show documentation links
986 * @return string
988 * @access public
990 public function getSelectorDisplay(Template $template, $use_fieldset = false, $show_doc = true)
992 $_form_params = [
993 'db' => $GLOBALS['db'],
994 'table' => $GLOBALS['table'],
997 // For non-English, display "Language" with emphasis because it's
998 // not a proper word in the current language; we show it to help
999 // people recognize the dialog
1000 $language_title = __('Language')
1001 . (__('Language') !== 'Language' ? ' - <em>Language</em>' : '');
1002 if ($show_doc) {
1003 $language_title .= MySQLDocumentation::showDocumentation('faq', 'faq7-2');
1006 $available_languages = $this->sortedLanguages();
1008 return $template->render('select_lang', [
1009 'language_title' => $language_title,
1010 'use_fieldset' => $use_fieldset,
1011 'available_languages' => $available_languages,
1012 '_form_params' => $_form_params,