Localized templates with Handlebars / Mustache
Currently I'm using AngularJS + JADE combo in almost all of my projects but before that time I had a lot of fun with Backbone / Backbone Marionette.
Some time ago one of our customers decided to localize whole web app which already contained a quite large front end codebase, client-side templating and Backbone / Marionette, Handlebars, require.js under the hood.
At the same time, we were launching new version of the app and didn't want to refactor almost everything, in other words - we wanted to minimize the impact of another new feature, yet still - we wanted to keep things clean, simple and easy to maintain.
That's the solution I came up with. Hope you'll find it helpful.
Tools
I decided to use i18n plugin for require.js
Translator class
I've created a CoffeeScript class which returns a compile() method for currently selected language version:
# file: modules/localized-template.coffee
define (require)->
_ = require 'lodash'
Handlebars = require 'handlebars'
LocalizedTemplate = (dict, tpl) ->
(model)->
dictModel =
i18n : dict
params = _.extend {}, model, dictModel
rendered = Handlebars.compile tpl
rendered params
Thanks to _.extend
method we are able to preserve all strings / view model data created in the view controller and override only necessary strings.
View
# file: modules/view-name/view-controller-name.coffee
define (require)->
require 'jquery'
require 'backbone'
require 'Backbone.Marionette'
Handlebars = require 'handlebars'
VideoView = require './video'
StreamTemplate = require 'text!./tpl/stream.tpl'
# most important part:
LocalizedTemplate = require 'cs!modules/localized-template'
i18n =
ui : require 'i18n!nls/ui'
StreamView = VideoView.extend
# here we swap Handlebars.compile method:
template : LocalizedTemplate i18n, StreamTemplate
tagName : 'li'
StreamView
Template
That's quite simple: we place localized strings under i18n.ui.variableName
.
# file: modules/view-name/tpl/stream.tpl
<div class="icon">
<img width="240" height="135" src="{{#if posterUrl}}{{{posterUrl}}}{{else}}/img/video-list/not-found.png{{/if}}" alt="{{title}}">
<div class="labels">
<span class="language">{{language.label}}</span>
<div class="specializations">
{{#each specializations}}
<span class="specialization">{{name}}</span>
{{/each}}
</div>
</div>
</div>
<div class="description">
<div class="title">{{title}}</div>
<div class="author">{{author}}</div>
<div class="affiliation">{{affiliation}}</div>
<div class="labels">
<span class="date">{{parsedStartTime}}</span>
<button class="subscribe">{{i18n.ui.subscribe}}</button>
</div>
</div>
It's important not to pollute "global" template scope. What if there was a localized variable called specializations = "Specialisierung"
? In this case it would be overwritten by array pulled from JSON. That's just evil.
Localized strings
I've implemented it according to official documentation.