Intro to i18n
Nowadays ZPT-JS has some i18n capabilities. How do they work? Let's see an example:
i18n.js"use strict"; var zpt = require( 'zpt' ); var I18n = zpt.I18n; var I18nBundle = zpt.I18nBundle; /* I18n maps init */ var msg = { en : {}, es : {} }; /* English i18n messages */ msg.en[ '/CONF/' ] = { language: 'en', locale: 'en-US' }; msg.en[ 'Hello world!' ] = 'Hello world!'; msg.en[ 'Results msg' ] = '{GENDER, select, male{He} female{She} other{They} }' + ' found ' + '{RES, plural, =0{no results} one{1 result} other{# results} }'; /* Spanish i18n messages */ msg.es[ '/CONF/' ] = { language: 'es', locale: 'es-ES' }; msg.es[ 'Hello world!' ] = '¡Hola mundo!'; msg.es[ 'Results msg' ] = '{ GENDER, select, male{Él} female{Ella} other{Ellos} }' + ' ' + '{ RES, plural, =0{no } other{} }' + '{ GENDER, select, male{ha} female{ha} other{han} }' + ' encontrado ' + '{ RES, plural, =0{ningún resultado} one{un único resultado} other{# resultados} }'; // Create I18n and i18nBundle instances var i18nES = new I18n( 'es', msg[ 'es' ] ); var i18nEN = new I18n( 'en', msg[ 'en' ] ); var i18nBundle = new I18nBundle( i18nES, i18nEN ); // Init dictionary var dictionary = { 'i18nBundle': i18nBundle }; // Parse template zpt.run({ root: document.body, dictionary: dictionary });i18n.html
<!DOCTYPE html> <html lang="es"> <head> <meta charset="utf-8"> <title>Some I18n examples</title> <script src="i18n.js"></script> </head> <body> <h1>Some I18n expressions</h1> <ol data-language="'en'" data-domain="i18nBundle"> <li> ¡Hola mundo! = <span data-content="tr: 'Hello world!'">Must be ¡Hola mundo!</span> </li> <li> Él ha encontrado 10 resultados = <span data-content="tr: 'Results msg' ( GENDER 'male'; RES 10 )">Must be 'Él ha encontrado 10 resultados'</span> </li> </ol> </body> </html>
Some remarks about this:
- The dictionary must contain a source of i18n resources. ZPT-JS supports these types:
- An instance of
I18n
class. Each instance includes all i18n strings in an i18n file in JSON format. The constructor ofI18n
class accepts 2 arguments; the first is the language of the messages and the second is a map (keys are the id of the messages and values the message themselves). The format of the messages must complain ICU standards. - An array of instances of
I18n
classes. - An instance of
I18nBundle
class. An instance of this class groups an instance ofI18n
class per supported language. They are useful to support using several languages in a template.
- An instance of
- The
data-domain
attribute defines the source of i18n resources to use. It can be one or several instances ofI18n
orI18nBundle
classes or an array of them. - The
data-language
attribute defines the current language to use. It is needed whendata-domain
contains aI18nBundle
instance, it is useless when we useI18n
instances. - The
tr
expression evaluates an expression but then it searches that value into the messages. - The expression
'Hello world!'
is a literal, but'Results msg' ( GENDER 'male'; RES 10 )
is a little more complex:- The first item is the message id:
'Results msg'
. - The rest are variables used to build the message:
GENDER 'male'
defines aGENDER
variable with'male'
as value. RES 10
defines aRES
variable with10
as value.
- The first item is the message id:
Some examples
Some examples of i18n tags in action:
-
<img src="image.png" data-attributes="title tr: 'Hello world!'">
adds an i18n title to the image. -
<li data-define="msg tr: 'Hello world!'">
defines amsg
variable with the i18n message of'Hello world!'
. -
<body data-on-error="tr: 'Oh, noooo!'">
sets the i18n message of'Oh, noooo!'
as the message to show if an error occurs. <span data-replace="tr: 'Hello world!'">
replaces thespan
tag by the i18n message of'Hello world!'
.
Some examples of valid data-domain
attributes:
data-domain="i18nES1"
Defines an instance ofI18n
class as the source of i18n strings.data-domain="i18nES2 i18nES1"
Defines 2 instances ofI18n
class as the source of i18n strings. ZPT will use the i18nES2 instance first; if the string is not found will be use i18nES1 instance.data-domain="i18nBundle1"
Defines an instance ofI18nBundle
class as the source of i18n strings.data-domain="i18nBundle2 i18nBundle1"
Defines 2 instances ofI18nBundle
class as the source of i18n strings.data-domain="i18nESArray"
Defines an array of instances ofI18n
class as the source of i18n strings. ZPT will use the first instance in the array; if the string is not found will be use the next instance until it is found.
Working with domains
In the previous example the domain was a simple I18nBundle
instance. This forces to use big maps with all the messages of one language. This can be awful if the amount of messages is big. data-domain
tag supports also a list of I18nBundle
instances, so the messages will be searched in the same order.
Therefore, data-domain="i18nBundle1 i18nBundle2"
allows to organise your i18n messages in 2 maps. The first one can contain general messages and the second one more particular messages (for example).
data-domain
also supports nested definitions:
<div data-domain="i18nBundle1"> <span data-content="tr: 'Hello world!'"> ¡Hola mundo! </span> <span data-domain="i18nBundle2" data-content="tr: 'Hello world!'"> ¡¡¡Hola mundo 2!!! </span> </div>
The first data-content
will search only in i18nBundle1
. The second one will search first in i18nBundle2
and if it is not found will search in i18nBundle1
.
Loading messages from JSON files
ZPT-JS makes it easy loading i18n messages from JSON files:
zpt.run({ command: 'preload', root: document.body, dictionary: dictionary, i18n: { urlPrefix: './i18n/', files: { 'es': [ 'es1.json', 'es2.json' ], 'en': [ 'en1.json', 'en2.json' ] } }, callback: function(){ // Add I18nBundle instances to dictionary dictionary.i18nBundle1 = new I18nBundle( dictionary.i18nES1, dictionary.i18nEN1 ); dictionary.i18nBundle2 = new I18nBundle( dictionary.i18nES2, dictionary.i18nEN2 ); // Run ZPT zpt.run(); } });
ZPT will add to dictionary these vars:
- i18nES1. Includes all texts from es1.json file.
- i18nES2. Includes all texts from es2.json file.
- i18nEN1. Includes all texts from en1.json file.
- i18nEN2. Includes all texts from en2.json file.
- i18nESArray. An array with all spanish texts, in this example [ i18nES2, i18nES1 ].
- i18nENArray. An array with all english texts, in this example [ i18nEN2, i18nEN1 ].
- i18nArray. An array with all texts defined only if only one language is present. In this example it would not be defined, there are 2 languages.
The order in arrays is inverted: first the last files, then the first.
The bundles (i18nBundle1 and i18nBundle2) are not required, add to the dictionary if you need them.
Numbers
ZPT-JS uses Intl as i18n API for numbers. Let's see some examples:
-
<span data-content="trNumber: 1355.23">
formats that number depending on the active i18n domain (1,355.23 in english, 1.355,23 in spanish...). -
<span data-content="trNumber: 1355.23643 ( maximumFractionDigits 3 )">
formats that number depending on the active i18n domain and using a maximum of 3 fraction digits (1,355.236 in english, 1.355,236 in spanish...). -
<span data-content="trNumber: 1355.23643 ( maximumFractionDigits 3; minimumIntegerDigits 6 )">
formats that number depending on the active i18n domain and using a maximum of 3 fraction digits and a minimum of 6 integer digits (001,355.236 in english, 001.355,236 in spanish...).
Take a look on NumberFormat options to see all available options.
Currencies
ZPT-JS uses Intl as i18n API for currencies. Let's see some examples:
-
<span data-content="trCurrency: 'EUR' 1355.23">
formats that number depending on the active i18n domain and using that currency (€ 1,355.23 in english, 1.355,23 € in spanish...). -
<span data-content="trCurrency: 'USD' 1355.23 ( currencyDisplay 'name' )">
uses the name of the currency (1,355.23 US dollars in english, 1.355,23 € dólares estadounidenses in spanish...).
Options are the same as numbers (NumberFormat options) plus some specific options of currencies.
Dates and times
ZPT-JS uses Intl as i18n API for date and times. Let's see some examples:
-
<span data-content="trDate: ( js: new Date( Date.UTC( 2012, 11, 21, 3, 0, 0 ) ) )">
formats that date depending on the active i18n domain (12/21/2012 in english, 21/12/2012 in spanish...). -
<span data-content="trDate: ( js: new Date( Date.UTC( 2012, 11, 21, 3, 0, 0 ) ) ) ( weekday 'long'; year 'numeric'; month 'long'; day 'numeric' )">
formats that date with some options (Friday, December 21, 2012 in english, viernes, 21 de diciembre de 2012 in spanish...). -
<span data-content="trDate: ( js: new Date( Date.UTC( 2012, 11, 21, 3, 0, 0 ) ) ) ( hour 'numeric'; minute 'numeric'; second 'numeric' )">
formats that date with some options (4:00:00 AM in english, 4:00:00 in spanish...).
Take a look on DateTimeFormat options to see all available options.