Internationalisation

Internationalisation (or i18n) is one of the core Caplin libraries. It allows any application developed using BladeRunnerJS to be customised to suit any locale. This includes translating the text to different languages, but also things like altering the format of numbers and dates.

Objectives

The tile blade contains a resources/i18n folder where all the property files are placed. These files contain the i18n tokens for terms and phrases specific to the blade. As with themes in BladeRunnerJS, there exists one such directory at each level of the application (app-level, bladeset-level, and blade-level) so that the i18n tokens can be placed at the right level.

In this tutorial you are going to create two translations for your tile – English and German – without any code replication. You will also learn how to localise date and number formats and create CSS which is applied only for a specific language.

i18n Tokens in HTML/XML

Look in the en.properties file to see the list of tokens available for the trade tile. Note that the i18n tokens too must be appropriately namespaced (with the blade namespace “mycompany.tutorial.tile”).

To use an i18n token in place of a static string within some HTML/XML, you simply need to use the following syntax: @{this.is.an.i18n.token}

Try this yourself

In your view (resources/html/tile.html) adjust some of the hard-coded labels to use i18n tokens instead. For example, try changing the label that currently contains the string "Instrument" so that it looks like this instead:

<label>@{mycompany.tutorial.tile.currency_pair_lbl}</label>

Load the tile in your workbench and the label at the top of the tile will say "Currency Pair" instead of "Instrument". The text is now being retrieved from the i18n properties file.

If the i18n token referenced in your HTML cannot be found, the raw token will be displayed surrounded by three question mark characters. Try temporarily deleting the mycompany.tutorial.tile.currency_pair_lbl entry from your i18n properties file and refresh the page. You will see the content change from "Currency Pair" to this value:

??? mycompany.tutorial.tile.currency_pair_lbl ???

Now fix the application by putting the token back in your properties file.

How the Locale is Chosen

Notice that tile/resources/i18n also contains the German locale with a properties file: de.properties. This file has the same i18n tokens, but these map to German words and phrases, of course.

Then why is the tile displayed using the English phrases by default?

First of all the locale needs to be supported by the Caplin Trader application.

Open app.conf in the root of your application directory (e.g CaplinTrader/apps/TutorialApp/app.conf) and add "de" to the locales to add support for the German locale (English remains the default as it is the first in the list):

locales: en, de

Now that we have listed German as a supported locale for our application, the application will automatically be served in German to our German users, based on their browser settings.

When a request is made to /myapp/ a 'locale forwarding' page is returned to the browser. Using the browsers'Accept-Language header; the value of the locale cookie and the locales the app supports this page forwards the browser to a localized app page, for example /myapp/en/. This localized app page then contains the neccessary CSS, JS, XML and HTML requests for the app.​


Further details are available in the BR documentation.

You will see your tile become German if you change your browser locale to German. Find the language settings in Chrome under "Settings > Show advanced settings > Languages > Language and input settings...", add German and drag it to the top:

Remove the /en from your URL and reload the application. You will see that the "Currency Pair" text has changed to German. The other labels are still English because we haven't updated our HTML to use i18n tokens instead of static strings.

Try this yourself

Fix up the static strings in your HTML for buy rate, sell rate, amount and dealt currency so that the tile is fully internationalised.

Then try adding a new i18n token for the Reset button’s label. You should replace the static string in the HTML view and properties to your i18n files to make sure the button works in both English and German. The German word for reset is "Rücksetzen"

Oops, the German translation (shown below) does not fit inside the small Reset button! But we have a way to address this too.

Localised CSS

The CSS used in your application can also be locale specific. For instance, in the case of our tile, we will need to make the reset button wider, and the message and tile height longer. German text seems to take up more space!

Try this yourself

Create a file called noir_de.css in the tile/themes/noir directory. Here you can override the relevant css in style.css Locale-specific theme css overrides general theme css.

We'll make the reset button wider when the German locale is in use, and just for fun let's turn the German version of the tile green as well.

.tile .reset-button button {
    width:80px;
}

.tile {
    background: -moz-linear-gradient(top, #008001 0%, #004001 100%);
    background: -webkit-linear-gradient(top, #008001 0%, #004001 100%);
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#008001', endColorstr='#004001')"; /* IE8+ */
    background: linear-gradient(top, #008001 0%, #004001 100%);
}

So when you have created specific styling for German, your English tile will still retain the original styling but the German version of the tile will be green with a wider reset button:

How the Locale Chosen Part 2

The i18n Bundler also looks at the BRJS.LOCALE cookie to determine which locale should be chosen. This takes precedence over the default locale and request headers. Therefore if your browser language is set to German but you set the BRJS.LOCALE cookie to "en" your tile will be English.

This is useful if you want to work on a particular localised version of your blade. For example, you can set this cookie if you are fine-tuning the CSS for a particular localised version of the blade, and you don't want to mess with your browser settings. Otherwise every other site you visit in different tabs while you work on the blade will also be localised to the language you're working on.

Try this yourself

While your browser default language is still German, set the BRJS.LOCALE cookie to English. In your workbench (CaplinTrader/apps/TutorialApp/tutorial-bladeset/blades/tile/workbench/index.html). There’s a setLocaleCookie() method defined there already, but the invocation of the method is currently commented out. Comment it in and reload the page, this will set a cookie. Check this in the Resources tab of your Chrome developer tools: expand Cookies, select localhost and check the BRJS.LOCALE cookie.

Reload the workbench without the "/de" part and even though your browser is set to German the tile will display as english. 

If you want to delete this cookie later on and fall back to your browser settings, comment out the line again. Then open the Resources tab of your Chrome developer tools and delete the BRJS.LOCALE cookie. Refresh the page without the "/en" suffix and the tile should switch back to German, or whatever language you have selected in your browser settings.

i18n Tokens in JavaScript

So far we have seen how to reference i18n tokens in your HTML. You can also reference these tokens from your JavaScript code, so that you can localise strings. You can access the i18n tokens inside the JavaScript presentation model by making a call to the translator, which is a core library provided by BladeRunnerJS:

var i18n = require('br/I18n');

i18n("this.is.an.i18n.token")

Note that i18n is actually an alias for br.i18n.Translator.getTranslator().getMessage(), which translates the i18n token to the corresponding phrase.

Try this Yourself

Start by making a basic translation in your confirmation message. In the constructor of your view model (TilePresentationModel.js) you will notice that the message property is initialised to the string "no trade". This is what you see on the tile when you first load it up.

this.message = new WritableProperty('no trade');

Define a new i18n token in your en.properties (and de.properties, if you like):

mycompany.tutorial.tile.no_trade_lbl=Click buy or sell to trade

Then go back to your view model and require i18n

var i18n = require('br/I18n');

Then change the message property so that it uses the token for its initial message:

this.message = new WritableProperty(i18n("mycompany.tutorial.tile.no_trade_lbl"));

Parameterised Translations

In the example above you made a basic translation from your JavaScript code. The i18n library also allows you to make "parameterised translations". This involves defining an i18n property containing tokens, and then passing in the values for those tokens when you call the translator.

A good example of this is the i18n token for the confirmation message that appears when you execute a trade. Have a look at this line in the tile's en.properties file:

mycompany.tutorial.tile.confirmation=[currencypair] trade complete: You [boughtsold] [amount][dealt] at rate [rate]

Observe that this message contains a number of parameters in square brackets. When you call the translator from your JavaScript code, you are responsible for passing in the values that should be inserted in place of these parameters. How is this done?

You must create a JavaScript object (which is essentially just map a properties) and fill it with the values for the tokens. Then you pass this map into the translator in addition to the ID of the i18n property you want to use.

Try this Yourself

In your TilePresentationModel.js file, you have a buyClicked method that is triggered when the user clicks the buy button. Recall that we bound the click event on the buy button to this method in the Presenter tutorial. The method currently prints out a hard-coded string.

TilePresentationModel.prototype.buyClicked = function() {    
    var currencyPair = this.currencyPair.getValue();
    var amount = this.amount.value.getValue();
    var dealtCurrency = this.dealtCurrency.getValue();
    var rate = this.buyRate.value.getValue();
    this.message.setValue(currencyPair + " trade complete: You bought " + amount + dealtCurrency + " at rate " + rate);
};

Change this method so that you create a JavaScript object containing the values for the parameters. Then pass this parameter object into the translator as a second argument.

TilePresentationModel.prototype.buyClicked = function(){
    var messageParams = {
        currencypair: this.currencyPair.getValue(),
        boughtsold: i18n('mycompany.tutorial.tile.confirmation.bought'),
        amount: this.amount.value.getValue(),
        dealt: this.dealtCurrency.getValue(),
        rate: this.buyRate.value.getValue()
    };
    
    var messageString = i18n('mycompany.tutorial.tile.confirmation', messageParams);
    
    this.message.setValue(messageString);
};

Refresh the tile and click the buy button. You will see that the confirmation message is now generated from the parameterised i18n token. 

Experiment with what happens if you pass a parameter object to the translator which does not contain an entry for one of the parameters in the token. You will see that the translator simply prints out the raw parameter, e.g "[currencypair]" instead of a real value. You generally don't want this to ever happen.

Local Date Format

BladeRunnerJS includes a default date format for English and German locales. You can also specify the date format associated with other locales.

To demonstrate localisation of dates, let's alter the "sellClicked" method so that it includes a localised date at the start of the message. Notice the use of the Translator’s "formatDate" method. This will format the date according to the current locale:

TilePresentationModel.prototype.sellClicked = function(){
	var localisedDate = i18n.getTranslator().formatDate(new Date());

	var messageParams = {
		currencypair: this.currencyPair.getValue(),
		boughtsold: i18n('mycompany.tutorial.tile.confirmation.sold'),
		amount: this.amount.value.getValue(),
		dealt: this.dealtCurrency.getValue(),
		rate: this.sellRate.value.getValue()
	};

	var messageString = i18n('mycompany.tutorial.tile.confirmation', messageParams);
	this.message.setValue(localisedDate + ' ' + messageString);
};

Now when you click the sell button you will see a date appear, which will have a different format depending on whether you are using the English or German version of the tile.

To override the default English date format, add the following (or any other format you prefer) to CaplinTrader/apps/TutorialApp/default-aspect/resources/i18n/en/en.properties. Note that this is a different en.properties file to the one you have been editing so far, which was within the tile blade itself. Date formats should almost always be the same throughout the application rather than differing from blade to blade, so the date formats are defined up at the aspect level.

br.i18n.date.format=DD, MMMM YY

Local Number Format

Number formatting too is subject to locale. In our buyClicked and sellClicked methods we are currently printing the raw amount that the user traded. It's nicer to format this with the appropriate thousands separator character and decimal point character.

Note that formatting of numbers falls under the remit of internationalisation because the characters differ by locale. In English we use the comma as a thousands separator and the full stop as a decimal point, but in French the space is used as a thousands separator and comma is used for the decimal point. German uses the full stop as a thousands separator.

To format a number to the user's locale, we can use the "formatNumber" method on the Translator object. Let's update our sellClicked method to localise the amount before printing it.

TilePresentationModel.prototype.sellClicked = function(){
	var localisedDate = i18n.getTranslator().formatDate(new Date());

	var messageParams = {
		currencypair: this.currencyPair.getValue(),
		boughtsold: i18n('mycompany.tutorial.tile.confirmation.sold'),
		amount: i18n.getTranslator().formatNumber(this.amount.value.getValue()),
		dealt: this.dealtCurrency.getValue(),
		rate: this.sellRate.value.getValue()
	};

	var messageString = i18n('mycompany.tutorial.tile.confirmation', messageParams);
	this.message.setValue(localisedDate + ' ' + messageString);
};

Now the tile amount format (in the confirmation message) differs according to the locale used:

Review

Having completed this tutorial you should now be able to:

  • Create language properties files for each locale containing the i18n tokens pertaining to any specific part of your application;
  • Use these i18n tokens in HTML and JavaScript to internationalise your components;
  • Customise date and number formats to a user’s locale;
  • Apply specific styling for a locale where necessary.

There is more information about internationalisation and how to internationalisae your app on the BladeRunnerJS site.