Internationalization (i18n) mechanism of Spring Web MVC is based on the interface LocaleResolver. The implementations of this interface apply different strategies to resolve locale information of the HTTP request client.
When DispatcherServlet (the front controller) receives an HTTP request, it looks for the configured LocaleResolver; if it finds one, it tries to use it to set the client's locale based on the request content.
|
By default, AcceptHeaderLocaleResolver implementation is used, which utilizes the HTTP request header's 'Accept-Language' value to resolve client locale. It actually internally uses ServletRequest.getLocale() which applies the logic of constructing Locale from the header. In this tutorial we are going to see some examples to make use of this default LocaleResolver. |
Working with client's Locale
The controller:
We can retrieve client's Locale instance as a controller parameter, so that we can apply whatever i18n logic we want.
package com.logicbig.example;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Locale;
@Controller
public class I18nExampleController {
@GetMapping("/test1")
@ResponseBody
public String handleRequest (Locale locale) {
return String.format("Request received. Language: %s, Country: %s %n",
locale.getLanguage(), locale.getDisplayCountry());
}
}
Running the example
To try examples, run embedded Jetty (configured in pom.xml of example project below):
mvn jetty:run
$ curl -s "http://localhost:8080/test1" Request received. Language: en, Country: United States
Let's try it with some other locale. The 'Accept-Language' header is sent implicitly by the client web browser. For testing purposes, we can modify the browser settings with a different display language. I prefer to use curl to explicitly send a different 'Accept-Language' header. In the following example we are sending 'fr-FR' with the HTTP request:
$ curl -s -H "Accept-Language: fr-FR" "http://localhost:8080/test1" Request received. Language: fr, Country: France
Using MessageSource to display external i18n messages
To display localized external messages, we have to provide an instance of MessageSource as a bean. Please check out our Spring core i18n tutorial.
With a traditional Spring MVC configuration, we have to explicitly register an implementation:
package com.logicbig.example;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@EnableWebMvc
@Configuration
@ComponentScan
public class WebConfig implements WebMvcConfigurer {
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasenames("messages");
source.setDefaultEncoding("UTF-8");
return source;
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/pages/", ".jsp");
}
}
If we are using Spring Boot, then we can still register the MessageSource as above (but with @SpringBootApplication). If we want to take advantage of Boot's auto-configuration, we just have to place messages.properties along with a couple of optional messages_xy.properties (where xy represents language codes) files in the classpath.
In this example, we are going to place the following property files:
src/main/resources/messages.propertiesapp.name = resource bundle test invoked by {0}
src/main/resources/messages_fr.propertiesapp.name=test de regroupement de ressources invoqué par {0}
Resolving messages in a controller
package com.logicbig.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Locale;
@Controller
public class I18nExampleController2 {
@Autowired
MessageSource messageSource;
@GetMapping("/test2")
@ResponseBody
public String handleRequest (Locale locale) {
return messageSource.getMessage(
"app.name", new Object[]{"Joe"}, locale);
}
}
Output
$ curl -s "http://localhost:8080/test2" resource bundle test invoked by Joe
$ curl -s -H "Accept-Language: fr-FR" "http://localhost:8080/test2" test de regroupement de ressources invoqu' par Joe
Resolving messages in a JSP page
In a JSP file, to resolve i18n messages by code, we will use spring:message tag.
src/main/webapp/WEB-INF/pages/myPage.jsp:
<%@ page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<html>
<body>
<h3>A JSP page</h3>
<spring:message code="app.name" arguments="${by}"/>
</body>
</html>
The controller
@Controller
public class I18nExampleController3 {
@RequestMapping("/test3")
public String handleRequest (Model model) {
model.addAttribute("by", "Joe");
return "myPage";
}
}
Output
$ curl -s "http://localhost:8080/test3"
<html> <body> <h3>A JSP page</h3> resource bundle test invoked by Joe </body> </html>
$ curl -s -H "Accept-Language: fr-FR" "http://localhost:8080/test3"
<html> <body> <h3>A JSP page</h3> test de regroupement de ressources invoqu' par Joe </body> </html>
Custom Locale selection
Some web applications might want to provide options to the users to select a preferred language rather than using the browser-generated Accept-Language header. They might use a dropdown language selection component on the home page or whatever method they choose to get that information from the users. Once the user has selected a language, it should be remembered by the server side for further visits/interactions. Spring addresses these kinds of use cases (where a custom locale setting must be remembered) by providing the following implementations of LocaleResolver:
There's one more implementation of LocaleResolver: FixedLocaleResolver, which always returns a fixed default locale.
In the next couple of tutorials, we will explore the remaining implementations with examples.
Example ProjectDependencies and Technologies Used: - spring-webmvc 7.0.6 (Spring Web MVC)
Version Compatibility: 3.2.9.RELEASE - 7.0.6 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- spring-test 7.0.6 (Spring TestContext Framework)
- jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
- jakarta.servlet.jsp.jstl 3.0.1 (Jakarta Standard Tag Library Implementation)
- JDK 25
- Maven 3.9.11
|