Tuesday, March 8, 2016

Custom path of the messages

The guide says "You can externalize messages in the conf/messages.xxx files.". If you have a dozen languages it will be a mess of messages files in the config folder. Fortunately, there is property in the application.conf file that somehow fell off the tutorial - play.i18n.path. You can assign this variable the value that will prefix of messages files.
play.i18n.path = messages/
Will say the Play to look into the conf/messages folder for the messages.xxx files

Thursday, March 3, 2016

Handlebars. Helpers

Handlebars easily extensible. Let's create a helper for processing the asset link.  Here is the original Twirl template:
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
<link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
<script src="@routes.Assets.versioned("javascripts/hello.js")" type="text/javascript"></script>
We need to be able to do @routes.Assets.versioned("...") with the handlebars. Let's create the helper class that will hold all our handlebars helpers. From the start, we will add only one helper, the assets helper. This one could be done even in static method:

package handlebars;

public class Helpers {

  /**
  * Do the same as "@routes.Assets.versioned" in Twirl.
  * 
  * @param url relative path to the asset
  * @return actual path to the asset
  */
  public static CharSequence asset(String url) {
    return controllers.routes.Assets.versioned(new controllers.Assets.Asset(url)).toString();
  }
}
Now we need to register it. Jut one line into the code from my previous post:
...

// Initialize the engine
Handlebars handlebars = new Handlebars(loader);

// Add helpers
handlebars.registerHelpers(Helpers.class);

// Compile the "templates/page.hbs" template
Template template = handlebars.compile("page");

...
Now we can add assets to the handlebars template:
<link rel="stylesheet" media="screen" href="{{asset "stylesheets/main.css"}}">
<link rel="shortcut icon" type="image/png" href="{{asset "images/favicon.png"}}">
<script src="{{asset "javascripts/hello.js"}}" type="text/javascript"></script>
And the result, the same as with Twirl:
<link rel="stylesheet" media="screen" href="/assets/stylesheets/main.css">
<link rel="shortcut icon" type="image/png" href="/assets/images/favicon.png">
<script src="/assets/javascripts/hello.js" type="text/javascript&qu

Handlebars

I personally prefer the logicless templates, so mustache simply The Best for me.  Unfortunately, the real world is not so simple,  therefore, I use handlebars.

Paly itself prefer Twirl. Well, it's a good thing, but I still like handlebars much more.

Fortunately,  we can use handlebars as the template engine in Play. You can find some ready to use plugins on the GitHub, like play2-handlebars. And you can easily use the original handlebars.java library by yourself. I stick with the last option. It's easy and you can always update handlebars to the last version without depending on other plugins.

It's a good idea to use a separate folder for the templates. Assume you create the templates folder in the project root. Then you will need to add two new instructions to the build.sbt file.
// Add the handlebars library
libraryDependencies += "com.github.jknack" % "handlebars" % "4.0.3"

// Copy handlebars templates to the production
mappings in Universal ++=
  (baseDirectory.value / "templates" * "*" get) map
    (x => x -> ("templates/" + x.getName))
The last thing just to use the handlebars engine. I extend trivial controller class from the play-java framework.
package controllers;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.inject.Inject;

import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Template;
import com.github.jknack.handlebars.io.FileTemplateLoader;
import com.github.jknack.handlebars.io.TemplateLoader;
import com.google.common.collect.ImmutableMap;

import play.Environment;
import play.mvc.Controller;
import play.mvc.Result;

public class Application extends Controller {

  // We need an environment to get the template folder
  @Inject
  private Environment environment;
 
  public Result index() throws Exception {
     
    // The data
    Map data = new HashMap<>();
    data.put("title", "Page Title");
    data.put("header", "Header");
    data.put("main", ImmutableMap.of("article", "Main Article"));
    data.put("footer", "Footer");
     
    // Get the template folder
    File rootFolder = environment.getFile("/templates");

    // Put the ".hbs" as a template extension.
    TemplateLoader loader = new FileTemplateLoader(rootFolder, ".hbs");
        
    // Initialize the engine
    Handlebars handlebars = new Handlebars(loader);
        
    // Compile the "templates/page.hbs" template
    Template template = handlebars.compile("page");
     
    // Fill it with data
    String page = template.apply(data);
        
    // Return the page to the client
    return ok(page).as("text/html");
  }

}
Yea, here is the template:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{{title}}</title>
    <meta name="description" content="{{description}}">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <header>
        {{header}}
    </header>
    <main>
     <article>
        {{main.article}}
     </article>
    </main>
    <footer>
        {{footer}}
    </footer>
</body>
</html>
And the result:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Page Title</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <header>
        Header
    </header>
    <main>
     <article>
        Main Article
     </article>
    </main>
    <footer>
        Footer
    </footer>
</body>
</html>

Wednesday, March 2, 2016

Messages API. Unable to switch the language

Assume you are using java in the controller, and the default template engine.  It will work perfectly until you want to switch the language.

Let’s look at the guide of java internationalization JavaI18N
public Result index() {
  ctx().changeLang("fr");
  return ok(hellotemplate.render()); // "bonjour"
}
Well, it will only work in the case if you use java version of the messages API in the templates i.e.
@import play.i18n._
@Messages.get("hello")
If you prefer to use shorter scala version, like
@Messages("hello")
Then switched language will not be used in the current request. Scala Messages API will use the old one – default or taken from the request cookie.  I need to admit that ctx().changeLang(“fr”) will change the PLAY_LANG cookie, so in the next request (for example after refreshing the page)  scala will take this cookie and will show the “switched” language.