Spring MVC: возврат к неизвестному языковому коду в параметре uri
Я пытаюсь создать мое первое приложение Spring MVC 4 с поддержкой i18n и думал, как я могу использовать стандартную/резервную локаль в случае, если пользователь манипулирует параметром uri языка для не существующего или поддерживаемого языкового стандарта
Например
http://localhost.de?lang=abc
Im, используя код
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.GERMAN);
return sessionLocaleResolver;
}
который работает вообще, если я открываю URL-адрес в первый раз, но, похоже, он не работает для случая, который я описывал.
Я знаю, что есть механизм, который будет использовать файл свойств сообщений по умолчанию, но я бы хотел установить для этого случая стандартную/резервную локаль.
Мне нужно реализовать, возможно, настраиваемый фильтр?
Ответы
Ответ 1
Мое предложение заключалось бы в подклассе SessionLocaleResolver и переопределении метода getLocale:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
private static Set<Locale> allowedLocales;
static {
HashSet<Locale> allowed = new HashSet<>();
allowed.add(Locale.GERMAN);
allowed.add(Locale.CANADA);
allowedLocales = Collections.unmodifiableSet(allowed);
}
@Bean
LocaleResolver localeResolver() {
return new LimitedSessionLocaleResolver();
}
class LimitedSessionLocaleResolver extends SessionLocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = super.resolveLocale(request);
if (!allowedLocales.contains(locale)) {
return determineDefaultLocale(request);
}
return locale;
}
}
}
Это не модифицирует классы Spring любым основным способом и, вероятно, будет работать без проблем в обозримом будущем.
Ответ 2
Вероятно, далек от совершенства, но это то, что я построил...
Мне также нужно сказать, что я немного изменил механизм выбора языка по умолчанию из-за потребностей SEO, и я решил изменить язык, не используя параметр get, но используя вместо этого первую часть моего пути uri для выбранного языка.
Например:
http://myurl.com/en/test.html вместо http://myurl.com/test.html?lang=de
В моей конфигурации на основе аннотаций:
@Bean
public LocaleResolver localeResolver() {
UriLocaleResolver uriLocaleResolver = new UriLocaleResolver();
return uriLocaleResolver;
}
Локальный резольвер
public class UriLocaleResolver implements LocaleResolver {
private final Logger logger = LoggerFactory.getLogger(getClass());
private Locale locale = null;
@Autowired
private LocalizationService localizationService;
@Override
public Locale resolveLocale(final HttpServletRequest servletRequest) {
if (locale != null) {
return locale;
}
String languageIsoCode = null;
try {
languageIsoCode = ((String)servletRequest.getAttribute(RequestKey.LANGUAGE_ISO_CODE)).toLowerCase();
}
catch (Exception e) { }
if (StringUtils.isBlank(languageIsoCode) || !localizationService.getSupportedLocaleLanguageIsoCodes().contains(languageIsoCode)) {
logger.trace("Couldn't find valid language iso code. Using default locale '{}'", GlobalConstant.DEFAULT_LOCALE);
return GlobalConstant.DEFAULT_LOCALE;
}
logger.trace("Found language iso code '{}'", languageIsoCode);
return new Locale(languageIsoCode);
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
this.locale = locale;
}
}
Фильтр, который проверяет правильный код языка в uri...
@Component
public class UriLocalizationFilter extends OncePerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private LocalizationService localizationService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String uri = request.getRequestURI().substring(request.getContextPath().length());
String[] pathParts = uri.split("/");
if (!uri.startsWith(GlobalConstant.FRONTEND_RESOURCE_PATH_PREFIX) && pathParts.length >= 1 && pathParts[1].length() == 2) {
String originalLanguageIsoCode = pathParts[1];
String lowerCaseLanguageIsoCode = originalLanguageIsoCode.toLowerCase();
if (localizationService.getSupportedLocaleLanguageIsoCodes().contains(lowerCaseLanguageIsoCode)) {
logger.debug("Found valid language iso code {}", lowerCaseLanguageIsoCode);
}
else {
logger.debug("Found invalid language iso code {}. Using default language iso code {}.", lowerCaseLanguageIsoCode, GlobalConstant.DEFAULT_LOCALE.getLanguage());
lowerCaseLanguageIsoCode = GlobalConstant.DEFAULT_LOCALE.getLanguage();
}
String newUrl = StringUtils.removeStart(uri, '/' + originalLanguageIsoCode);
request.setAttribute(RequestKey.LANGUAGE_ISO_CODE, lowerCaseLanguageIsoCode);
logger.debug("Dispatching to new url '{}'", newUrl);
request.getRequestDispatcher(newUrl).forward(request, response);
}
else {
filterChain.doFilter(request, response);
}
}
public void setLocalizationService(LocalizationService localizationService) {
this.localizationService = localizationService;
}
}
}
Внешний путь - это "ресурсы" в моем случае, когда все статические файлы, такие как js, css, шрифты и т.д., кладут.
localizationService.getSupportedLocaleLanguageIsoCodes() - это набор, содержащий в настоящее время три языковых кода (en, ru, de).
Поэтому в случае неправильного кода языка, такого как abc im, выполняющего переадресацию с моим дефолтным языком "de". Для меня это было/приемлемым решением, потому что неправильный код языка в uri означает, что ури манипулировали пользователем...
Как я сказал, это может быть не "решение"; например, im не уверен, как и если он работает с файлами cookie и/или "помнит меня" (используя spring security)... просто не успел проверить его еще...