From dcf42b6a1f5a1b47d79fa2d2c06e2e4b07a9024f Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Willms <tleilax+studip@gmail.com> Date: Fri, 9 Sep 2022 11:07:12 +0000 Subject: [PATCH] add language middleware that sets the system's language from a given... Closes #1568 Merge request studip/studip!993 --- composer.json | 3 +- composer.lock | 58 ++++++++++++- lib/classes/JsonApi/Middlewares/Language.php | 89 ++++++++++++++++++++ lib/classes/JsonApi/middleware.php | 3 + 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 lib/classes/JsonApi/Middlewares/Language.php diff --git a/composer.json b/composer.json index 696383c3c73..03104690821 100644 --- a/composer.json +++ b/composer.json @@ -52,6 +52,7 @@ "symfony/console": "~5.3.16", "symfony/process": "^5.4", "jumbojett/openid-connect-php": "^0.9.2", - "league/oauth2-server": "^8.3" + "league/oauth2-server": "^8.3", + "willdurand/negotiation": "^3.1" } } diff --git a/composer.lock b/composer.lock index 1acf6b621f5..29f2142a9d7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c5b753821c3aa77c5de49e8932b47994", + "content-hash": "7a9043e1984ae4fb02e5872928486f0a", "packages": [ { "name": "algo26-matthias/idna-convert", @@ -4019,6 +4019,62 @@ } ], "time": "2021-09-14T12:46:25+00:00" + }, + { + "name": "willdurand/negotiation", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/willdurand/Negotiation.git", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Negotiation\\": "src/Negotiation" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "William Durand", + "email": "will+git@drnd.me" + } + ], + "description": "Content Negotiation tools for PHP provided as a standalone library.", + "homepage": "http://williamdurand.fr/Negotiation/", + "keywords": [ + "accept", + "content", + "format", + "header", + "negotiation" + ], + "support": { + "issues": "https://github.com/willdurand/Negotiation/issues", + "source": "https://github.com/willdurand/Negotiation/tree/3.1.0" + }, + "time": "2022-01-30T20:08:53+00:00" } ], "packages-dev": [ diff --git a/lib/classes/JsonApi/Middlewares/Language.php b/lib/classes/JsonApi/Middlewares/Language.php new file mode 100644 index 00000000000..8843445665d --- /dev/null +++ b/lib/classes/JsonApi/Middlewares/Language.php @@ -0,0 +1,89 @@ +<?php +namespace JsonApi\Middlewares; + +use Negotiation\LanguageNegotiator; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Server\RequestHandlerInterface as RequestHandler; + +/** + * This class defines a middleware that tries to set the language for Stud.IP + * by analyzing the HTTP header "Accept-Language". + * + * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> + */ +class Language +{ + /** + * @param Request $request das Request-Objekt + * @param RequestHandler $handler der PSR-15 Request Handler + * + * @return ResponseInterface das neue Response-Objekt + */ + public function __invoke(Request $request, RequestHandler $handler) + { + $language = $this->detectValidLanguageFromRequest($request); + + // Set language if detected + if ($language) { + $_SESSION['_language'] = $language; + setTempLanguage(false, $language); + } + + return $handler->handle($request); + } + + /** + * 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]); + } +} diff --git a/lib/classes/JsonApi/middleware.php b/lib/classes/JsonApi/middleware.php index 7a318d5b7ca..f65231c8861 100644 --- a/lib/classes/JsonApi/middleware.php +++ b/lib/classes/JsonApi/middleware.php @@ -26,6 +26,9 @@ return function (App $app) { // Add Routing Middleware $app->addRoutingMiddleware(); + // Add language middleware + $app->add(new Middlewares\Language()); + /** @var array|null */ $corsOrigin = \Config::get()->getValue('JSONAPI_CORS_ORIGIN'); if (is_array($corsOrigin) && count($corsOrigin)) { -- GitLab