Интернационализация электронной почты с использованием шаблонов Velocity / FreeMarker - PullRequest
25 голосов
/ 07 марта 2012

Как я могу достичь i18n, используя шаблонизатор, такой как Velocity или FreeMarker, для создания тела электронной почты?

Обычно люди, как правило, создают шаблоны, такие как:

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

И создайте пакет ресурсов со свойствами, такими как:

message.hi=Hi
message.welcome=Welcome to Spring!
message.link=Click here to send email.

Это создает одну основную проблему: если мои .vm файлы становятся большими с большим количеством строк текста, становится утомительно переводить и управлять каждым из них в отдельных файлах комплекта ресурсов (.properties).

Я пытаюсь создать отдельный файл .vm для каждого языка, например, mytemplate_en_gb.vm, mytemplate_fr_fr.vm, mytemplate_de_de.vm, а затем как-то сказать Velocity / Spring, чтобы он выбрал правильный, основываясь на вводимой локали.

Возможно ли это весной? Или мне следует рассмотреть, возможно, более простые и очевидные альтернативные подходы?

Примечание. Я уже видел учебник Spring о том, как создавать тела электронной почты с использованием шаблонизаторов. Но, похоже, он не отвечает на мой вопрос о i18n.

Ответы [ 2 ]

38 голосов
/ 15 марта 2012

Оказывается, использование одного шаблона и нескольких файлов language.properties выигрывает при наличии нескольких шаблонов.

Это создает одну основную проблему: если мои файлы .vm становятся большими с большим количеством строк текста, онистановится утомительным переводить и управлять каждым из них в отдельных файлах комплекта ресурсов (.properties).

Еще сложнее поддерживать, если ваша структура электронной почты дублируется в нескольких .vm файлах.Кроме того, нужно будет заново изобрести запасной механизм комплектов ресурсов.Ресурсные связки пытаются найти ближайшее совпадение с учетом локали.Например, если языковой стандарт en_GB, он пытается найти указанные ниже файлы по порядку, возвращаясь к последнему, если ни один из них не доступен.

  • language_en_GB.properties
  • language_en.properties
  • language.properties

Я опубликую (подробно), что мне нужно было сделать, чтобы упростить чтение пакетов ресурсов в шаблонах Velocity.

Доступ к пакету ресурсов в шаблоне Velocity

Конфигурация Spring

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="content/language" />
</bean>

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

<bean id="templateHelper" class="com.foo.template.TemplateHelper">
    <property name="velocityEngine" ref="velocityEngine" />
    <property name="messageSource" ref="messageSource" />
</bean>

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 =
            VelocityEngineUtils.mergeTemplateIntoString(this.velocityEngine,
                templateLocation, data);

        logger.exit(text);

        return text;
    }
}

Velocity Template

#parse("init.vm")
#msg("email.hello") ${user} / $user,
#msgArgs("email.message", [${emailId}]).
<h1>#msg("email.heading")</h1>

Мне пришлось создать сокращенный макрос, msg, чтобы читать из пакетов сообщений.Выглядит это так:

#**
 * msg
 *
 * Shorthand macro to retrieve locale sensitive message from language.properties
 *#
#macro(msg $key)
$messages.getMessage($key,null,$locale)
#end

#macro(msgArgs $key, $args)
$messages.getMessage($key,$args.toArray(),$locale)
#end

Комплект ресурсов

email.hello=Hello
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", "adarsh@email.com");

String body = templateHelper.merge("send-email.vm", data, locale);
18 голосов
/ 15 мая 2013

Вот решение (один шаблон, несколько файлов ресурсов) для Freemarker.

основная программа

// defined in the Spring configuration file
MessageSource messageSource;

Configuration config = new Configuration();
// ... additional config settings

// get the template (notice that there is no Locale involved here)
Template template = config.getTemplate(templateName);

Map<String, Object> model = new HashMap<String, Object>();
// the method called "msg" will be available inside the Freemarker template
// this is where the locale comes into play 
model.put("msg", new MessageResolverMethod(messageSource, locale));

Класс MessageResolverMethod

private class MessageResolverMethod implements TemplateMethodModel {

  private MessageSource messageSource;
  private Locale locale;

  public MessageResolverMethod(MessageSource messageSource, Locale locale) {
    this.messageSource = messageSource;
    this.locale = locale;
  }

  @Override
  public Object exec(List arguments) throws TemplateModelException {
    if (arguments.size() != 1) {
      throw new TemplateModelException("Wrong number of arguments");
    }
    String code = (String) arguments.get(0);
    if (code == null || code.isEmpty()) {
      throw new TemplateModelException("Invalid code value '" + code + "'");
    }
    return messageSource.getMessage(code, null, locale);
  }

}

Шаблон бесплатного маркера

${msg("subject.title")}
...