1.1 --- a/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt Fri Nov 24 00:07:36 2023 +0100 1.2 +++ b/src/main/kotlin/de/uapcore/lightpit/AbstractServlet.kt Sat Jan 06 20:31:14 2024 +0100 1.3 @@ -28,6 +28,7 @@ 1.4 import de.uapcore.lightpit.DataSourceProvider.Companion.SC_ATTR_NAME 1.5 import de.uapcore.lightpit.dao.DataAccessObject 1.6 import de.uapcore.lightpit.dao.createDataAccessObject 1.7 +import jakarta.servlet.http.Cookie 1.8 import jakarta.servlet.http.HttpServlet 1.9 import jakarta.servlet.http.HttpServletRequest 1.10 import jakarta.servlet.http.HttpServletResponse 1.11 @@ -35,6 +36,10 @@ 1.12 import java.util.* 1.13 1.14 abstract class AbstractServlet : HttpServlet() { 1.15 + 1.16 + companion object { 1.17 + const val LANGUAGE_COOKIE_NAME = "lpit_language" 1.18 + } 1.19 1.20 protected val logger = MyLogger() 1.21 1.22 @@ -100,22 +105,6 @@ 1.23 // the very first thing to do is to force UTF-8 1.24 req.characterEncoding = "UTF-8" 1.25 1.26 - // choose the requested language as session language (if available) or fall back to english, otherwise 1.27 - if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) { 1.28 - val availableLanguages = availableLanguages() 1.29 - val reqLocale = req.locale 1.30 - val sessionLocale = if (availableLanguages.contains(reqLocale)) reqLocale else availableLanguages.first() 1.31 - session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, sessionLocale) 1.32 - resp.locale = sessionLocale 1.33 - logger.debug( 1.34 - "Setting language for new session {0}: {1}", session.id, sessionLocale.displayLanguage 1.35 - ) 1.36 - } else { 1.37 - val sessionLocale = session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale 1.38 - resp.locale = sessionLocale 1.39 - logger.trace("Continuing session {0} with language {1}", session.id, sessionLocale) 1.40 - } 1.41 - 1.42 // set some internal request attributes 1.43 val http = HttpRequest(req, resp) 1.44 val fullPath = req.servletPath + Optional.ofNullable(req.pathInfo).orElse("") 1.45 @@ -126,6 +115,29 @@ 1.46 req.setAttribute(Constants.REQ_ATTR_REFERER, it) 1.47 } 1.48 1.49 + // choose the requested language as session language (if available) 1.50 + if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) { 1.51 + // language selection stored in cookie 1.52 + val cookieLocale = cookieLanguage(http) 1.53 + 1.54 + // if no cookie, fall back to request locale a.k.a "Browser Language" 1.55 + val reqLocale = cookieLocale ?: req.locale 1.56 + 1.57 + val availableLanguages = availableLanguages() 1.58 + val sessionLocale = if (availableLanguages.contains(reqLocale)) reqLocale else availableLanguages.first() 1.59 + 1.60 + // select the language (this will also refresh the cookie max-age) 1.61 + selectLanguage(http, sessionLocale) 1.62 + 1.63 + logger.debug( 1.64 + "Setting language for new session {0}: {1}", session.id, sessionLocale.displayLanguage 1.65 + ) 1.66 + } else { 1.67 + val sessionLocale = session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) as Locale 1.68 + resp.locale = sessionLocale 1.69 + logger.trace("Continuing session {0} with language {1}", session.id, sessionLocale) 1.70 + } 1.71 + 1.72 // if this is an error path, bypass the normal flow 1.73 if (fullPath.startsWith("/error/")) { 1.74 http.styleSheets = listOf("error") 1.75 @@ -182,4 +194,23 @@ 1.76 return locales.ifEmpty { listOf(Locale.ENGLISH) } 1.77 } 1.78 1.79 + private fun cookieLanguage(http: HttpRequest): Locale? = 1.80 + http.request.cookies?.firstOrNull { c -> c.name == LANGUAGE_COOKIE_NAME } 1.81 + ?.runCatching {Locale.forLanguageTag(this.value)}?.getOrNull() 1.82 + 1.83 + protected fun selectLanguage(http: HttpRequest, locale: Locale) { 1.84 + http.response.locale = locale 1.85 + http.session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, locale) 1.86 + // delete cookie if language selection matches request locale, otherwise set cookie 1.87 + val cookie = Cookie(LANGUAGE_COOKIE_NAME, "") 1.88 + cookie.isHttpOnly = true 1.89 + cookie.path = http.request.contextPath 1.90 + if (http.request.locale.language == locale.language) { 1.91 + cookie.maxAge = 0 1.92 + } else { 1.93 + cookie.value = locale.language 1.94 + cookie.maxAge = 2592000 // 30 days 1.95 + } 1.96 + http.response.addCookie(cookie) 1.97 + } 1.98 }