11

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?

4
  • Does nobody have a idea? Was searching a lot but couldnt find a solution or idea yet.... Commented Jun 17, 2015 at 13:12
  • What happens when you navigate to localhost.de?lang=abc? What locale is used in this case? Commented Aug 19, 2015 at 8:00
  • I cant tell you anymore as i built already a filter which checks the language code. If my app doesnt support this language im setting it manually to my default language. So in your case it would be set to "de" / german... Commented Aug 19, 2015 at 10:29
  • You can post your filter code as an answer, if you wish. It would be helpful for me and maybe others to see it. Commented Aug 19, 2015 at 11:14

3 Answers 3

8
+50

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.

Sign up to request clarification or add additional context in comments.

Comments

5

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...

Comments

0

A very late answer, but I thought the easiest way to set a default locale in spring mvc is simply set a i18n file properties with your default language.

For example if you have two languages available in your application (suppose english and deutsch), you've this files:

messages_de.properties
messages_en.properties

Then you can simply copy the messages_de.properties to messages.properties, then you've:

messages_de.properties
messages_en.properties
messages.properties

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.