You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2020/11/22 09:18:51 UTC
[struts] 01/01: WW-4929 Adds support for Accept-Language header
when looking up Locale
This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch WW-4929-support-accept-language
in repository https://gitbox.apache.org/repos/asf/struts.git
commit a1c52846888d555e94506f83e7eefb588f875392
Author: Lukasz Lenart <lu...@apache.org>
AuthorDate: Sun Nov 22 10:18:41 2020 +0100
WW-4929 Adds support for Accept-Language header when looking up Locale
---
.../struts2/interceptor/I18nInterceptor.java | 70 ++++++++++++++++++----
.../struts2/interceptor/I18nInterceptorTest.java | 43 +++++++++++--
2 files changed, 96 insertions(+), 17 deletions(-)
diff --git a/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java
index 4ddcedf..5964693 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java
@@ -23,6 +23,7 @@ import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.LocaleProviderFactory;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.util.TextParseUtil;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -35,8 +36,12 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+import java.util.Collections;
+import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* An interceptor that handles setting the locale specified in a session as the locale for the current action request.
@@ -59,8 +64,9 @@ public class I18nInterceptor extends AbstractInterceptor {
protected LocaleProviderFactory localeProviderFactory;
- // Request-Only = None
- protected enum Storage { COOKIE, SESSION, NONE }
+ private Set<Locale> supportedLocale = Collections.emptySet();
+
+ protected enum Storage { COOKIE, SESSION, REQUEST, ACCEPT_LANGUAGE }
public void setParameterName(String parameterName) {
this.parameterName = parameterName;
@@ -80,7 +86,7 @@ public class I18nInterceptor extends AbstractInterceptor {
public void setLocaleStorage(String storageName) {
if (storageName == null || "".equals(storageName)) {
- this.storage = Storage.NONE;
+ this.storage = Storage.ACCEPT_LANGUAGE;
} else {
try {
this.storage = Storage.valueOf(storageName.toUpperCase());
@@ -91,6 +97,19 @@ public class I18nInterceptor extends AbstractInterceptor {
}
}
+ /**
+ * Sets supported Locales by the application
+ *
+ * @param supportedLocale a comma separated list of supported Locale
+ */
+ public void setSupportedLocale(String supportedLocale) {
+ this.supportedLocale = TextParseUtil
+ .commaDelimitedStringToSet(supportedLocale)
+ .stream()
+ .map(Locale::new)
+ .collect(Collectors.toSet());
+ }
+
@Inject
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
this.localeProviderFactory = localeProviderFactory;
@@ -139,8 +158,10 @@ public class I18nInterceptor extends AbstractInterceptor {
localeHandler = new CookieLocaleHandler(invocation);
} else if (this.storage == Storage.SESSION) {
localeHandler = new SessionLocaleHandler(invocation);
+ } else if (this.storage == Storage.REQUEST) {
+ localeHandler = new RequestLocaleHandler(invocation);
} else {
- localeHandler = new RequestOnlyLocaleHandler(invocation);
+ localeHandler = new AcceptLanguageHandler(invocation);
}
LOG.debug("Using LocaleFinder implementation {}", localeHandler.getClass().getName());
@@ -176,7 +197,7 @@ public class I18nInterceptor extends AbstractInterceptor {
if (locale != null && !localeProvider.isValidLocale(locale)) {
Locale defaultLocale = localeProvider.getLocale();
- LOG.debug("Provided locale {} isn't valid, fallback to default locale", locale, defaultLocale);
+ LOG.debug("Provided locale {} isn't valid, fallback to default locale {}", locale, defaultLocale);
locale = defaultLocale;
}
@@ -200,7 +221,7 @@ public class I18nInterceptor extends AbstractInterceptor {
* @param locale The locale to save.
*/
protected void useLocale(ActionInvocation invocation, Locale locale) {
- invocation.getInvocationContext().setLocale(locale);
+ invocation.getInvocationContext().withLocale(locale);
}
/**
@@ -213,12 +234,12 @@ public class I18nInterceptor extends AbstractInterceptor {
boolean shouldStore();
}
- protected class RequestOnlyLocaleHandler implements LocaleHandler {
+ protected class RequestLocaleHandler implements LocaleHandler {
- protected ActionInvocation actionInvocation = null;
+ protected ActionInvocation actionInvocation;
protected boolean shouldStore = true;
- protected RequestOnlyLocaleHandler(ActionInvocation invocation) {
+ protected RequestLocaleHandler(ActionInvocation invocation) {
actionInvocation = invocation;
}
@@ -255,7 +276,32 @@ public class I18nInterceptor extends AbstractInterceptor {
}
}
- protected class SessionLocaleHandler extends RequestOnlyLocaleHandler {
+ protected class AcceptLanguageHandler extends RequestLocaleHandler {
+
+ protected AcceptLanguageHandler(ActionInvocation invocation) {
+ super(invocation);
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Locale find() {
+ Enumeration locales = actionInvocation.getInvocationContext().getServletRequest().getLocales();
+ while (locales.hasMoreElements()) {
+ Locale locale = (Locale) locales.nextElement();
+ if (supportedLocale.contains(locale)) {
+ return locale;
+ }
+ }
+ return super.find();
+ }
+
+ @Override
+ public boolean shouldStore() {
+ return false;
+ }
+ }
+
+ protected class SessionLocaleHandler extends RequestLocaleHandler {
protected SessionLocaleHandler(ActionInvocation invocation) {
super(invocation);
@@ -305,7 +351,7 @@ public class I18nInterceptor extends AbstractInterceptor {
String sessionId = session.getId();
synchronized (sessionId.intern()) {
Object sessionLocale = invocation.getInvocationContext().getSession().get(attributeName);
- if (sessionLocale != null && sessionLocale instanceof Locale) {
+ if (sessionLocale instanceof Locale) {
locale = (Locale) sessionLocale;
LOG.debug("Applied session locale: {}", locale);
}
@@ -324,7 +370,7 @@ public class I18nInterceptor extends AbstractInterceptor {
}
}
- protected class CookieLocaleHandler extends RequestOnlyLocaleHandler {
+ protected class CookieLocaleHandler extends RequestLocaleHandler {
protected CookieLocaleHandler(ActionInvocation invocation) {
super(invocation);
}
diff --git a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java
index 99b9ffb..e27ad43 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java
@@ -36,6 +36,7 @@ import org.springframework.mock.web.MockHttpSession;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -224,7 +225,6 @@ public class I18nInterceptorTest extends TestCase {
}
public void testCookieCreation() throws Exception {
-
prepare(I18nInterceptor.DEFAULT_COOKIE_PARAMETER, "da_DK");
final Cookie cookie = new Cookie(I18nInterceptor.DEFAULT_COOKIE_ATTRIBUTE, "da_DK");
@@ -243,6 +243,37 @@ public class I18nInterceptorTest extends TestCase {
assertNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should not create a locale object
}
+ public void testAcceptLanguageBasedLocale() throws Exception {
+ // given
+ request.setPreferredLocales(Arrays.asList(new Locale("da_DK"), new Locale("pl")));
+ interceptor.setLocaleStorage(null);
+ interceptor.setSupportedLocale("en,pl");
+
+ // when
+ interceptor.intercept(mai);
+
+ // then
+ assertNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should not be stored here
+ assertNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should not create a locale object
+ assertEquals(new Locale("pl"), mai.getInvocationContext().getLocale());
+ }
+
+ public void testAcceptLanguageBasedLocaleWithFallbackToDefault() throws Exception {
+ // given
+ request.setPreferredLocales(Arrays.asList(new Locale("da_DK"), new Locale("es")));
+
+ interceptor.setLocaleStorage(null);
+ interceptor.setSupportedLocale("en,pl");
+
+ // when
+ interceptor.intercept(mai);
+
+ // then
+ assertNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should not be stored here
+ assertNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should not create a locale object
+ assertEquals(Locale.US, mai.getInvocationContext().getLocale());
+ }
+
private void prepare(String key, Serializable value) {
Map<String, Serializable> params = new HashMap<>();
params.put(key, value);
@@ -256,9 +287,10 @@ public class I18nInterceptorTest extends TestCase {
interceptor.init();
session = new HashMap<>();
- ac = ActionContext.of(new HashMap<>()).bind();
- ac.setSession(session);
- ac.setParameters(HttpParameters.create().build());
+ ac = ActionContext.of(new HashMap<>())
+ .bind()
+ .withSession(session)
+ .withParameters(HttpParameters.create().build());
request = new MockHttpServletRequest();
request.setSession(new MockHttpSession());
@@ -286,7 +318,8 @@ public class I18nInterceptorTest extends TestCase {
}
static class CookieMatcher implements IArgumentMatcher {
- private Cookie expected;
+
+ private final Cookie expected;
CookieMatcher(Cookie cookie) {
expected = cookie;