add language selection cookie - fixes #352

Sat, 06 Jan 2024 20:31:14 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 06 Jan 2024 20:31:14 +0100
changeset 298
1275eb652008
parent 296
355c86eaeca5
child 299
238de141d189

add language selection cookie - fixes #352

build.gradle.kts file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/servlet/LanguageServlet.kt file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/changelogs/changelog-de.jspf file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/changelogs/changelog.jspf file | annotate | diff | comparison | revisions
--- a/build.gradle.kts	Fri Nov 24 00:07:36 2023 +0100
+++ b/build.gradle.kts	Sat Jan 06 20:31:14 2024 +0100
@@ -5,7 +5,7 @@
     war
 }
 group = "de.uapcore"
-version = "1.2.1"
+version = "1.2.2"
 
 repositories {
     mavenCentral()
@@ -37,7 +37,7 @@
                 arrayOf(
                     "jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0",
                     "org.glassfish.web:jakarta.servlet.jsp.jstl:3.0.1",
-                    "org.postgresql:postgresql:42.6.0"
+                    "org.postgresql:postgresql:42.7.1"
                 ).forEach {
                     if (libsAreProvided) compileOnly(it) else implementation(it)
                 }
--- 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)
+    }
 }
--- a/src/main/kotlin/de/uapcore/lightpit/servlet/LanguageServlet.kt	Fri Nov 24 00:07:36 2023 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/servlet/LanguageServlet.kt	Sat Jan 06 20:31:14 2024 +0100
@@ -58,8 +58,7 @@
         if (lang != null) {
             val locale = Locale.forLanguageTag(lang)
             if (!locale.language.isNullOrBlank()) {
-                http.response.locale = locale
-                http.session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, locale)
+                super.selectLanguage(http, locale)
             }
         }
 
--- a/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf	Fri Nov 24 00:07:36 2023 +0100
+++ b/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf	Sat Jan 06 20:31:14 2024 +0100
@@ -24,6 +24,12 @@
   --%>
 <%@ page contentType="text/html;charset=UTF-8" %>
 
+<h3>Version 1.2.2</h3>
+
+<ul>
+    <li>Eine Ă„nderung der Sprache wird nun auch in einem Cookie gespeichert.</li>
+</ul>
+
 <h3>Version 1.2.1</h3>
 
 <ul>
--- a/src/main/webapp/WEB-INF/changelogs/changelog.jspf	Fri Nov 24 00:07:36 2023 +0100
+++ b/src/main/webapp/WEB-INF/changelogs/changelog.jspf	Sat Jan 06 20:31:14 2024 +0100
@@ -24,6 +24,12 @@
   --%>
 <%@ page contentType="text/html;charset=UTF-8" %>
 
+<h3>Version 1.2.2</h3>
+
+<ul>
+    <li>Active language selection is now also stored in a cookie.</li>
+</ul>
+
 <h3>Version 1.2.1</h3>
 
 <ul>

mercurial