Almost all the guidelines for developers suggests to switch languages using LocaleChangeInterceptor
in such way:
http://www.somesite.com/action/?lang=en
How can we make language code be an essential part of URL? For example:
http://www.somesite.com/en/action
As it stands now, there are hacky ways to handle this using some combination of Filters
and Interceptors
.
@Component
public class PathVariableLocaleFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(PathVariableLocaleFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String url = defaultString(request.getRequestURI().substring(request.getContextPath().length()));
String[] variables = url.split("/");
if (variables.length > 1 && isLocale(variables[1])) {
LOG.debug("Found locale {}", variables[1]);
request.setAttribute(LOCALE_ATTRIBUTE_NAME, variables[1]);
String newUrl = StringUtils.removeStart(url, '/' + variables[1]);
LOG.trace("Dispatching to new url \'{}\'", newUrl);
RequestDispatcher dispatcher = request.getRequestDispatcher(newUrl);
dispatcher.forward(request, response);
} else {
filterChain.doFilter(request, response);
}
}
private boolean isLocale(String locale) {
//validate the string here against an accepted list of locales or whatever
try {
LocaleUtils.toLocale(locale);
return true;
} catch (IllegalArgumentException e) {
LOG.trace("Variable \'{}\' is not a Locale", locale);
}
return false;
}
}
// --
public class LocaleAttributeChangeInterceptor extends HandlerInterceptorAdapter {
public static final String LOCALE_ATTRIBUTE_NAME = LocaleAttributeChangeInterceptor.class.getName() + ".LOCALE";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Object newLocale = request.getAttribute(LOCALE_ATTRIBUTE_NAME);
if (newLocale != null) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale.toString()));
}
// Proceed in any case.
return true;
}
}
// --
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleAttributeChangeInterceptor());
}
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
return new CookieLocaleResolver();
}
}
There should be core framework support for something like this.
Comment From: Olivier4477
Hello,
Today, how do I develop example.com/fr example.com/en? Why my question? Because I just realized that HandlerInterceptorAdapter is deprecated => it is necessary to use implements HandlerInterceptor if I understood correctly?
I find very little documentation on this subject or a lot on ?Lang=en ?Lang=fr but very old tutorials.
thank you
Comment From: rstoyanchev
There is nothing built-in for this indeed, but I think it could be done with much less code.
You could configure a path prefix globally for all request mappings, e.g. "/{locale}"
, see the reference docs. Then you don't need a filter to remove the prefix and forward. The HandlerInterceptor#preHandle
could then use PathPattern
or AntPathMatcher
, depending on which is configured for use, to extract the "locale" URI variable.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.