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.