In the previous sections, we saw how to create an interceptor (ProcessingTimeLogInterceptor
) and configure it in our web application context. Spring provides some pre-built interceptors that we can configure in our application context as and when needed. One such pre-built interceptor is LocaleChangeInterceptor
, which allows us to change the current locale on every request and configures LocaleResolver
to support internationalization.
Internationalization means adapting computer software to different languages and regional differences. For example, if you are developing a web application for a Dutch-based company, they may expect all the web page text to be displayed in the Dutch language, use the Euro for currency calculations, expect a space as a thousand separator when displaying numbers, and use a "," (comma) as a decimal point. On the other hand, when the same Dutch company wants to open a market in America, they expect the same web application to be adapted for American locales; for example, the web pages should be displayed in English, dollars should be used for currency calculations, numbers should be formatted with "," (comma) as the thousand separator, a "." (dot) acts as a decimal point, and so on.
The technique for designing a web application that can automatically adapt to different regions and countries without needing to be re-engineered is called internationalization, sometimes shortened to i18n (i-eighteen letters-n).
In Spring MVC, we can achieve internationalization through LocaleChangeInterceptor
(org.springframework.web.servlet.i18n.LocaleChangeInterceptor
). The LocaleChangeInterceptor
allows us to change the current locale for every web request via a configurable request parameter.
In Chapter 4, Working with Spring Tag Libraries, we have seen how to externalize text messages in the add products page; now we are going to add internationalization support for the same add products page (addProducts.jsp
), because in Spring MVC, prior to internationalizing a label, we must externalize that label first. Since we already externalized all the label messages in the add products page (addProducts.jsp
), we can proceed to internationalize the add products page.
Technically we can add support for as many languages as we want with internationalization, but for demonstration purposes I am going to show you how to make our add products page with Dutch language support:
messages_nl.properties
under /src/main/resources
in your project, add the following lines in it, and save the file:addProduct.form.productId.label = Nieuw product ID addProduct.form.name.label = Naam addProduct.form.unitPrice.label = prijs per eenheid addProduct.form.manufacturer.label = fabrikant addProduct.form.category.label = categorie addProduct.form.unitsInStock.label = Aantal op voorraad addProduct.form.description.label = Beschrijving addProduct.form.condition.label = Product Staat addProduct.form.productImage.label = product afbeelding
addProduct.jsp
and add the following set of tags right after the <body>
tag:<section> <div class="pull-right" style="padding-right:50px"> <a href="?language=en" >English</a>|<a href="? language=nl" >Dutch</a> </div> </section>
WebApplicationContextConfig.java
, and add one more bean definition for the locale resolver as follows:@Bean public LocaleResolver localeResolver(){ SessionLocaleResolver resolver = new SessionLocaleResolver(); resolver.setDefaultLocale(new Locale("en")); return resolver; }
addInterceptors
method to configure one more interceptor in our InterceptorRegistry
as follows:@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new ProcessingTimeLogInterceptor()); LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); localeChangeInterceptor.setParamName("language"); registry.addInterceptor(localeChangeInterceptor); }
http://localhost:8080/webstore/market/products/add
URL; you will be able to see our regular add products page with two extra links in the top-right corner to choose the language:LocaleChangeInterceptor
will add a request parameter called language
to the web request, we need to add this language
request parameter to our whitelisting set in our ProductController
. Open our ProductController
and, within the initialiseBinder
method, add the language
request parameter to the whitelisting set as follows: binder.setAllowedFields("productId",
"name",
"unitPrice",
"description",
"manufacturer",
"category",
"unitsInStock",
"condition",
"productImage",
"language");
In step 1, we just created a property file called messages_nl.properties
. This file acts as a Dutch-based message source for all our externalized label messages in the addProducts.jsp
file. In order to display the externalized label messages, we used the <spring:message
>
tag in our addProducts.jsp
file.
But by default, the <spring:message
>
tag will read messages from the messages.properties
file only; we need to make a provision for our end user to switch to the Dutch locale when they view the webpage, so that the label messages can come from the messages_nl.properties
file. We provided such a provision through a locale-choosing link in addProducts.jsp
, as mentioned in step 2:
<a href="?language=en" >English</a>|<a href="?language=nl" >Dutch</a>
In step 2, we created two links, one each to choose English or Dutch as the preferred locale. When the user clicks on one of these links, it will add a request parameter called language
to the URL with the corresponding locale value. For example, when we click on the English link on the add products page at runtime, it will change the request URL to http://localhost:8080/webstore/products/add?language=en
; similarly if you click on the Dutch link, it will change the request URL to http://localhost:8080/webstore/products/add?language=nl
.
In step 3, we created a SessionLocaleResolver
bean in our web application context as follows:
@Bean public LocaleResolver localeResolver(){ SessionLocaleResolver resolver = new SessionLocaleResolver(); resolver.setDefaultLocale(new Locale("en")); return resolver; }
SessionLocaleResolver
is the one that sets the locale attribute in the user's session. One important property of SessionLocaleResolver
is defaultLocale
. We assigned en
as the value for the default locale, which indicates that by default our page should use English as its default locale.
In step 4, we created a LocaleChangeInterceptor
bean and configured it in the existing interceptor list:
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new ProcessingTimeLogInterceptor()); LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); localeChangeInterceptor.setParamName("language"); registry.addInterceptor(localeChangeInterceptor); }
We assigned the string language
as the value for the paramName
property in LocaleChangeInterceptor
. There is a reason for this because, if you notice, in step 2 when we created the locale choosing link in add products page (addProduct.jsp
), we used the same parameter name as the request parameter within the <a>
tag:
<a href="?language=en" >English</a>|<a href="?language=nl" >Dutch</a>
This way we can give a hint to LocaleChangeInterceptor
to choose the correct user-preferred locale. So whatever parameter name you plan to use in your URL, use the same name as the value for the paramName
property in LocaleChangeInterceptor
. One more thing: whatever value you have given to the language
request parameter in the link should match the translation message source file suffix. For example, in our case we have created a Dutch translation message source file, messages_nl.properties
; here the suffix is nl
and messages.properties
without any suffix is considered the default for the en
suffix. That's why in step 2 we have given nl
and en
as the values for the language
parameters for Dutch and English, respectively:
<a href="?language=en" >English</a>|<a href="?language=nl" >Dutch</a>
So finally, when we run our application and enter the http://localhost:8080/webstore/market/products/add
URL, you will see our regular add products page with two extra links in the top-right corner to choose the language.
Clicking on Dutch changes the request URL to http://localhost:8080/webstore/market/products/add?language=nl
, which brings up the LocaleChangeInterceptor
and reads the Dutch-based label messages from messages_nl.properties
.
Note that, if we do not give a language parameter in our URL, Spring will use the default message source file (messages.properties
) for translation; if we give a language parameter, Spring will use that parameter value as the suffix to identify the correct language message source file (messages_nl.properties
).
As I already mentioned just for demonstration purposes, I have internationalized a single web page (addProducts.jsp
). I encourage you to internationalize the product detail web page (product.jsp
) in our project. You can use the Google translate service (https://translate.google.com/) to find out the Dutch translation for labels. Along with this, try to add language support for one more language of your choice.