Spring internationalization(i18n) example for response and errors

UPDATED: 19 May 2018
Nowadays,  your application has users from around the world. For ease of your user, you want them to use web application in their local language. You must consider internationalization (i18n) while creating your web application.

In following example we will learn how you can apply internationalization to your spring application. In my previous articles we learned Creating first rest api in spring-boot and you can browse all spring article for starter.

Language specific properties file
Create properties file under src/main/resource folder for individual language you want to support in your application. We create package locale under folder and placed all file in it.

messages_de.properties
springboot.hello=Hallo
springboot.customerror=benutzerdefinierte Fehlermeldung
messages_en.properties
springboot.hello=Hello
springboot.customerror=custom error message
messages_fr.properties
springboot.hello=Bonjour
springboot.customerror=message d'erreur personnalisé
messages_zh.properties
For Chinese character you have to convert character / word into Unicode. How to convert Chinese character or word to Unicode online?
springboot.hello=\u4f60\u597d
springboot.customerror=\u81ea\u5b9a\u4e49\u9519\u8bef\u6d88\u606f

Source code (ApplicationConfig.java)
Load your language properties file in spring context using ReloadableResourceBundleMessageSource.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @author javaQuery
 * @since 2018-02-18
 * @github https://github.com/javaquery/spring-boot-examples
 */
@Configuration
public class ApplicationConfig extends WebMvcConfigurerAdapter{
    @Bean
    public ReloadableResourceBundleMessageSource messageSource(){
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:locale/messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
 
    /**
     * Internationalization for parameter and payload validation messages. 
     * @return
     */
    @Bean
    public LocalValidatorFactoryBean validator() {
       LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
       bean.setValidationMessageSource(messageSource());
       return bean;
    }

    @Override
    public Validator getValidator() {
       return validator();
    }
}

Source code (Application.java)
Add ApplicationConfig.java file package com.javaquery.examples.springboot.main.config in Application.java for component scan.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

/**
 * @author javaQuery
 * @since 2018-01-31
 * @github https://github.com/javaquery/spring-boot-examples
 * 
 * @change message internationalization (locale)
 */
@SpringBootApplication
@ComponentScan(basePackages = { "com.javaquery.examples.springboot.rest",  "com.javaquery.examples.springboot.main.config"})
@EntityScan("com.javaquery.examples.springboot.model")
@EnableJpaRepositories("com.javaquery.examples.springboot.model.repositories")
public class Application {
    public static void main(String[] args) {
       SpringApplication.run(Application.class, args);
    }
}

Source code (LocaleController.java)
import java.util.Locale;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Springboot internationalization example
 * @author javaQuery
 * @since 2018-04-06  
 */
@RestController
@RequestMapping("/api")
@Validated
public class LocaleController {
 
    @Autowired
    private MessageSource messageSource; 
 
    /**
     * Response internationalization.
     * @param name
     * @param locale
     * @return
     */
    @GetMapping("/locale")
    public ResponseEntity<?> sayHello(@RequestParam("name") String name, Locale locale){
       String localeHello = messageSource.getMessage("springboot.hello", null, locale);
       return ResponseEntity.ok(localeHello + " " + name);
    }
 
    /**
     * Validation parameter internationalization. 
     * @param firstname
     * @param lastname
     * @param notEmptyParam
     * @param locale
     * @return
     */
    @GetMapping("/locale/param")
    public ResponseEntity<?> parameterValidation(@Size(min = 1, max = 5, message = "firstname {javax.validation.constraints.Size.message}") @RequestParam("firstname") String firstname,
       @NotEmpty(message = "lastname {org.hibernate.validator.constraints.NotEmpty.message}") @RequestParam("lastname") String lastname,
       @NotEmpty(message = "{springboot.customerror}") String notEmptyParam, Locale locale){
         String localeHello = messageSource.getMessage("springboot.hello", null, locale);
         return ResponseEntity.ok(localeHello + " " + firstname + " " + lastname);
    }
}
We used properties file key springboot.hello to send language specific response of "Hello".

Internationalization for error messages
parameterValidation method demonstrate use of internationalization code for validation.
 - You can create your own custom code like {springboot.customerror}.
 - Use inbuilt codes. You find out internationalization code from source of annotation.


Enable Internationalization
To enable internationalization for http request send header accept-language in your request.

Request/Response
cURL POST localhost:8080/api/locale?name=vicky
-H 'accept-language: fr'
-code 200
-body Bonjour vicky
cURL POST localhost:8080/api/locale/param?firstname=VickyV&lastname=Thakor
-H 'accept-language: de'
-code 400
-body {"errors":["firstname muss zwischen 1 und 5 liegen","benutzerdefinierte Fehlermeldung"]}

0 comments :