In this technical post, we will see how to manage different languages at your Spring Boot application aka. internationalization. NOTE: If you need to know what tools you need to have installed in your computer in order to create a Spring Boot basic project, please refer my previous post: Spring Boot. Spring Boot uses MessageSource
configured with a MessageSourceAutoConfiguration. These settings can be easily changed in the application.properties
file:
spring.messages.basename=i18n/messages
Once we create that line, you can define your application messages in the file resources/i18n/messages.properties
:
user.hello=Hello from internationalization!
Spring boot create a MessageSource
bean and is automatically added to the context, so you can use it in your services. Let’s create a new project using this command:
spring init --dependencies=web --build=gradle --language=java spring-boot-internationalization
Then we can create a LocaleResolver
class that will be responsible for defining user’s locale.
package com.jos.dem.springboot.internationalization.helper;
import java.util.List;
import java.util.Arrays;
import java.util.Locale;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
public class LocaleResolver extends AcceptHeaderLocaleResolver {
private static final List<Locale> LOCALES = Arrays.asList(new Locale("en"), new Locale("es"));
private Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public Locale resolveLocale(HttpServletRequest request) {
String language = request.getHeader("Accept-Language");
if (language == null || language.isEmpty()) {
return Locale.getDefault();
}
List<Locale.LanguageRange> list = Locale.LanguageRange.parse(language);
Locale locale = Locale.lookup(list, LOCALES);
return locale;
}
}
That’s it, here we have two locales supported: en and es. The locale should be passed in the header called “Accept-Language”. In Spring Boot we can read that header using HttpServletRequest
, please consider the following controller:
package com.jos.dem.springboot.internationalization.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import com.jos.dem.springboot.internationalization.services.LocaleService;
@RestController
class InternationalizationController {
@Autowired
private LocaleService localeService;
@GetMapping("/")
public String index(HttpServletRequest request){
return localeService.getMessage("user.hello", request);
}
}
As you can see we are creating an abstraction to use locale service and will be responsible for choosing right message according to specified locale.
package com.jos.dem.springboot.internationalization.services;
import javax.servlet.http.HttpServletRequest;
public interface LocaleService {
String getMessage(String code, HttpServletRequest request);
}
Here is the implementation:
package com.jos.dem.springboot.internationalization.services.impl;
import org.springframework.stereotype.Service;
import org.springframework.context.MessageSource;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import com.jos.dem.springboot.internationalization.helper.LocaleResolver;
import com.jos.dem.springboot.internationalization.services.LocaleService;
@Service
public class LocaleServiceImpl implements LocaleService {
@Autowired
private MessageSource messageSource;
@Autowired
private LocaleResolver localeResolver;
public String getMessage(String code, HttpServletRequest request){
return messageSource.getMessage(code, null, localeResolver.resolveLocale(request));
}
}
Under the resources folder we created two files: src/main/resources/i18n/messages.properties
and src/main/resources/i18n/messages_es.properties
. Here is the content of messages.properties
:
user.hello=Hello from internationalization!
And here is content of messages_es.properties
:
user.hello=¡Hola Internacionalización!
Now you can run the project:
gradle bootRun
In your browser go to this url: http://localhost:8080/ and you should see the message based in your browser language.
Here is our Junit Jupiter testing case, to cover LocaleResolver
functionality:
package com.jos.dem.springboot.internationalization;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import com.jos.dem.springboot.internationalization.helper.LocaleResolver;
class LocaleResolverTest {
@InjectMocks
private LocaleResolver resolver = new LocaleResolver();
@Mock
private HttpServletRequest request;
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
@DisplayName("should get locale default")
void shouldGetDefaultLocale() {
Locale result = resolver.resolveLocale(request);
assertEquals(Locale.getDefault(), result);
}
@Test
@DisplayName("should get en-US as locale")
void shodldGetEnLocale() {
when(request.getHeader("Accept-Language")).thenReturn("en-US,en;q=0.8");
Locale result = resolver.resolveLocale(request);
assertEquals(new Locale("en"), result);
}
@Test
@DisplayName("should get es-MX as locale")
void shouldGetEsLocale() {
when(request.getHeader("Accept-Language")).thenReturn("es-MX,en-US;q=0.7,en;q=0.3");
Locale result = resolver.resolveLocale(request);
assertEquals(new Locale("es"), result);
}
}
To browse the code go here, to download the project:
git clone git@github.com:josdem/spring-boot-internationalization.git
To run the test:
gradle test