src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt

changeset 298
1275eb652008
parent 254
55ca6cafc3dd
--- a/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt	Fri Nov 24 00:07:36 2023 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt	Sat Jan 06 20:31:14 2024 +0100
@@ -28,6 +28,7 @@
 import de.uapcore.lightpit.DataSourceProvider.Companion.SC_ATTR_NAME
 import de.uapcore.lightpit.dao.DataAccessObject
 import de.uapcore.lightpit.dao.createDataAccessObject
+import jakarta.servlet.http.Cookie
 import jakarta.servlet.http.HttpServlet
 import jakarta.servlet.http.HttpServletRequest
 import jakarta.servlet.http.HttpServletResponse
@@ -35,6 +36,10 @@
 import java.util.*
 
 abstract class AbstractServlet : HttpServlet() {
+
+    companion object {
+        const val LANGUAGE_COOKIE_NAME = "lpit_language"
+    }
     
     protected val logger = MyLogger()
 
@@ -100,22 +105,6 @@
         // the very first thing to do is to force UTF-8
         req.characterEncoding = "UTF-8"
 
-        // choose the requested language as session language (if available) or fall back to english, otherwise
-        if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) {
-            val availableLanguages = availableLanguages()
-            val reqLocale = req.locale
-            val sessionLocale = if (availableLanguages.contains(reqLocale)) reqLocale else availableLanguages.first()
-            session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, sessionLocale)
-            resp.locale = sessionLocale
-            logger.debug(
-                "Setting language for new session {0}: {1}", session.id, sessionLocale.displayLanguage
-            )
-        } else {
-            val sessionLocale = session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale
-            resp.locale = sessionLocale
-            logger.trace("Continuing session {0} with language {1}", session.id, sessionLocale)
-        }
-
         // set some internal request attributes
         val http = HttpRequest(req, resp)
         val fullPath = req.servletPath + Optional.ofNullable(req.pathInfo).orElse("")
@@ -126,6 +115,29 @@
             req.setAttribute(Constants.REQ_ATTR_REFERER, it)
         }
 
+        // choose the requested language as session language (if available)
+        if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) {
+            // language selection stored in cookie
+            val cookieLocale = cookieLanguage(http)
+
+            // if no cookie, fall back to request locale a.k.a "Browser Language"
+            val reqLocale = cookieLocale ?: req.locale
+
+            val availableLanguages = availableLanguages()
+            val sessionLocale = if (availableLanguages.contains(reqLocale)) reqLocale else availableLanguages.first()
+
+            // select the language (this will also refresh the cookie max-age)
+            selectLanguage(http, sessionLocale)
+
+            logger.debug(
+                "Setting language for new session {0}: {1}", session.id, sessionLocale.displayLanguage
+            )
+        } else {
+            val sessionLocale = session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale
+            resp.locale = sessionLocale
+            logger.trace("Continuing session {0} with language {1}", session.id, sessionLocale)
+        }
+
         // if this is an error path, bypass the normal flow
         if (fullPath.startsWith("/error/")) {
             http.styleSheets = listOf("error")
@@ -182,4 +194,23 @@
         return locales.ifEmpty { listOf(Locale.ENGLISH) }
     }
 
+    private fun cookieLanguage(http: HttpRequest): Locale? =
+        http.request.cookies?.firstOrNull { c -> c.name == LANGUAGE_COOKIE_NAME }
+            ?.runCatching {Locale.forLanguageTag(this.value)}?.getOrNull()
+
+    protected fun selectLanguage(http: HttpRequest, locale: Locale) {
+        http.response.locale = locale
+        http.session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, locale)
+        // delete cookie if language selection matches request locale, otherwise set cookie
+        val cookie = Cookie(LANGUAGE_COOKIE_NAME, "")
+        cookie.isHttpOnly = true
+        cookie.path = http.request.contextPath
+        if (http.request.locale.language == locale.language) {
+            cookie.maxAge = 0
+        } else {
+            cookie.value = locale.language
+            cookie.maxAge = 2592000 // 30 days
+        }
+        http.response.addCookie(cookie)
+    }
 }

mercurial