Intro to i18n
Nowadays ZPT-JS has some i18n capabilities. How do they work? Let's see an example:
i18n.js
import { zpt } from './zpt-esm.js';
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
I18nclass. Each instance includes all i18n strings in an i18n file in JSON format. The constructor ofI18nclass 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
I18nclasses. - An instance of
I18nBundleclass. An instance of this class groups an instance ofI18nclass per supported language. They are useful to support using several languages in a template.
- An instance of
- The
data-domainattribute defines the source of i18n resources to use. It can be one or several instances ofI18norI18nBundleclasses or an array of them. - The
data-languageattribute defines the current language to use. It is needed whendata-domaincontains aI18nBundleinstance, it is useless when we useI18ninstances. - The
trexpression 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 aGENDERvariable with'male'as value. RES 10defines aRESvariable with10as 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 amsgvariable 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 thespantag by the i18n message of'Hello world!'.
Some examples of valid data-domain attributes:
data-domain="i18nES1"Defines an instance ofI18nclass as the source of i18n strings.data-domain="i18nES2 i18nES1"Defines 2 instances ofI18nclass 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 ofI18nBundleclass as the source of i18n strings.data-domain="i18nBundle2 i18nBundle1"Defines 2 instances ofI18nBundleclass as the source of i18n strings.data-domain="i18nESArray"Defines an array of instances ofI18nclass 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.