mirror of
https://github.com/apache/struts.git
synced 2026-08-07 23:57:03 +00:00
Merge pull request #451 from apache/WW-4929-support-accept-language
[WW-4929] Adds support for Accept-Language header when looking up Locale
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user