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',
638 'Українська',
653 'uz[-_]lat|uz@latin|uzbek-latin',
659 'Ўзбекча',
660 'uz[-_]cyr|uz@cyrillic|uzbek-cyrillic',
679 'Chinese traditional',
681 'zh[-_](tw|hk)|chinese traditional',
684 // only TW and HK use traditional Chinese while others (CN, SG, MY)
685 // use simplified Chinese
688 'Chinese simplified',
690 'zh(?![-_](tw|hk))([-_][[:alpha:]]{2,3})?|chinese simplified',
696 private $availableLocales;
699 private $availableLanguages = [];
702 private $langFailedConfig = false;
705 private $langFailedCookie = false;
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
732 public function listLocaleDir()
736 /* Check for existing directory */
737 if (! is_dir(LOCALE_PATH
)) {
741 /* Open the directory */
742 $handle = @opendir
(LOCALE_PATH
);
743 /* This can happen if the kit is English-only */
744 if ($handle === false) {
748 /* Process all files */
749 while (($file = readdir($handle)) !== false) {
752 . '/LC_MESSAGES/phpmyadmin.mo';
755 ||
! @file_exists
($path)
762 /* Close the handle */
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();
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
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(
821 $this->availableLanguages
[$lang] = new Language(
832 return $this->availableLanguages
;
836 * Returns (cached) list of all available languages sorted
839 * @return Language[] array of Language objects
841 public function sortedLanguages()
843 $this->availableLanguages();
844 uasort($this->availableLanguages
, static function (Language
$a, Language
$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];
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
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) {
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) {
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) {
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) {
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)) {
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)) {
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
958 * Displays warnings about invalid languages. This needs to be postponed
959 * to show messages at time when language is initialized.
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
974 __('Ignoring unsupported language code.'),
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
990 public function getSelectorDisplay(Template
$template, $use_fieldset = false, $show_doc = true)
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>' : '');
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,