From e0eac4b22f7fbac824e2c34c9bc1f11755b09dbd Mon Sep 17 00:00:00 2001 From: ismisepaul Date: Sun, 25 Oct 2026 00:46:06 +0100 Subject: [PATCH] fix: validateLanguage keeps the player's language instead of always en_GB (#937) validateLanguage only accepted a session lang matching ".[a-z]{2}-[A-Z]{2}$", but the language picker stores two-letter codes (es, fr, ...) and translation.jsp stores the request Locale (es_ES), so it always returned en_GB and every lesson and challenge answered in English whatever language was picked. Accept a two-letter language, optionally with a region, and return the language when it is one the picker offers. Anything else still falls back to en_GB. Co-Authored-By: Claude Opus 5.5 --- src/main/java/utils/Validate.java | 24 +++++----- src/test/java/utils/ValidateLanguageTest.java | 45 +++++++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 src/test/java/utils/ValidateLanguageTest.java diff --git a/src/main/java/utils/Validate.java b/src/main/java/utils/Validate.java index 12f7c0b72..0bd6ad706 100644 --- a/src/main/java/utils/Validate.java +++ b/src/main/java/utils/Validate.java @@ -5,6 +5,7 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpSession; import java.math.BigInteger; +import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -332,28 +333,31 @@ public static String validateEncryptionKey(String userSalt) { * Function that will check if a valid language is set. if not, returns en (English) * * @param ses Session Language Parameter - * @return en by default, or the valid setting found in the submitted lang + * @return The supported language code in the session's lang (a picker value such as "es", or a + * locale such as "es_ES"), or en_GB by default */ public static String validateLanguage(HttpSession ses) { String result = "en_GB"; - String lang = new String(); + String lang = ""; - try { + if (ses != null && ses.getAttribute("lang") != null) { lang = ses.getAttribute("lang").toString(); - } catch (NullPointerException e) { - lang = ""; } - // log.debug("lang submitted: " + lang); - if (lang != null) { - if (lang.matches(".[a-z]{2}-[A-Z]{2}$")) { - result = lang; + // A two-letter language, optionally followed by a region (es, es_ES, pt-BR) + if (lang.matches("^[a-z]{2}([_-][A-Za-z]{2})?$")) { + String language = lang.substring(0, 2); + if (SUPPORTED_LANGUAGES.contains(language)) { + result = language; } } - // log.debug("lang set to: " + result); return result; } + /** The languages offered by translation-select.jsp */ + private static final Set SUPPORTED_LANGUAGES = + Set.of("en", "es", "fr", "pt", "ga", "zh", "hi", "ru"); + /** * Validates objects received through a function request. Also ensures max length is not too high. * diff --git a/src/test/java/utils/ValidateLanguageTest.java b/src/test/java/utils/ValidateLanguageTest.java new file mode 100644 index 000000000..5d4e4bc49 --- /dev/null +++ b/src/test/java/utils/ValidateLanguageTest.java @@ -0,0 +1,45 @@ +package utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Locale; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpSession; + +/** Validate.validateLanguage keeps the language the player picked (#937) */ +class ValidateLanguageTest { + + private static String languageFor(Object lang) { + MockHttpSession session = new MockHttpSession(); + session.setAttribute("lang", lang); + return Validate.validateLanguage(session); + } + + @Test + public void pickerLanguages_AreKept() { + for (String lang : new String[] {"en", "es", "fr", "pt", "ga", "zh", "hi", "ru"}) { + assertEquals(lang, languageFor(lang), lang); + } + } + + @Test + public void languageWithRegion_IsReducedToTheLanguage() { + assertEquals("es", languageFor("es_ES")); + assertEquals("pt", languageFor("pt-BR")); + // translation.jsp stores the request's Locale when no language was picked + assertEquals("fr", languageFor(Locale.FRANCE)); + } + + @Test + public void unsupportedOrMalformed_FallsBackToDefault() { + for (String lang : new String[] {"de", "xen-GB", "EN", "english", "../es", "es_ES_x", ""}) { + assertEquals("en_GB", languageFor(lang), lang); + } + } + + @Test + public void missingLanguage_FallsBackToDefault() { + assertEquals("en_GB", Validate.validateLanguage(new MockHttpSession())); + assertEquals("en_GB", Validate.validateLanguage(null)); + } +}