Core Java

JSR 303 loading messages from an I18N property file

Overview

This article will illustrate how to adapt the JSR 303 validation API to load messages from an I18n property file, and this by conserving all benefits of internationalisation and support for multiple languages.

To achieve this we are going to implement a custom MessageInterpolator which will be based upon Spring API for managing I18N messages.
 
 
 

Dependencies

Below the required maven dependencies to make this work, the Javax validation and Hibernate validation are not listed in here :

<dependencies>
     <dependency>
         <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.0.0.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework.webflow</groupId>
          <artifactId>spring-binding</artifactId>
          <version>2.3.2.RELEASE</version>
    </dependency>
</dependencies>

Configuration of MessageSource

The first step is the configuration of the MessageSource bean which is responsible of scanning and indexing the content of properties files.

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="basenames">
            <list>
                <value>com.myproject.i18n.MyMessages</value>
                <value>com.myproject.i18n.ErrorMessages</value>
            </list>
        </property>
    </bean>

MyMessages and ErrorMessages are the properties files we wanted to scan, the name of the files support the conventions for multiple language.

For example if our application must support english and french then we should have : MyMessages_en.properties and MyMessages_fr.properties.

Custom MessageInterpolator

In this custom MessageInterpolator we redefine the way JSR 303 resolve messages to display, we provide a custom implementation which uses Spring MessagesSource and the MessageBuild to search and prepare for the message to be displayed.

import java.util.Locale;

import javax.validation.MessageInterpolator;

import org.springframework.binding.message.MessageBuilder;
import org.springframework.context.MessageSource;

public class SpringMessageInterpolator implements MessageInterpolator {
    @Autowired
    private MessageSource messageSource, 

    @Override
    public String interpolate(String messageTemplate, Context context) {
        String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get("params");

        MessageBuilder builder = new MessageBuilder().code(messageTemplate);
        if (params != null) {
            for (String param : params) {
                builder = builder.arg(param);
            }
        }

        return builder.build().resolveMessage(messageSource, Locale.FRANCE).getText();
    }

    @Override
    public String interpolate(String messageTemplate, Context context, Locale locale) {
        String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get("params");

        MessageBuilder builder = new MessageBuilder().code(messageTemplate);
        if (params != null) {
            builder = builder.args(params);
        }

        return builder.build().resolveMessage(messageSource, local).getText();
    }
}

Usage on a custom JSR 303

Let say that we create a new JSR 303 validation annotation, which validate will check if a field is not blank. To use the custom Spring message interpolator, we need to declare a message on one of the properties files loaded by the Spring Message source, lets declare that on the ErrorMessages.properties:

{com.myproject.validation.NotBlank}    Mandatory field

Best practice is to name the key of the message like the complete classe name of our validation annotation, you are free to choose any key name you want but it must be between the brackets {} to work.

Our custom annotation will look like below :

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = NotBlankValidator.class)
public @interface NotBlank {
    String message() default "{com.myproject.validation.NotBlank";

    Class<?>[] groups() default {};

    String[] params() default {};

    Class<? extends Payload>[] payload() default {};
}

Please verify that the default value of the message attribute is the same as the one you put on the property file.

Thats it, now you can use the annotation normally like you do, and if you don’t provide a hardcoded message it will get loaded from the property file if is declared there.

Reference: JSR 303 loading messages from an I18N property file from our JCG partner Idriss Mrabti at the Fancy UI blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button