3 declare(strict_types
=1);
7 use PhpMyAdmin\Html\MySQLDocumentation
;
8 use const E_USER_ERROR
;
12 use function file_exists
;
15 use function preg_grep
;
17 use function strtolower
;
18 use function trigger_error
;
23 * Language selection manager
28 * Definition data for languages
30 * Each member contains:
32 * - English language name
33 * - Native language name
34 * - Match regular expression
39 private static $languageData = [
57 'العربية',
65 'ar[_-]ly|arabic (libya)|libian arabic',
85 'Беларуская',
93 'be[-_]lat|be@latin|belarusian latin',
106 'Български',
176 'Ελληνικά',
189 'English (United Kingdom)',
191 'en[_-]gb|english (United Kingdom)',
225 'فارسی',
274 'עברית',
281 'हिन्दी',
337 '日本語',
344 '한국어',
351 'ქართული',
442 'Монгол',
511 'Portuguese (Brazil)',
512 'Português (Brasil)',
513 'pt[-_]br|portuguese (brazil)',
518 'Réunion Creole',
520 'rcf|creole (reunion)',
533 'Русский',
540 'සිංහල',
554 'Slovenščina',
569 'sr[-_]lat|sr@latin|serbian latin',
575 'Српски',
603 'ภาษาไทย',
617 'Türkçe',
630 'Central Atlas Tamazight',
632 'tzm|central atlas tamazight',
645 'Українська',
660 'uz[-_]lat|uz@latin|uzbek-latin',
666 'Ўзбекча',
667 'uz[-_]cyr|uz@cyrillic|uzbek-cyrillic',
686 'Chinese traditional',
688 'zh[-_](tw|hk)|chinese traditional',
691 // only TW and HK use traditional Chinese while others (CN, SG, MY)
692 // use simplified Chinese
695 'Chinese simplified',
697 'zh(?![-_](tw|hk))([-_][[:alpha:]]{2,3})?|chinese simplified',
703 private $availableLocales;
706 private $availableLanguages = [];
709 private $langFailedConfig = false;
712 private $langFailedCookie = false;
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
739 public function listLocaleDir()
743 /* Check for existing directory */
744 if (! is_dir(LOCALE_PATH
)) {
748 /* Open the directory */
749 $handle = @opendir
(LOCALE_PATH
);
750 /* This can happen if the kit is English-only */
751 if ($handle === false) {
755 /* Process all files */
756 while (($file = readdir($handle)) !== false) {
759 . '/LC_MESSAGES/phpmyadmin.mo';
762 ||
! @file_exists
($path)
769 /* Close the handle */
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();
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
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(
828 $this->availableLanguages
[$lang] = new Language(
839 return $this->availableLanguages
;
843 * Returns (cached) list of all available languages sorted
846 * @return Language[] array of Language objects
848 public function sortedLanguages()
850 $this->availableLanguages();
851 uasort($this->availableLanguages
, static function (Language
$a, Language
$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];
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
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) {
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) {
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) {
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) {
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)) {
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)) {
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
965 * Displays warnings about invalid languages. This needs to be postponed
966 * to show messages at time when language is initialized.
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
981 __('Ignoring unsupported language code.'),
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
997 public function getSelectorDisplay(Template
$template, $use_fieldset = false, $show_doc = true)
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>' : '');
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,