adarshr adarshr - 1 year ago 274
Java Question

Email Internationalization using Velocity/FreeMarker Templates

How can I achieve i18n using a templating engine such as Velocity or FreeMarker for constructing email body?

Typically people tend to create templates like:

<h3>${message.hi} ${user.userName}, ${message.welcome}</h3>
${}<a href="mailto:${user.emailAddress}">${user.emailAddress}</a>.

And have a resource bundle created with properties like:

message.welcome=Welcome to Spring! here to send email.

This creates one basic problem: If my
files becomes large with many lines of text, it becomes tedious to translate and manage each of them in separate resource bundle (
) files.

What I am trying to do is, have a separate
file created for each language, something like
mytemplate_en_gb.vm, mytemplate_fr_fr.vm, mytemplate_de_de.vm
and then somehow tell Velocity/Spring to pick up the right one based on the input Locale.

Is this possible in Spring? Or should I be looking at perhaps more simple and obvious alternative approaches?

Note: I have already seen the Spring tutorial on how to create email bodies using templating engines. But it doesn't seem to answer my question on i18n.

Answer Source

It turns out using one template and multiple files wins over having multiple templates.

This creates one basic problem: If my .vm files becomes large with many lines of text, it becomes tedious to translate and manage each of them in separate resource bundle (.properties) files.

It is even harder to maintain if your email structure is duplicated over multiple .vm files. Also, one will have to re-invent the fall-back mechanism of resource bundles. Resource bundles try to find the nearest match given a locale. For example, if the locale is en_GB, it tries to find the below files in order, falling back to the last one if none of them is available.


I will post (in detail) what I had to do to simplify reading resource bundles in Velocity templates here.

Accessing Resource Bundle in a Velocity template

Spring Configuration

<bean id="messageSource" class="">
    <property name="basename" value="content/language" />

<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">    
<property name="resourceLoaderPath" value="/WEB-INF/template/" />
    <property name="velocityProperties">
         <entry key="velocimacro.library" value="<path_to_macro.vm>" />

<bean id="templateHelper" class="">
   <property name="velocityEngine" ref="velocityEngine" />
   <property name="messageSource" ref="messageSource" />

TemplateHelper Class

public class TemplateHelper {
    private static final XLogger logger = XLoggerFactory.getXLogger(TemplateHelper.class);
    private MessageSource messageSource;
    private VelocityEngine velocityEngine;

    public String merge(String templateLocation, Map<String, Object> data, Locale locale) {
        logger.entry(templateLocation, data, locale);

        if (data == null) {
            data = new HashMap<String, Object>();

        if (!data.containsKey("messages")) {
            data.put("messages", this.messageSource);

        if (!data.containsKey("locale")) {
            data.put("locale", locale);

        String text =
                templateLocation, data);


        return text;

Velocity Template

#msg("email.hello") ${user} / $user,
#msgArgs("email.message", [${emailId}]).

I had to create a short-hand macro, msg in order to read from message bundles. It looks like this:

 * msg
 * Shorthand macro to retrieve locale sensitive message from
#macro(msg $key)

#macro(msgArgs $key, $args)

Resource Bundle

email.heading=This is a localised message
email.message=your email id : {0} got updated in our system.


Map<String, Object> data = new HashMap<String, Object>();
data.put("user", "Adarsh");
data.put("emailId", "");

String body = templateHelper.merge("send-email.vm", data, locale);