Over a million developers have joined DZone.

JSR 303 Loading Messages from an I18N Property File

· Java Zone

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

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.

Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.

Topics:

Published at DZone with permission of Mrabti Idriss, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}