Friday, October 29, 2010

i18n Locale configuration with Spring

i18n with Spring is fairly easy.

First of all, you'll need to declare a bean named localeResolver.  DispatcherServlet will automatically looking for this bean when a request comes in.  Spring has AcceptHeaderLocaleResolver, CookieLocaleResolver, and SessionLocaleResolver.  In my case, I'll use SessionLocaleResolver to go with user sessions.

Then you'll need to define a LocaleChangeInterceptor bean and list it in your url handler mapping's interceptors list.

Here is my spring bean declaration:
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="lang" />
</bean>

<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

<bean id="handlerMapping"
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors">
        <list>
            <ref bean="localeChangeInterceptor" />
        </list>
    </property>
</bean>

With above configuration, I can change the locale to it_IT with a link like http://localhost:8080/myApp/home.do?lang=it_IT

In Java, there are serveral ways to get the locale:
1. If you know which locale resolver, in my case SessionLocaleResolver, I use
Locale locale = (Locale) request.getSession().getAttribute(
    SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME);
2. A universal way to get the locale
Locale locale = RequestContextUtils.getLocale(request);
3. Spring documentation says using RequestContext.getLocale() to get the locale.  However, getLocale is not a static method in RequestContext.

To get the locale in JSP page:
${sessionScope['org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE']}

If you are using displaytag, you'll need to add following configuration to displaytag.properties
locale.resolver=org.displaytag.localization.I18nSpringAdapter

After all this, there is still one more problem -- no default Locale before user pick any.
There are couple ways to solve this:
1. If you want to force use a certain Locale, LocaleResolver has a setDefaultLocale method, so specify default Locale in localeResolver bean and we now have:

<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    <property name="defaultLocale">
        <bean class="java.util.Locale">
            <constructor-arg index="0" value="it"/>
            <constructor-arg index="1" value="IT"/>
        </bean>
    </property>
</bean>
One downside with this approach.  In JSP page though, the default Locale value can not be picked up using Expression Language above.

2. If you want to use the user's browser's language preference, then you need to manually set the locale:
In Java:
request.getSession().setAttribute(
    SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,
    request.getLocale());
In JSP (not actually setting the default locale in session, but use the browser default when there is no locale in session):
<c:set var="language">${sessionScope['org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE']}</c:set>
<c:if test="${empty language}">
    <c:set var="language">${pageContext.request.locale}</c:set>
</c:if>