diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index d462ea5401..fe377f987e 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -44,14 +44,26 @@ * @param {string} language - Language code for this translator instance */ function Translator(language) { - classCallCheck(this, Translator); + var self = this; + classCallCheck(self, Translator); if (!language) { throw new TypeError('Parameter `language` must be a language string. Received ' + language + (language === '' ? '(empty string)' : '')); } - this.lang = language; - this.translations = {}; + self.modules = Object.keys(Translator.moduleFactories).map(function (namespace) { + var factory = Translator.moduleFactories[namespace]; + return [namespace, factory(language)]; + }).reduce(function (prev, elem) { + var namespace = elem[0]; + var module = elem[1]; + prev[namespace] = module; + + return prev; + }, {}); + + self.lang = language; + self.translations = {}; } Translator.prototype.load = load; @@ -221,6 +233,10 @@ var namespace = result[0]; var key = result[1]; + if (self.modules[namespace]) { + return Promise.resolve(self.modules[namespace](key, args)); + } + if (namespace && !key) { return Promise.resolve('[[' + namespace + ']]'); } @@ -307,6 +323,22 @@ Translator.cache = {}; + /** + * Register a custom module to handle translations + * @param {string} namespace - Namespace to handle translation for + * @param {Function} factory - Function to return the translation function for this namespace + */ + Translator.registerModule = function registerModule(namespace, factory) { + Translator.moduleFactories[namespace] = factory; + + Object.keys(Translator.cache).forEach(function (key) { + var translator = Translator.cache[key]; + translator.modules[namespace] = factory(translator.lang); + }); + }; + + Translator.moduleFactories = {}; + return Translator; }()); diff --git a/test/translator.js b/test/translator.js index 673311ddaf..c3740a4f84 100644 --- a/test/translator.js +++ b/test/translator.js @@ -19,9 +19,16 @@ describe('translator shim', function () { }); describe('new Translator(language)', function () { + it('should throw if not passed a language', function (done) { + assert.throws(function () { + new Translator(); + }, /language string/); + done(); + }); + describe('.translate()', function () { it('should handle basic translations', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[global:home]]').then(function (translated) { assert.strictEqual(translated, 'Home'); @@ -30,7 +37,7 @@ describe('new Translator(language)', function () { }); it('should handle language keys in regular text', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('Let\'s go [[global:home]]').then(function (translated) { assert.strictEqual(translated, 'Let\'s go Home'); @@ -39,7 +46,7 @@ describe('new Translator(language)', function () { }); it('should accept a language parameter and adjust accordingly', function (done) { - var translator = new Translator('de'); + var translator = Translator.create('de'); translator.translate('[[global:home]]').then(function (translated) { assert.strictEqual(translated, 'Übersicht'); @@ -48,7 +55,7 @@ describe('new Translator(language)', function () { }); it('should handle language keys in regular text with another language specified', function (done) { - var translator = new Translator('de'); + var translator = Translator.create('de'); translator.translate('[[global:home]] test').then(function (translated) { assert.strictEqual(translated, 'Übersicht test'); @@ -57,7 +64,7 @@ describe('new Translator(language)', function () { }); it('should handle language keys with parameters', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[global:pagination.out_of, 1, 5]]').then(function (translated) { assert.strictEqual(translated, '1 out of 5'); @@ -66,7 +73,7 @@ describe('new Translator(language)', function () { }); it('should handle language keys inside language keys', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[notifications:outgoing_link_message, [[global:guest]]]]').then(function (translated) { assert.strictEqual(translated, 'You are now leaving Guest'); @@ -75,7 +82,7 @@ describe('new Translator(language)', function () { }); it('should handle language keys inside language keys with multiple parameters', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[notifications:user_posted_to, [[global:guest]], My Topic]]').then(function (translated) { assert.strictEqual(translated, 'Guest has posted a reply to: My Topic'); @@ -84,7 +91,7 @@ describe('new Translator(language)', function () { }); it('should handle language keys inside language keys with all parameters as language keys', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[notifications:user_posted_to, [[global:guest]], [[global:guest]]]]').then(function (translated) { assert.strictEqual(translated, 'Guest has posted a reply to: Guest'); @@ -93,7 +100,7 @@ describe('new Translator(language)', function () { }); it('should properly handle parameters that contain square brackets', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[global:pagination.out_of, [guest], [[global:home]]]]').then(function (translated) { assert.strictEqual(translated, '[guest] out of Home'); @@ -102,7 +109,7 @@ describe('new Translator(language)', function () { }); it('should properly handle parameters that contain parentheses', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[global:pagination.out_of, (foobar), [[global:home]]]]').then(function (translated) { assert.strictEqual(translated, '(foobar) out of Home'); @@ -111,7 +118,7 @@ describe('new Translator(language)', function () { }); it('should not translate language key parameters with HTML in them', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); var key = '[[global:403.login, test]]'; translator.translate(key).then(function (translated) { @@ -121,7 +128,7 @@ describe('new Translator(language)', function () { }); it('should properly escape % and ,', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); var title = 'Test 1, 2, 3 % salmon'; title = title.replace(/%/g, '%').replace(/,/g, ','); @@ -132,15 +139,8 @@ describe('new Translator(language)', function () { }); }); - it('should throw if not passed a language', function (done) { - assert.throws(function () { - new Translator(); - }, /language string/); - done(); - }); - it('should not translate [[derp] some text', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[derp] some text').then(function (translated) { assert.strictEqual('[[derp] some text', translated); done(); @@ -148,7 +148,7 @@ describe('new Translator(language)', function () { }); it('should not translate [[derp:xyz] some text', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[derp:xyz] some text').then(function (translated) { assert.strictEqual('[[derp:xyz] some text', translated); done(); @@ -156,7 +156,7 @@ describe('new Translator(language)', function () { }); it('should translate [[pages:users/latest]] properly', function (done) { - var translator = new Translator('en_GB'); + var translator = Translator.create('en_GB'); translator.translate('[[pages:users/latest]]').then(function (translated) { assert.strictEqual(translated, 'Latest Users'); done(); @@ -186,3 +186,50 @@ describe('Translator.create()', function () { done(); }); }); + +describe('Translator modules', function () { + it('should work before registered', function (done) { + var translator = Translator.create(); + + Translator.registerModule('test-custom-integer-format', function (lang) { + return function (key, args) { + var num = parseInt(args[0], 10) || 0; + if (key === 'binary') { + return num.toString(2); + } + if (key === 'hex') { + return num.toString(16); + } + if (key === 'octal') { + return num.toString(8); + } + return num.toString(); + }; + }); + + translator.translate('[[test-custom-integer-format:octal, 24]]') + .then(function (translation) { + assert.strictEqual(translation, '30'); + done(); + }); + }); + + it('should work after registered', function (done) { + var translator = Translator.create('de'); + + translator.translate('[[test-custom-integer-format:octal, 23]]') + .then(function (translation) { + assert.strictEqual(translation, '27'); + done(); + }); + }); + + it('registerModule be passed the language', function (done) { + Translator.registerModule('something', function (lang) { + assert.ok(lang); + }); + + var translator = Translator.create('fr_FR'); + done(); + }); +});