From 00028bc455146aac8113be750a4165153f8a8fb4 Mon Sep 17 00:00:00 2001
From: Jan-Hendrik Willms <tleilax+studip@gmail.com>
Date: Wed, 19 Oct 2022 09:27:19 +0000
Subject: [PATCH] get language from accept header only if no valid is started,
 fixes #1680, fixes #1394

Closes #1680 and #1394

Merge request studip/studip!1089
---
 lib/classes/JsonApi/Middlewares/Language.php | 79 ++------------------
 lib/classes/JsonApi/middleware.php           |  2 +-
 lib/language.inc.php                         | 50 ++++++++-----
 3 files changed, 41 insertions(+), 90 deletions(-)

diff --git a/lib/classes/JsonApi/Middlewares/Language.php b/lib/classes/JsonApi/Middlewares/Language.php
index 4cde6997357..547e9f04e2f 100644
--- a/lib/classes/JsonApi/Middlewares/Language.php
+++ b/lib/classes/JsonApi/Middlewares/Language.php
@@ -4,6 +4,7 @@ namespace JsonApi\Middlewares;
 use Negotiation\LanguageNegotiator;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface as Request;
+use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
 
 /**
@@ -12,7 +13,7 @@ use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
  *
  * @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
  */
-class Language
+final class Language implements MiddlewareInterface
 {
     /**
      * @param Request        $request das Request-Objekt
@@ -20,79 +21,13 @@ class Language
      *
      * @return ResponseInterface das neue Response-Objekt
      */
-    public function __invoke(Request $request, RequestHandler $handler)
+    public function process(Request $request, RequestHandler $handler): ResponseInterface
     {
-        $language = $this->detectValidLanguageFromRequest($request);
-        $language_before = false;
+        $language = $_SESSION['_language'] ?? get_accepted_languages($request);
 
-        // Set language if detected
-        if ($language) {
-            $language_before = $_SESSION['_language'];
-            $_SESSION['_language'] = $language;
-            setTempLanguage(false, $language);
-        }
+        init_i18n($language);
+        $_SESSION['_language'] = $language;
 
-        $response =  $handler->handle($request);
-
-        if ($language) {
-            $_SESSION['_language'] = $language_before;
-            restoreLanguage();
-        }
-
-        return $response;
-    }
-
-    /**
-     * Tries to detect a valid installed language from the request.
-     *
-     * @param Request $request
-     * @return string|null The detected language (if any)
-     */
-    private function detectValidLanguageFromRequest(Request $request): ?string
-    {
-        if (!$request->hasHeader('Accept-Language')) {
-            return null;
-        }
-
-        $negotiator = new LanguageNegotiator();
-        $best_language = $negotiator->getBest(
-            $request->getHeaderLine('Accept-Language'),
-            $this->getStudIPLanguagePriorities()
-        );
-
-        if (!$best_language) {
-            return null;
-        }
-
-        return $this->normalizeLanguageForStudIP($best_language->getType());
-    }
-
-    /**
-     * Returns a list of the normalized installed languages for the Stud.IP
-     * system.
-     *
-     * @return array
-     */
-    private function getStudIPLanguagePriorities(): array
-    {
-        return array_map(
-            function ($language) {
-                return str_replace('_', '-', $language);
-            },
-            array_keys($GLOBALS['INSTALLED_LANGUAGES'])
-        );
-    }
-
-    /**
-     * Normalizes the given language string (<language>-<variety>, e.g. de-de)
-     * for Stud.IP (e.g. de_DE).
-     *
-     * @param string $language
-     * @return string
-     */
-    private function normalizeLanguageForStudIP(string $language): string
-    {
-        $tags = explode('-', $language);
-        return $tags[0] . '_' . strtoupper($tags[1]);
+        return $handler->handle($request);
     }
 }
diff --git a/lib/classes/JsonApi/middleware.php b/lib/classes/JsonApi/middleware.php
index f65231c8861..e00b41bf55f 100644
--- a/lib/classes/JsonApi/middleware.php
+++ b/lib/classes/JsonApi/middleware.php
@@ -27,7 +27,7 @@ return function (App $app) {
     $app->addRoutingMiddleware();
 
     // Add language middleware
-    $app->add(new Middlewares\Language());
+    $app->add(Middlewares\Language::class);
 
     /** @var array|null */
     $corsOrigin = \Config::get()->getValue('JSONAPI_CORS_ORIGIN');
diff --git a/lib/language.inc.php b/lib/language.inc.php
index 2168d161294..fbe84717554 100644
--- a/lib/language.inc.php
+++ b/lib/language.inc.php
@@ -35,7 +35,7 @@
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // +---------------------------------------------------------------------------+
-
+use Negotiation\AcceptHeader;
 
 /**
 * This function tries to find the preferred language
@@ -47,22 +47,38 @@
 * @return       string  preferred user language, given in "en_GB"-style
 *
 */
-function get_accepted_languages() {
-    global $INSTALLED_LANGUAGES;
-
-    $_language = Config::get()->DEFAULT_LANGUAGE;
-    $accepted_languages = explode(",", getenv("HTTP_ACCEPT_LANGUAGE"));
-    if (is_array($accepted_languages) && count($accepted_languages)) {
-        foreach ($accepted_languages as $temp_accepted_language) {
-            foreach ($INSTALLED_LANGUAGES as $temp_language => $temp_language_settings) {
-                if (mb_substr(trim($temp_accepted_language), 0, 2) == mb_substr($temp_language, 0, 2)) {
-                    $_language = $temp_language;
-                    break 2;
-                }
-            }
-        }
+function get_accepted_languages(Psr\Http\Message\RequestInterface $request = null) {
+    $accepted_languages = null;
+    if ($request === null) {
+        $accepted_languages = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
+    } elseif ($request->hasHeader('Accept-Language')) {
+        $accepted_languages = $request->getHeaderLine('Accept-Language');
+    }
+
+    // No languages found from http header, set default
+    if (!$accepted_languages) {
+        return Config::get()->DEFAULT_LANGUAGE;
     }
-    return $_language;
+
+    // Use negotiator to identify best match
+    $negotiator = new Negotiation\LanguageNegotiator();
+    $best_language = $negotiator->getBest(
+        $accepted_languages,
+        array_map(
+            function ($language) {
+                return str_replace('_', '-', $language);
+            },
+            array_keys($GLOBALS['INSTALLED_LANGUAGES'])
+        )
+    );
+
+    // No best match, set default
+    if (!$best_language) {
+        return Config::get()->DEFAULT_LANGUAGE;
+    }
+
+    // Normalize language definition for stud.ip
+    return $best_language->getBasePart() . '_' . strtoupper($best_language->getSubPart());
 }
 
 
@@ -192,7 +208,7 @@ function restoreLanguage() {
 * @access   public
 */
 function setLocaleEnv($language, $language_domain = ''){
-    putenv('LANGUAGE'); //unset language preference 
+    putenv('LANGUAGE'); //unset language preference
     $ret = setlocale(LC_ALL, $language . '.UTF-8');
     setlocale(LC_NUMERIC, 'C');
     if ($language_domain) {
-- 
GitLab