Preventing The I18n Process of Spring MVC from Falling Back to System Locale

One day we decided to optimize the translation process of one of our web applications to reduce the number of resource files from three to two. Our web application supported two languages (Finnish and English), and the default resource file had the exactly same content than the resource file for the Finnish language. Of course this caused us extra work when new strings were added to the user interface. So, we decided to remove the resource file for the Finnish language to ease our pain. That worked perfectly well on the local development environment, but when the software was installed to our testing environment, it did not work. No matter what we did, the user interface was always translated to English.

It was time to think. We had following configuration in the configuration file of Spring application context:

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="basenames">
        <list>
            <value>classpath:i18n/messages</value>
        </list>
    </property>
</bean>

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    <property name="defaultLocale" value="fi" />
</bean>

<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="lang" />
</bean>

After some digging and browsing the Javadocs of Spring Framework, I found out that we had forgotten the default behavior of java.util.ResourceBundle. The locale of the server was EN_US, and after the resource file for Finnish language was not found, resource file for English language was used instead. Luckily the Javadocs revelead that the org.springframework.context.support.RealoadableResourceBundleMessageSource class contains a property called fallbackToSystemLocale, which can be used to override the default behavior of java.util.ResourceBundle. When the value of this property is set to false, the default resource file is used, when the resource file for the asked locale is not found.

The working configuration is following:

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="basenames">
        <list>
            <value>classpath:i18n/messages</value>
        </list>
    </property>
    <property name="fallbackToSystemLocale" value="false"/>
</bean>

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    <property name="defaultLocale" value="fi" />
</bean>

<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="lang" />
</bean>

So, what did we learn from this:

  1. We were reminded of the default behavior of java.util.ResourceBundle (Remembering the basics is really important).
  2. We cannot really trust the locale if the deployment environment is the same than the locale of our locale development environment. Thus, in this use case the fallbackToSystemLocale property of the org.springframework.context.support.RealoableResourceBundleMessageSource class should always be set to false.
7 comments… add one
  • Foo Apr 16, 2010 @ 15:31

    This is a nice thing to know. Thank you for explaining this.

  • Olivier Jaquemet Apr 16, 2013 @ 17:39

    Hi All, Petri,

    I'd like to share this little tip for readers of this post who are not using Spring but simply stumbled on your post looking for information on this behavior of the ResourceBundle.

    As explained by Petri, if you are using java ResourceBundle, the default behavior of the JVM implementation will be to use the system default local as a fallback.

    Since Java 1.6 you can change this behavior by specifying a ResourceBundle.Control instance when retrieving your ResourceBundle.

    Example :

    * Default behavior, using system default local as a fallback :

    
    ResourceBundle bundle =  ResourceBundle.getBundle("com.example.Messages", someLocale);
    
    

    * Custom behavior, using only Locale specified as parameter :

    
    ResourceBundle.Control NO_FALLBACK_CONTROL =
       ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT);
    ResourceBundle bundle =  
       ResourceBundle.getBundle("com.example.Messages", locale, NO_FALLBACK_CONTROL);
    
    
    • Petri Apr 16, 2013 @ 18:19

      Hi Olivier,

      thank you for your contribution.

      It is a useful tip and the best part of it is that I had not heard about it. In other words, I learned something new today!

  • Nacho Sep 11, 2014 @ 19:01

    Thank you Petri, you make my day. I had this problem when deploying to a server with a different locale than my local machine.

    • Petri Sep 12, 2014 @ 12:33

      You are welcome!

  • Arho Huttunen Oct 2, 2015 @ 12:17

    Wow, I was just struggling with this issue. We just recently moved our CI from one machine to another and tests started failing because of localization. I stumbled upon this post and this was actually a life saver. The reason was the the default locale of the older machine was Finnish and the one on the newer machine was English. Thanks for sharing.

    • Petri Oct 2, 2015 @ 19:57

      Hi Arho,

      WOW. It was amazing to hear that this old post was useful to you.

Leave a Reply