#459 separe i18n code from Recline
This commit is contained in:
parent
e9f6554eac
commit
55c51cfad6
@ -1,36 +1,54 @@
|
||||
/*jshint multistr:true */
|
||||
|
||||
// TODO probably some kind of mixin would be better, like: my.View - Backbone.View.extend({}); _.extend(my.View, Backbone.I18nView);
|
||||
"use strict";
|
||||
var I18nMessages = function(uniqueID, translations, languageResolverOrLocale, appHardcodedLocale) {
|
||||
var defaultResolver = function() {
|
||||
};
|
||||
|
||||
Backbone.I18nView = Backbone.View.extend({
|
||||
defaultLocale: 'en',
|
||||
locale: 'en',
|
||||
cache: {},
|
||||
initializeI18n: function(locale, appHardcodedLocale) {
|
||||
this.defaultLocale = appHardcodedLocale || 'en';
|
||||
this.locale = locale || this.defaultLocale;
|
||||
// which locale should we use?
|
||||
languageResolverOrLocale = (typeof languageResolverOrLocale !== 'undefined') ? languageResolverOrLocale : defaultResolver;
|
||||
appHardcodedLocale = appHardcodedLocale || 'en';
|
||||
|
||||
this.cache[this.locale] = {};
|
||||
},
|
||||
if (typeof(languageResolverOrLocale) === 'function') {
|
||||
languageResolverOrLocale = languageResolverOrLocale();
|
||||
}
|
||||
if (languageResolverOrLocale == undefined) {
|
||||
languageResolverOrLocale = appHardcodedLocale;
|
||||
}
|
||||
|
||||
if (I18nMessages.prototype._formatters[uniqueID, languageResolverOrLocale]) {
|
||||
return I18nMessages.prototype._formatters[uniqueID, languageResolverOrLocale];
|
||||
}
|
||||
I18nMessages.prototype._formatters[uniqueID, languageResolverOrLocale] = this;
|
||||
|
||||
// ========== VARIABLES & FUNCTIONS ==========
|
||||
var self = this;
|
||||
|
||||
this.locale = languageResolverOrLocale;
|
||||
this.cache= {};
|
||||
|
||||
this.getLocale = function() {
|
||||
return this.locale;
|
||||
};
|
||||
|
||||
// ============= FormatJS.io backend =========
|
||||
|
||||
this.t = function(key, values, defaultMessage) {
|
||||
values = (typeof values !== 'undefined') ? values : {};
|
||||
|
||||
// TODO how to use it from outside? an singleton instance of I18n? use case: pass translated strings into view initializer
|
||||
t: function(key) {
|
||||
this.t(key, {}, null);
|
||||
},
|
||||
t: function(key, values, defaultMessage) {
|
||||
// get the message from current locale
|
||||
var msg = recline.View.translations[this.locale][key];
|
||||
var msg = this.translations[this.locale][key];
|
||||
|
||||
// fallback to key or default message if no translation is defined
|
||||
if (msg == null) {
|
||||
if (this.locale != this.defaultLocale) {
|
||||
if (this.locale != this.appHardcodedLocale) {
|
||||
console.warn("Missing locale for " + this.locale + "." + key);
|
||||
}
|
||||
msg = defaultMessage;
|
||||
}
|
||||
if (msg == null) {
|
||||
msg = key;
|
||||
if (this.locale === this.defaultLocale) {
|
||||
if (this.locale === this.appHardcodedLocale) {
|
||||
// no need to define lang entry for short sentences, just use underscores as spaces
|
||||
msg = msg.replace(/_/g, ' ');
|
||||
}
|
||||
@ -38,9 +56,9 @@ Backbone.I18nView = Backbone.View.extend({
|
||||
}
|
||||
|
||||
try {
|
||||
var formatter = this.cache[this.locale][msg];
|
||||
var formatter = this.cache[msg];
|
||||
if (formatter === undefined) {
|
||||
this.cache[this.locale][msg] = formatter = new IntlMessageFormat(msg, this.locale);
|
||||
this.cache[msg] = formatter = new IntlMessageFormat(msg, this.locale);
|
||||
}
|
||||
var formatted = formatter.format(values);
|
||||
|
||||
@ -56,16 +74,18 @@ Backbone.I18nView = Backbone.View.extend({
|
||||
}
|
||||
},
|
||||
|
||||
MustacheFormatter: function() {
|
||||
var formatter = new Proxy(this, {
|
||||
get: function(view, name) {
|
||||
// ============ Mustache integration ========
|
||||
|
||||
this.mustacheI18Tags = function() {
|
||||
var tagsProxy = new Proxy(this, {
|
||||
get: function(messages, name) {
|
||||
return function() {
|
||||
var f = function (text, render) {
|
||||
var trans = view.t(name, this, text);
|
||||
var trans = messages.t(name, this, text);
|
||||
return render(trans);
|
||||
}
|
||||
f.toString = function() {
|
||||
return view.t(name);
|
||||
return messages.t(name);
|
||||
}
|
||||
return f;
|
||||
};
|
||||
@ -76,7 +96,11 @@ Backbone.I18nView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
return {
|
||||
't': formatter,
|
||||
't': tagsProxy,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
this.injectMustache = function(tmplData) {
|
||||
return _.extend(tmplData, self.mustacheI18Tags());
|
||||
}
|
||||
};
|
||||
@ -22,6 +22,14 @@ var Fixture = {
|
||||
];
|
||||
var dataset = new recline.Model.Dataset({records: documents, fields: fields});
|
||||
return dataset;
|
||||
},
|
||||
|
||||
getTranslations: function() {
|
||||
return {
|
||||
pl: {
|
||||
Grid: 'Tabela'
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,101 +1,80 @@
|
||||
(function ($) {
|
||||
|
||||
module("View - i18n support");
|
||||
module("I18nMessages");
|
||||
|
||||
test('translate simple key custom locale', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset,
|
||||
locale: 'pl' // todo or should it go in the state parameter?
|
||||
});
|
||||
var fmt = I18nMessages('somelib', Fixture.getTranslations(), 'pl');
|
||||
|
||||
equal(view.t('Grid'), 'Tabela');
|
||||
equal(fmt.t('Grid'), 'Tabela');
|
||||
});
|
||||
|
||||
test('translate simple key default locale', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset
|
||||
});
|
||||
var fmt = I18nMessages('somelib', Fixture.getTranslations());
|
||||
|
||||
equal(view.t('Add_row'), 'Add row');
|
||||
equal(fmt.t('Add_row'), 'Add row');
|
||||
});
|
||||
|
||||
test('override custom locale', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset,
|
||||
locale: 'pl'
|
||||
});
|
||||
var fmt = I18nMessages('recline', recline.View.translations, 'pl');
|
||||
var oldTranslation = recline.View.translations['pl']['Grid'];
|
||||
|
||||
// set custom strings in external app after recline script
|
||||
// set custom strings in external app after including recline script
|
||||
recline.View.translations['pl']['Grid'] = 'Dane';
|
||||
|
||||
equal(view.t('Grid'), 'Dane');
|
||||
equal(fmt.t('Grid'), 'Dane');
|
||||
|
||||
recline.View.translations['pl']['Grid'] = oldTranslation;
|
||||
});
|
||||
|
||||
test('override default locale', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset
|
||||
});
|
||||
var fmt = I18nMessages('recline', recline.View.translations, 'en');
|
||||
var oldTranslation = recline.View.translations['en']['Grid'];
|
||||
|
||||
// set custom strings in external app after recline script
|
||||
// set custom strings in external app after including recline script
|
||||
recline.View.translations['en']['Grid'] = 'Data';
|
||||
|
||||
equal(view.t('Grid'), 'Data');
|
||||
equal(fmt.t('Grid'), 'Data');
|
||||
|
||||
recline.View.translations['en']['Grid'] = oldTranslation;
|
||||
});
|
||||
|
||||
test('fallback to key if translation not present', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset
|
||||
});
|
||||
var fmt = I18nMessages('somelib', Fixture.getTranslations());
|
||||
|
||||
equal(view.t('thiskeydoesnotexist'), 'thiskeydoesnotexist');
|
||||
equal(fmt.t('thiskeydoesnotexist'), 'thiskeydoesnotexist');
|
||||
});
|
||||
|
||||
test('fallback to default message', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset
|
||||
});
|
||||
var fmt = I18nMessages('somelib', Fixture.getTranslations());
|
||||
|
||||
equal(view.t('thiskeydoesnotexist', {}, 'Fallback to default message'), 'Fallback to default message');
|
||||
equal(fmt.t('thiskeydoesnotexist', {}, 'Fallback to default message'), 'Fallback to default message');
|
||||
});
|
||||
|
||||
test('mustache formatter - simple key', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset,
|
||||
locale: 'pl'
|
||||
});
|
||||
var fmt = I18nMessages('somelib', Fixture.getTranslations(), 'pl');
|
||||
|
||||
// test without template rendering
|
||||
var mustacheIntegration = fmt.injectMustache({});
|
||||
equal(mustacheIntegration.t.Grid, 'Tabela')
|
||||
|
||||
// test within template rendering
|
||||
var template = '{{t.Grid}}';
|
||||
var tmplData = {};
|
||||
|
||||
// adding i18n support [do it in view before passing data to render functions]
|
||||
tmplData = _.extend(tmplData, view.MustacheFormatter());
|
||||
tmplData = fmt.injectMustache(tmplData);
|
||||
|
||||
var out = Mustache.render(template, tmplData);
|
||||
equal(out, 'Tabela');
|
||||
});
|
||||
|
||||
test('mustache formatter - complex key', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset,
|
||||
});
|
||||
var fmt = I18nMessages('somelib', Fixture.getTranslations(), 'pl');
|
||||
|
||||
var template = '{{#t.num_records}}{recordCount} records{{/t.num_records}}';
|
||||
var tmplData = {recordCount: 5};
|
||||
|
||||
// adding i18n support [do it in view before passing data to render functions]
|
||||
tmplData = _.extend(tmplData, view.MustacheFormatter());
|
||||
// injecting i18n support [do it in view before passing data to render functions]
|
||||
tmplData = fmt.injectMustache(tmplData);
|
||||
|
||||
var out = Mustache.render(template, tmplData);
|
||||
equal(out, '5 records');
|
||||
@ -103,46 +82,80 @@ test('mustache formatter - complex key', function () {
|
||||
|
||||
|
||||
test('translate complex key default locale', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset
|
||||
});
|
||||
var fmt = I18nMessages('somelib', Fixture.getTranslations(), 'pl');
|
||||
|
||||
equal(view.t('codeforall', {records: 3}, '<span>{records} records</span>'), '<span>3 records</span>');
|
||||
});
|
||||
|
||||
test('translate complex key custom locale', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset,
|
||||
locale: 'pl'
|
||||
});
|
||||
test('mustache formatter - translate complex key custom locale', function () {
|
||||
var translations = {
|
||||
pl: {
|
||||
codeforall: '<span>{records} rekordy</span>'
|
||||
}
|
||||
};
|
||||
var fmt = I18nMessages('somelib', translations, 'pl');
|
||||
|
||||
recline.View.translations['pl']['codeforall'] = '<span>{records} rekordy</span>';
|
||||
equal(view.t('codeforall', {records: 3}, '<span>{records} records</span>'), '<span>3 rekordy</span>');
|
||||
equal(fmt.t('codeforall', {records: 3}, '<span>{records} records</span>'), '<span>3 rekordy</span>');
|
||||
});
|
||||
|
||||
test('translate complex key custom locale custom count', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
var view = new recline.View.MultiView({
|
||||
model: dataset,
|
||||
locale: 'pl'
|
||||
});
|
||||
|
||||
recline.View.translations['pl']['codeforall'] = '{records, plural, ' +
|
||||
test('mustache formatter - translate complex key custom locale custom count', function () {
|
||||
var translations = {
|
||||
pl: {
|
||||
codeforall: {records, plural, ' +
|
||||
'=0 {brak zdjęć}' +
|
||||
'=1 {{records} zdjęcie}' +
|
||||
'few {{records} zdjęcia}' +
|
||||
'other {{records} zdjęć}}';
|
||||
'other {{records} zdjęć}}'
|
||||
}
|
||||
};
|
||||
var fmt = I18nMessages('somelib', translations, 'pl');
|
||||
|
||||
equal(view.t('codeforall', {records: 0}), 'brak zdjęć');
|
||||
equal(view.t('codeforall', {records: 1}), '1 zdjęcie');
|
||||
equal(view.t('codeforall', {records: 3}), '3 zdjęcia');
|
||||
equal(view.t('codeforall', {records: 5}), '5 zdjęć');
|
||||
equal(fmt.t('codeforall', {records: 0}), 'brak zdjęć');
|
||||
equal(fmt.t('codeforall', {records: 1}), '1 zdjęcie');
|
||||
equal(fmt.t('codeforall', {records: 3}), '3 zdjęcia');
|
||||
equal(fmt.t('codeforall', {records: 5}), '5 zdjęć');
|
||||
});
|
||||
|
||||
test('I18nMessages specified locale', function () {
|
||||
var fmt = I18nMessages('somelib', {}, 'pl');
|
||||
|
||||
// todo test dynamic language changes
|
||||
equal(fmt.getLocale(), 'pl');
|
||||
});
|
||||
|
||||
test('I18nMessages default locale', function () {
|
||||
var fmt = I18nMessages('somelib', {});
|
||||
|
||||
// no language set in HTML tag
|
||||
equal($('html').attr('lang'), undefined);
|
||||
|
||||
equal(fmt.getLocale(), 'en');
|
||||
});
|
||||
|
||||
test('I18nMessages default html:lang default locale resolver', function () {
|
||||
var fmt = I18nMessages('somelib', {});
|
||||
|
||||
|
||||
$('html').attr('lang', 'de');
|
||||
equal(fmt.getLocale(), 'de');
|
||||
$('html').attr('lang', null);
|
||||
});
|
||||
|
||||
test('I18nMessages default locale custom resolver', function () {
|
||||
var localeResolver = function() { return 'fr'; };
|
||||
var fmt = I18nMessages('somelib', {}, localeResolver);
|
||||
|
||||
equal(fmt.getLocale(), 'fr');
|
||||
});
|
||||
|
||||
test('I18nMessages singletons, function () {
|
||||
var lib1_pl = I18nMessages('lib1', {}, 'pl');
|
||||
var lib2_pl = I18nMessages('lib2', {}, 'pl');
|
||||
var lib1_en = I18nMessages('lib1', {}, 'en');
|
||||
|
||||
strictEqual(I18nMessages('lib1', {}, 'pl'), lib1_pl);
|
||||
notStrictEqual(lib1_pl, lib1_en);
|
||||
notStrictEqual(lib1_pl, lib2_pl);
|
||||
});
|
||||
|
||||
})(this.jQuery);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user