Spring Boot REST: Internationalization (i18n) Example

Internationalization (i18n) is a crucial consideration for modern applications, ensuring they are accessible and user-friendly for a global audience. Spring Boot simplifies the process of implementing i18n when developing dynamic views in a web application or building RESTful APIs.

In this comprehensive guide, we will explore the configuration, naming conventions, and practical examples to master i18n in Spring Boot.

At a very high level, we need to perform the following steps to add the i18n support in a Spring Boot REST API:

  • Add the translation files for all supported locales in the ‘/src/main/resources‘ folder
  • Configure LocaleResolver bean, if the default AcceptHeaderLocaleResolver is not needed.
  • Inject and use the MessageSource bean in the REST controller handler methods.

1. Maven

The i18n support is inbuilt in the core framework so we do not need to include any additional dependencies. We are good with adding only the web-starter module for writing the REST APIs.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
implementation 'org.springframework.boot:spring-boot-starter-web'

2. Adding Translation Files or Message Resource Bundles

Translation files store messages in different languages. These files follow a structured naming convention and provide a centralized way to manage the application’s text content.

2.1. Naming Rules for Translation Files

In the ‘src/main/resources‘ directory, create an i18n folder to house your translation files. The naming convention for these files is crucial for Spring Boot to locate and load the correct translations.

For example, consider creating files like messages_en.properties and messages_fr.properties for English and French translations, respectively.

# messages_en.properties
greeting.message=Hello, {0}!
error.notfound=Resource not available
# messages_fr.properties
greeting.message=Bonjour, {0}!
error.notfound=Ressource non disponible

In these files, the key greeting.message is associated with a language-specific greeting message.

3. The i18n Support in Spring Boot

To enable internationalization, the Spring boot autoconfiguration feature scans for resource bundles in the classpath and configures an instance of org.springframework.context.MessageSource.

3.1. Default Configuration

The default autoconfiguration is done by MessageSourceAutoConfiguration which kicks in the following conditions:

  • No bean is present with the name “messageSource“.
  • Presence of resource bundle with name messages.properties or name configured using ‘spring.messages.basename‘ property.

3.2. Custom Names for Translation Files

We can customize the name of the resource bundle using the property ‘spring.messages.basename‘ in the application.properties file.

spring.messages.basename=messages,config.i18n.messages

The auto-configuration creates the default ResourceBundleMessageSource instance and customizes it with other property values in the namespace ‘spring.messages.*‘.

3.3. NoSuchMessageException

The NoSuchMessageException exception indicates that there is no message available for the given key and locale i.e. when the application attempts to retrieve a message with a specific key from a MessageSource, but that key is not found in any of the configured message properties files.

{
  "timestamp": "2019-09-28T17:03:36.056+0000",
  "status": 500,
  "error": "Internal Server Error",
  "message": "No message found under code 'error.notfound' for locale 'en_US'.",
  "path": "/"
}

To handle a NoSuchMessageException, you can catch it in your code and provide a default or fallback message.

Alternatively, you can add a translation file (messages.properties without language suffix) that will used as a fallback if the locale-specific (e.g. messages_es.properties) does not contain any message key.

4. Locale Resolution with LocaleResolver

The LocaleResolver is a pivotal component for managing locales in Spring Boot applications. It determines the user’s locale based on factors like session attributes, cookies, or request parameters.

Here are several commonly used locale resolvers in Spring applications:

Locale ResolverDescription
AcceptHeaderLocaleResolverExtracts the locale from the Accept-Language header of the HTTP request. Commonly used where users can specify their preferred language through browser settings or REST APIs.
CookieLocaleResolverStores the user’s locale preference in a cookie in the browser and remembers the user’s language choice across sessions.
SessionLocaleResolverStores the user’s locale preference in the user session. Commonly used in web applications where the language choice should persist throughout a user’s session.
HeaderLocaleResolverRetrieves the locale from a specific request header. It allows flexibility in choosing a custom header for determining the user’s language preference.
UrlLocaleResolverExtracts the locale from the request URL. Most commonly seen in REST APIs.
SmartLocaleResolverAttempts to determine the user’s locale based on various factors, including the request, session, and headers.
FixedLocaleResolverAlways returns a fixed, pre-configured locale. Used in the application that needs to operate in a specific language regardless of user preferences.

By default, Spring boot uses Accept-Language header to determine the user locale.

Accept-Language: es_ES

4.1. Java Configuration

We can define the following configuration for any change in the names of translation files or default locale.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

@Configuration
public class AppConfig {

    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:i18n/messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(3600); // Cache for an hour
        return messageSource;
    }

    @Bean
    public LocaleResolver localeResolver() {
        AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
        localeResolver.setDefaultLocale(Locale.US); // Set the default locale if none is specified
        return localeResolver;
    }
}

In this configuration, we use ReloadableResourceBundleMessageSource to reload messages when they are updated. The setBasename() method specifies the base name for the message properties files.

4.2. Properties Configuration

We can further customize the behavior using the following properties, also.

spring.messages.always-use-message-format=false
spring.messages.basename=messages
spring.messages.cache-duration=
spring.messages.encoding=UTF-8
spring.messages.fallback-to-system-locale=true
spring.messages.use-code-as-default-message=false
  • always-use-message-format: controls whether the MessageFormat should always be applied to messages, regardless of the presence of placeholders.
  • basename: specifies the base name for the message properties files. It sets the common prefix for all message files.
  • cache-duration: sets the duration, in seconds, for which messages should be cached. If not set, caching is disabled. If set, messages will be cached for the defined duration. After the cache duration elapses, the messages will be reloaded.
  • encoding: sets the character encoding for message files.
  • fallback-to-system-locale – control what to do when the user requests a message (code) that does not exist for the requested locale – either because there is no message properties file for the language or just because the message file does not contain the message code. If set to ‘false‘, the default locale or a specified fallback locale will be used. If set to ‘true‘, the system locale will be used as a fallback.
  • use-code-as-default-message – setting to ‘true’ will default the message code if the message is not found in the properties file.

5. Adding i18n for Spring Boot REST APIs

When dealing with RESTful APIs, we need to get the reference of the MessageSource bean in the REST controller. For the demo, we created a simple @RestController that returns the locale-specific message. Notice that even without any configuration, Spring boot auto-configures MessageSource bean.

Now, let’s add a handler method that returns localized messages based on the ‘Accept-Language‘ header.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import java.util.Locale;

@RestController
public class GreetingController {

  private final MessageSource messageSource;

  @Autowired
  public RestI18nController(MessageSource messageSource) {
    this.messageSource = messageSource;
  }

  @GetMapping("/api/greet/{username}")
  public String greet(@PathVariable String username, 
    @RequestHeader(name = "Accept-Language", required = false) Locale locale) {

    String greetingMessage = messageSource.getMessage("greeting.message", new Object[]{username}, locale);
    return String.format("Locale: %s, Message: %s", locale, greetingMessage);
  }
}

In this REST controller, the greet endpoint takes advantage of the Accept-Language header to determine the user’s preferred locale. The MessageSource is then used to retrieve the localized greeting message.

5.1. Without ‘Accept-Language’ Header

It will resolve to the default locale file i.e. messages_en.properties.

curl -X GET http://localhost:8080/api/greet/John -I

The API response:

Locale: en, Message: Hello, John!

5.2. With ‘Accept-Language’ Header

Access the endpoint with the appropriate Accept-Language header indicating French.

curl -X GET http://localhost:8080/api/greet/John -H "Accept-Language: fr" -I

The API response:

Locale: fr, Message: Bonjour, John!

6. Conclusion

Spring boot provides excellent support for message localization and internationalization using its auto-configuration feature. All we have to do is provide locale-specific resource bundle properties and MessageSource is automatically configured for us.

We can start sending the ‘Accept-Language‘ header to receive locale-specific messages from the REST APIs.

Happy Learning !!

Sourcecode Download

Comments

Subscribe
Notify of
guest
5 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.