Im trying to build my first Spring MVC 4 app with i18n support and was thinking how i can use a default/fallback locale in case of the user is manipulating the language uri parameter to a non existing or supported locale For example http://localhost.de?lang=abc
Im using the code
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.GERMAN);
return sessionLocaleResolver;
}
which works in general if i open the url the very first time but it seems not to work for the case i was describing. I know there is a mechanism which would use the default messages properties file but i would like to set a default/fallback locale for this case. Do i need to implement maybe a custom filter?
Spring mvc will fallback automatically to messages. properties file, when the lang parameter does not match with any of the i18n files availables in your application.
The Spring Web model-view-controller (MVC) framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale and theme resolution as well as support for uploading files.
tag will be use to activate Spring MVC annotation scanning capability which allows to make use of annotations like @Controller and @RequestMapping etc.
My suggestion would be to subclass the SessionLocaleResolver and override the getLocale method:
@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;
}
}
}
This does not modify the Spring classes in any major way and is probably going to work without issues for the foreseeable future.
Probably far from being perfect but this is what i built...
I also need to say that i changed the default language select mechanism a bit because of SEO needs and i decided to change the language not by using a get parameter but using instead the first part of my uri path for the selected language. For example: http://myurl.com/en/test.html instead of http://myurl.com/test.html?lang=de
In my annotation based configuration:
@Bean
public LocaleResolver localeResolver() {
UriLocaleResolver uriLocaleResolver = new UriLocaleResolver();
return uriLocaleResolver;
}
The locale resolver
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;
}
}
The filter which checks for a valid language code in the 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;
}
}
}
Frontend path is "resources" in my case where all static files like js, css, fonts etc are laying. localizationService.getSupportedLocaleLanguageIsoCodes() is a Set containing currently three language codes (en, ru, de). So in case of a wrong language code like abc im doing a forward with my default language "de". For me it was/is acceptable solution cause having a wrong language code in the uri means that the uri was manipulated by the user...
Like i said its maybe not "the" solution; for example im not sure how and if it works with cookies and/or "remember me" authentications (using spring security)...just had no time to test it yet...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With