How to create multilingual exception messages in Java Spring Boot

In some cases you may want to display exception messages on your website. Depending on how descriptive your message is, user can learn what happened and how to proceed. If your website supports only one language,  solution is simple. But what if your website supports multiple languages? This tutorial will show you how to do it in java and Spring Boot.


First, we need to create our messages

Let's create file that will store our messages.


invalid-param = Value {0} is incorrect!
no-access = You have no access to this resource!


This file will contain translations for our default language. We have to call it messages.properties and place it in our resources folder. Additionally we have to disable fallback to system locale. In your application.properties place this line:

spring.messages.fallback-to-system-locale=false

If we want to add other language, create similar file named messages_LANGUAGE.properties for example: messages_pl.properties

Now that we have our messages, we need to create exceptions


@Getter
public class BadRequestException extends RuntimeException{

    private final Object[] args;

    private final String messageName;

    public BadRequestException(String messageName, Object[] args){
        super(messageName);
        this.messageName = messageName;
        this.args = args;
    }
}

This exception is a template for all responses with status Bad Request. Fields messageName and args are used for getting message from our resource file. I have added Getter annotation from Lombok to generate getters. We can use this exception directly or create exception that extends it.


public class InvalidParamException extends BadRequestException{

    public InvalidParamException( String param) {
        super("invalid-param", new Object[]{param} );
    }
}


Let's create simple controller that will throw exceptions


@RestController
@RequestMapping("/api")
public class Controller {

    @GetMapping("/test/{param}")
    public ResponseEntity testExceptionWithParam(@PathVariable String param){
        if(param.equals("Secret")){
            return ResponseEntity.ok().body("Congratulations! You have found this secret message!");
        }else{
            throw new InvalidParamException(param);
        }
    }

    @GetMapping("/test")
    public ResponseEntity testSimpleException(){
        throw new BadRequestException("no-access", new Object[]{});
    }
}

There is nothing fancy here, just simple endpoints that throw exceptions. Note that there is no try and catch blocks. That’s because we will use exception handler.

Exception handler


@RestControllerAdvice
public class ResponseExceptionHandler extends ResponseEntityExceptionHandler
{
    private final MessageSource messageSource;

    public ResponseExceptionHandler(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @ExceptionHandler({BadRequestException.class})
    protected ResponseEntity handleBadRequestException(BadRequestException exception, Locale locale){
        String messageName = exception.getMessageName();
        Object[] args = exception.getArgs();

        String message = messageSource.getMessage(messageName, args, locale);
        return ResponseEntity.badRequest().body(message);
    }
}
This is the most important part. We define class that extends ResponseEntityExceptionHandler and we annotate it with RestControllerAdvice. We create method handleBadRequestException and annotate it with ExceptionHandler with exception class this method will catch. Additionally we add Locale as parameter. Spring will automatically inject proper locale. Inside function we use MessageSource to get message using name of the message and arguments we have in our exception. And then we return ResponseEntity with status we want. And that’s it. You can test it by changing language in your browser.