Translated using Weblate (Slovenian)
[phpmyadmin.git] / libraries / classes / LanguageManager.php
blob243750e9c93c2eda1abb4dbe1b6e6b7abb1e2d6a
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 'tzm' => [
629 'tzm',
630 'Central Atlas Tamazight',
631 'Tamaziɣt',
632 'tzm|central atlas tamazight',
635 'ug' => [
636 'ug',
637 'Uyghur',
638 'ئۇيغۇرچە',
639 'ug|uyghur',
642 'uk' => [
643 'uk',
644 'Ukrainian',
645 '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;',
646 'uk|ukrainian',
647 'uk_UA',
649 'ur' => [
650 'ur',
651 'Urdu',
652 'اُردوُ',
653 'ur|urdu',
654 'ur_PK',
656 'uz@latin' => [
657 'uz@latin',
658 'Uzbek (latin)',
659 'O&lsquo;zbekcha',
660 'uz[-_]lat|uz@latin|uzbek-latin',
663 'uz' => [
664 'uz',
665 'Uzbek (cyrillic)',
666 '&#1038;&#1079;&#1073;&#1077;&#1082;&#1095;&#1072;',
667 'uz[-_]cyr|uz@cyrillic|uzbek-cyrillic',
670 'vi' => [
671 'vi',
672 'Vietnamese',
673 'Tiếng Việt',
674 'vi|vietnamese',
675 'vi_VN',
677 'vls' => [
678 'vls',
679 'Flemish',
680 'West-Vlams',
681 'vls|flemish',
684 'zh_tw' => [
685 'zh_TW',
686 'Chinese traditional',
687 '&#20013;&#25991;',
688 'zh[-_](tw|hk)|chinese traditional',
689 'zh_TW',
691 // only TW and HK use traditional Chinese while others (CN, SG, MY)
692 // use simplified Chinese
693 'zh_cn' => [
694 'zh_CN',
695 'Chinese simplified',
696 '&#20013;&#25991;',
697 'zh(?![-_](tw|hk))([-_][[:alpha:]]{2,3})?|chinese simplified',
698 'zh_CN',
702 /** @var array */
703 private $availableLocales;
705 /** @var array */
706 private $availableLanguages = [];
708 /** @var bool */
709 private $langFailedConfig = false;
711 /** @var bool */
712 private $langFailedCookie = false;
714 /** @var bool */
715 private $langFailedRequest = false;
717 /** @var LanguageManager */
718 private static $instance;
721 * Returns LanguageManager singleton
723 * @return LanguageManager
725 public static function getInstance()
727 if (self::$instance === null) {
728 self::$instance = new LanguageManager();
731 return self::$instance;
735 * Returns list of available locales
737 * @return array
739 public function listLocaleDir()
741 $result = ['en'];
743 /* Check for existing directory */
744 if (! is_dir(LOCALE_PATH)) {
745 return $result;
748 /* Open the directory */
749 $handle = @opendir(LOCALE_PATH);
750 /* This can happen if the kit is English-only */
751 if ($handle === false) {
752 return $result;
755 /* Process all files */
756 while (($file = readdir($handle)) !== false) {
757 $path = LOCALE_PATH
758 . '/' . $file
759 . '/LC_MESSAGES/phpmyadmin.mo';
760 if ($file === '.'
761 || $file === '..'
762 || ! @file_exists($path)
764 continue;
767 $result[] = $file;
769 /* Close the handle */
770 closedir($handle);
772 return $result;
776 * Returns (cached) list of all available locales
778 * @return array of strings
780 public function availableLocales()
782 if (! $this->availableLocales) {
783 if (! isset($GLOBALS['PMA_Config']) || empty($GLOBALS['PMA_Config']->get('FilterLanguages'))) {
784 $this->availableLocales = $this->listLocaleDir();
785 } else {
786 $this->availableLocales = preg_grep(
787 '@' . $GLOBALS['PMA_Config']->get('FilterLanguages') . '@',
788 $this->listLocaleDir()
793 return $this->availableLocales;
797 * Checks whether there are some languages available
799 * @return bool
801 public function hasChoice()
803 return count($this->availableLanguages()) > 1;
807 * Returns (cached) list of all available languages
809 * @return Language[] array of Language objects
811 public function availableLanguages()
813 if (! $this->availableLanguages) {
814 $this->availableLanguages = [];
816 foreach ($this->availableLocales() as $lang) {
817 $lang = strtolower($lang);
818 if (isset(static::$languageData[$lang])) {
819 $data = static::$languageData[$lang];
820 $this->availableLanguages[$lang] = new Language(
821 $data[0],
822 $data[1],
823 $data[2],
824 $data[3],
825 $data[4]
827 } else {
828 $this->availableLanguages[$lang] = new Language(
829 $lang,
830 ucfirst($lang),
831 ucfirst($lang),
832 $lang,
839 return $this->availableLanguages;
843 * Returns (cached) list of all available languages sorted
844 * by name
846 * @return Language[] array of Language objects
848 public function sortedLanguages()
850 $this->availableLanguages();
851 uasort($this->availableLanguages, static function (Language $a, Language $b) {
852 return $a->cmp($b);
855 return $this->availableLanguages;
859 * Return Language object for given code
861 * @param string $code Language code
863 * @return Language|false Language object or false on failure
865 public function getLanguage($code)
867 $code = strtolower($code);
868 $langs = $this->availableLanguages();
869 if (isset($langs[$code])) {
870 return $langs[$code];
873 return false;
877 * Return currently active Language object
879 * @return Language Language object
881 public function getCurrentLanguage()
883 return $this->availableLanguages[strtolower($GLOBALS['lang'])];
887 * Activates language based on configuration, user preferences or
888 * browser
890 * @return Language
892 public function selectLanguage()
894 // check forced language
895 if (! empty($GLOBALS['PMA_Config']->get('Lang'))) {
896 $lang = $this->getLanguage($GLOBALS['PMA_Config']->get('Lang'));
897 if ($lang !== false) {
898 return $lang;
900 $this->langFailedConfig = true;
903 // Don't use REQUEST in following code as it might be confused by cookies
904 // with same name. Check user requested language (POST)
905 if (! empty($_POST['lang'])) {
906 $lang = $this->getLanguage($_POST['lang']);
907 if ($lang !== false) {
908 return $lang;
910 $this->langFailedRequest = true;
913 // check user requested language (GET)
914 if (! empty($_GET['lang'])) {
915 $lang = $this->getLanguage($_GET['lang']);
916 if ($lang !== false) {
917 return $lang;
919 $this->langFailedRequest = true;
922 // check previous set language
923 if (! empty($GLOBALS['PMA_Config']->getCookie('pma_lang'))) {
924 $lang = $this->getLanguage($GLOBALS['PMA_Config']->getCookie('pma_lang'));
925 if ($lang !== false) {
926 return $lang;
928 $this->langFailedCookie = true;
931 $langs = $this->availableLanguages();
933 // try to find out user's language by checking its HTTP_ACCEPT_LANGUAGE variable;
934 $accepted_languages = Core::getenv('HTTP_ACCEPT_LANGUAGE');
935 if ($accepted_languages) {
936 foreach (explode(',', $accepted_languages) as $header) {
937 foreach ($langs as $language) {
938 if ($language->matchesAcceptLanguage($header)) {
939 return $language;
945 // try to find out user's language by checking its HTTP_USER_AGENT variable
946 $user_agent = Core::getenv('HTTP_USER_AGENT');
947 if (! empty($user_agent)) {
948 foreach ($langs as $language) {
949 if ($language->matchesUserAgent($user_agent)) {
950 return $language;
955 // Didn't catch any valid lang : we use the default settings
956 if (isset($langs[$GLOBALS['PMA_Config']->get('DefaultLang')])) {
957 return $langs[$GLOBALS['PMA_Config']->get('DefaultLang')];
960 // Fallback to English
961 return $langs['en'];
965 * Displays warnings about invalid languages. This needs to be postponed
966 * to show messages at time when language is initialized.
968 * @return void
970 public function showWarnings()
972 // now, that we have loaded the language strings we can send the errors
973 if (! $this->langFailedConfig
974 && ! $this->langFailedCookie
975 && ! $this->langFailedRequest
977 return;
980 trigger_error(
981 __('Ignoring unsupported language code.'),
982 E_USER_ERROR
987 * Returns HTML code for the language selector
989 * @param Template $template Template instance
990 * @param bool $use_fieldset whether to use fieldset for selection
991 * @param bool $show_doc whether to show documentation links
993 * @return string
995 * @access public
997 public function getSelectorDisplay(Template $template, $use_fieldset = false, $show_doc = true)
999 $_form_params = [
1000 'db' => $GLOBALS['db'],
1001 'table' => $GLOBALS['table'],
1004 // For non-English, display "Language" with emphasis because it's
1005 // not a proper word in the current language; we show it to help
1006 // people recognize the dialog
1007 $language_title = __('Language')
1008 . (__('Language') !== 'Language' ? ' - <em>Language</em>' : '');
1009 if ($show_doc) {
1010 $language_title .= MySQLDocumentation::showDocumentation('faq', 'faq7-2');
1013 $available_languages = $this->sortedLanguages();
1015 return $template->render('select_lang', [
1016 'language_title' => $language_title,
1017 'use_fieldset' => $use_fieldset,
1018 'available_languages' => $available_languages,
1019 '_form_params' => $_form_params,