Я реализовал нечто очень похожее, используя комбинацию Filter и Interceptor.
Фильтр извлекает первую переменную пути и, если она является допустимой локалью, устанавливает его в качестве атрибута запроса, удаляет его с начала запрошенного URI и перенаправляет запрос на новый URI.
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;
}
}
Перехватчик очень похож на LocaleChangeInterceptor
, он пытается получить локаль из атрибута запроса и, если локаль найдена, он устанавливает его на LocaleResolver
.
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;
}
}
После того, как они у вас есть, вам нужно настроить Spring на использование перехватчика и LocaleResolver
.
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleAttributeChangeInterceptor());
}
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
return new CookieLocaleResolver();
}
И добавьте фильтр к AbstractAnnotationConfigDispatcherServletInitializer
.
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new PathVariableLocaleFilter() };
}
Я не проверил его полностью, но, похоже, он работает до сих пор, и вам не нужно прикасаться к контроллерам, чтобы принять переменную пути {locale}
, она должна просто работать "из коробки". Возможно, в будущем у нас будет автоматическое решение Spring locale as variable / subfolder, так как кажется, что все больше и больше сайтов его принимают, и, по мнению некоторых, это способ пойти .