]> git.mxchange.org Git - friendica.git/blobdiff - src/Core/L10n/L10n.php
Make L10n immutable
[friendica.git] / src / Core / L10n / L10n.php
index 355f4c6ed4666c9dfdb51c0cd14e0e681fbcbaf9..be9e1419f39ab60cf2dbac73a41ef186ab2926f9 100644 (file)
@@ -4,7 +4,7 @@ namespace Friendica\Core\L10n;
 
 use Friendica\Core\Config\Configuration;
 use Friendica\Core\Hook;
-use Friendica\Core\Session;
+use Friendica\Core\Session\ISession;
 use Friendica\Database\Database;
 use Friendica\Util\Strings;
 use Psr\Log\LoggerInterface;
@@ -23,12 +23,6 @@ class L10n
         * @var string
         */
        private $lang = '';
-       /**
-        * A language code saved for later after pushLang() has been called.
-        *
-        * @var string
-        */
-       private $langSave = '';
 
        /**
         * An array of translation strings whose key is the neutral english message.
@@ -36,12 +30,6 @@ class L10n
         * @var array
         */
        private $strings = [];
-       /**
-        * An array of translation strings saved for later after pushLang() has been called.
-        *
-        * @var array
-        */
-       private $stringsSave = [];
 
        /**
         * @var Database
@@ -53,14 +41,14 @@ class L10n
         */
        private $logger;
 
-       public function __construct(Configuration $config, Database $dba, LoggerInterface $logger)
+       public function __construct(Configuration $config, Database $dba, LoggerInterface $logger, ISession $session, array $server, array $get)
        {
                $this->dba    = $dba;
                $this->logger = $logger;
 
-               $this->loadTranslationTable(L10n::detectLanguage($config->get('system', 'language', 'en')));
-
-               \Friendica\Core\L10n::init($this);
+               $this->loadTranslationTable(L10n::detectLanguage($server, $get, $config->get('system', 'language', 'en')));
+               $this->setSessionVariable($session);
+               $this->setLangFromSession($session);
        }
 
        /**
@@ -76,73 +64,29 @@ class L10n
        /**
         * Sets the language session variable
         */
-       public function setSessionVariable()
+       private function setSessionVariable(ISession $session)
        {
-               if (Session::get('authenticated') && !Session::get('language')) {
-                       $_SESSION['language'] = $this->lang;
+               if ($session->get('authenticated') && !$session->get('language')) {
+                       $session->set('language', $this->lang);
                        // we haven't loaded user data yet, but we need user language
-                       if (Session::get('uid')) {
+                       if ($session->get('uid')) {
                                $user = $this->dba->selectFirst('user', ['language'], ['uid' => $_SESSION['uid']]);
                                if ($this->dba->isResult($user)) {
-                                       $_SESSION['language'] = $user['language'];
+                                       $session->set('language', $user['language']);
                                }
                        }
                }
 
                if (isset($_GET['lang'])) {
-                       Session::set('language', $_GET['lang']);
+                       $session->set('language', $_GET['lang']);
                }
        }
 
-       public function setLangFromSession()
+       private function setLangFromSession(ISession $session)
        {
-               if (Session::get('language') !== $this->lang) {
-                       $this->loadTranslationTable(Session::get('language'));
-               }
-       }
-
-       /**
-        * This function should be called before formatting messages in a specific target language
-        * different from the current user/system language.
-        *
-        * It saves the current translation strings in a separate variable and loads new translations strings.
-        *
-        * If called repeatedly, it won't save the translation strings again, just load the new ones.
-        *
-        * @param string $lang Language code
-        *
-        * @throws \Exception
-        * @see   popLang()
-        * @brief Stores the current language strings and load a different language.
-        */
-       public function pushLang($lang)
-       {
-               if ($lang === $this->lang) {
-                       return;
-               }
-
-               if (empty($this->langSave)) {
-                       $this->langSave    = $this->lang;
-                       $this->stringsSave = $this->strings;
-               }
-
-               $this->loadTranslationTable($lang);
-       }
-
-       /**
-        * Restores the original user/system language after having used pushLang()
-        */
-       public function popLang()
-       {
-               if (!isset($this->langSave)) {
-                       return;
+               if ($session->get('language') !== $this->lang) {
+                       $this->loadTranslationTable($session->get('language'));
                }
-
-               $this->strings = $this->stringsSave;
-               $this->lang    = $this->langSave;
-
-               $this->stringsSave = null;
-               $this->langSave = null;
        }
 
        /**
@@ -160,6 +104,11 @@ class L10n
        {
                $lang = Strings::sanitizeFilePathItem($lang);
 
+               // Don't override the language setting with empty languages
+               if (empty($lang)) {
+                       return;
+               }
+
                $a          = new \stdClass();
                $a->strings = [];
 
@@ -168,12 +117,12 @@ class L10n
                while ($p = $this->dba->fetch($addons)) {
                        $name = Strings::sanitizeFilePathItem($p['name']);
                        if (file_exists("addon/$name/lang/$lang/strings.php")) {
-                               include "addon/$name/lang/$lang/strings.php";
+                               include __DIR__ . "/../../../addon/$name/lang/$lang/strings.php";
                        }
                }
 
-               if (file_exists("view/lang/$lang/strings.php")) {
-                       include "view/lang/$lang/strings.php";
+               if (file_exists(__DIR__ . "/../../../view/lang/$lang/strings.php")) {
+                       include __DIR__ . "/../../../view/lang/$lang/strings.php";
                }
 
                $this->lang    = $lang;
@@ -186,49 +135,78 @@ class L10n
         * @brief Returns the preferred language from the HTTP_ACCEPT_LANGUAGE header
         *
         * @param string $sysLang The default fallback language
+        * @param array  $server  The $_SERVER array
+        * @param array  $get     The $_GET array
         *
         * @return string The two-letter language code
         */
-       public static function detectLanguage(string $sysLang = 'en')
+       public static function detectLanguage(array $server, array $get, string $sysLang = 'en')
        {
-               $lang_list = [];
-
-               if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
-                       // break up string into pieces (languages and q factors)
-                       preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
-
-                       if (count($lang_parse[1])) {
-                               // go through the list of prefered languages and add a generic language
-                               // for sub-linguas (e.g. de-ch will add de) if not already in array
-                               for ($i = 0; $i < count($lang_parse[1]); $i++) {
-                                       $lang_list[] = strtolower($lang_parse[1][$i]);
-                                       if (strlen($lang_parse[1][$i]) > 3) {
-                                               $dashpos = strpos($lang_parse[1][$i], '-');
-                                               if (!in_array(substr($lang_parse[1][$i], 0, $dashpos), $lang_list)) {
-                                                       $lang_list[] = strtolower(substr($lang_parse[1][$i], 0, $dashpos));
-                                               }
-                                       }
-                               }
-                       }
+               $lang_variable = $server['HTTP_ACCEPT_LANGUAGE'] ?? null;
+
+               $acceptedLanguages = preg_split('/,\s*/', $lang_variable);
+
+               if (empty($acceptedLanguages)) {
+                       $acceptedLanguages = [];
                }
 
-               if (isset($_GET['lang'])) {
-                       $lang_list = [$_GET['lang']];
+               // Add get as absolute quality accepted language (except this language isn't valid)
+               if (!empty($get['lang'])) {
+                       $acceptedLanguages[] = $get['lang'];
                }
 
-               // check if we have translations for the preferred languages and pick the 1st that has
-               foreach ($lang_list as $lang) {
-                       if ($lang === 'en' || (file_exists("view/lang/$lang") && is_dir("view/lang/$lang"))) {
-                               $preferred = $lang;
-                               break;
-                       }
+               // return the sys language in case there's nothing to do
+               if (empty($acceptedLanguages)) {
+                       return $sysLang;
                }
-               if (isset($preferred)) {
-                       return $preferred;
+
+               // Set the syslang as default fallback
+               $current_lang = $sysLang;
+               // start with quality zero (every guessed language is more acceptable ..)
+               $current_q = 0;
+
+               foreach ($acceptedLanguages as $acceptedLanguage) {
+                       $res = preg_match(
+                               '/^([a-z]{1,8}(?:-[a-z]{1,8})*)(?:;\s*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?$/i',
+                               $acceptedLanguage,
+                               $matches
+                       );
+
+                       // Invalid language? -> skip
+                       if (!$res) {
+                               continue;
+                       }
+
+                       // split language codes based on it's "-"
+                       $lang_code = explode('-', $matches[1]);
+
+                       // determine the quality of the guess
+                       if (isset($matches[2])) {
+                               $lang_quality = (float)$matches[2];
+                       } else {
+                               // fallback so without a quality parameter, it's probably the best
+                               $lang_quality = 1;
+                       }
+
+                       // loop through each part of the code-parts
+                       while (count($lang_code)) {
+                               // try to mix them so we can get double-code parts too
+                               $match_lang = strtolower(join('-', $lang_code));
+                               if (file_exists(__DIR__ . "/../../../view/lang/$match_lang") &&
+                                   is_dir(__DIR__ . "/../../../view/lang/$match_lang")) {
+                                       if ($lang_quality > $current_q) {
+                                               $current_lang = $match_lang;
+                                               $current_q    = $lang_quality;
+                                               break;
+                                       }
+                               }
+
+                               // remove the most right code-part
+                               array_pop($lang_code);
+                       }
                }
 
-               // in case none matches, get the system wide configured language, or fall back to English
-               return $sysLang;
+               return $current_lang;
        }
 
        /**
@@ -424,4 +402,19 @@ class L10n
 
                return $arr;
        }
+
+       /**
+        * Creates a new L10n instance based on the given langauge
+        *
+        * @param string $lang The new language
+        *
+        * @return static A new L10n instance
+        * @throws \Exception
+        */
+       public function withLang(string $lang)
+       {
+               $newL10n = clone $this;
+               $newL10n->loadTranslationTable($lang);
+               return $newL10n;
+       }
 }