Sat, 06 Jan 2024 20:31:14 +0100
add language selection cookie - fixes #352
--- 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>