Sunday, March 13, 2016

Handlebars. i18n to @Message

The Handlebars internationalization helper {{i18n}} is not very good choice for the Play. It uses different name convention for the message files, it hard to use different language in different requests. The {{i18n}} uses the local attribute to detect the language, by default it uses default local that defines by the Locale.getDefault(). It's a little bit different from the Play philosophy where you can change the language in every request, even only for one request with the help of the setTransientLang().

So I created the {{message}} helper that does the same work as the @Message in Twirl. It takes just a few strings of code. Actually, there are only two difference from the {{assert}} helper, described in the previous post. First - I need to detect the language on an every request, so I can not use the static helper. Second - messages could have arguments, so I need to use Options parameter in the helper method. And a little bit sugar - the MessageFormat library, just like in the original @Message.

Changes in the Helper class

import java.text.MessageFormat;
import com.github.jknack.handlebars.Options;
import play.i18n.Messages;


private final Messages messages;


* Creates Helpers with the given message pack.
* @param messages
*          The Play message pack.
public Helpers(final Messages messages) {
  this.messages = messages;


* Do the same as "@Message(key)" in Twirl. It use MessageFormat for the
* formatting as well as "@Message(key)".
* @param key
*          message key in the messages.** files.
* @return message
public CharSequence message(final String key, Options options){
  String message =;
  String messageFormatted = MessageFormat.format(message, options.params);
  return messageFormatted;

Cahnges in the Handlebars initialization

import play.i18n.Messages;
import play.i18n.MessagesApi;


private MessagesApi messagesApi;


Messages messages = new Messages(ctx().lang(), messagesApi);
Helpers helpers = new Helpers(messages);



{{message "page.header.sub" "name"}}


page.header.sub=Page Sub Header {0}
I used the new Messages(ctx().lang(), messagesApi) opposite to the ctx().messages() because I want full support of the ctx().changeLang() and the ctx().setTransientLang().

The ctx().lang() returns the current language, it is the language that was set by the ctx().changeLang() or the ctx().setTransientLang() or was taken from the PLAY_LANG cookie or taken from the Accept-Language header or taken from the default locale of the server.

The ctx().messages() returns a messages pack for the language of the current request. It is the language that was taken from the PLAY_LANG cookie or taken from the Accept-Language header or taken from the default locale of the server.

The ctx().changeLang() change the context lang field and set the PLAY_LANG cookie for the current response. So the ctx().messages() will return the message pack of the new language only in the next request. It is a tiny difference but I spend some time to catch it.

No comments:

Post a Comment