diff --git a/package.json b/package.json index 6e4cfbba84..2132731a42 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "start": "node loader.js", "lint": "eslint --cache .", "pretest": "npm run lint", - "test": "istanbul cover _mocha test" + "test": "istanbul cover _mocha test", + "test-windows": "_mocha test" }, "dependencies": { "async": "~1.5.0", diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index f0fc980666..08525702d5 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -120,12 +120,44 @@ // the current level of nesting of the translation strings var level = 0; var sliced; + var textBeforeColonFound = false; + var colonFound = false; + var textAfterColonFound = false; + var commaAfterNameFound = false; while (cursor + 2 <= len) { sliced = str.slice(cursor, cursor + 2); + // found some text after the double bracket, + // so this is probably a translation string + if (!textBeforeColonFound && sliced[0].match(/[a-zA-Z0-9\-_]/)) { + textBeforeColonFound = true; + cursor += 1; + // found a colon, so this is probably a translation string + } else if (textBeforeColonFound && !colonFound && sliced[0] === ':') { + colonFound = true; + cursor += 1; + // found some text after the colon, + // so this is probably a translation string + } else if (colonFound && !textAfterColonFound && sliced[0].match(/[a-zA-Z0-9\-_]/)) { + textAfterColonFound = true; + cursor += 1; + } else if (textAfterColonFound && !commaAfterNameFound && sliced[0] === ',') { + commaAfterNameFound = true; + cursor += 1; + // a space or comma was found before the name + // this isn't a translation string, so back out + } else if (!(textBeforeColonFound && colonFound && textAfterColonFound && commaAfterNameFound) && + sliced[0].match(/[^a-zA-Z0-9\-_.\]]/)) { + cursor += 1; + lastBreak -= 2; + if (level > 0) { + level -= 1; + } else { + break; + } // if we're at the beginning of another translation string, // we're nested, so add to our level - if (sliced === '[[') { + } else if (sliced === '[[') { level += 1; cursor += 2; // if we're at the end of a translation string diff --git a/test/translator-new.js b/test/translator-new.js deleted file mode 100644 index cdb854ea82..0000000000 --- a/test/translator-new.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict'; -/*global require*/ - -var assert = require('assert'); -var Translator = require('../public/src/modules/translator.js').Translator; - - -describe('new Translator(language)', function(){ - describe('.translate()', function(){ - it('should handle basic translations', function(done) { - var translator = new Translator('en_GB'); - - translator.translate('[[global:home]]').then(function(translated) { - assert.strictEqual(translated, 'Home'); - done(); - }); - }); - - it('should handle language keys in regular text', function(done) { - var translator = new Translator('en_GB'); - - translator.translate('Let\'s go [[global:home]]').then(function(translated) { - assert.strictEqual(translated, 'Let\'s go Home'); - done(); - }); - }); - - it('should accept a language parameter and adjust accordingly', function(done) { - var translator = new Translator('de'); - - translator.translate('[[global:home]]').then(function(translated) { - assert.strictEqual(translated, 'Übersicht'); - done(); - }); - }); - - it('should handle language keys in regular text with another language specified', function(done) { - var translator = new Translator('de'); - - translator.translate('[[global:home]] test').then(function(translated) { - assert.strictEqual(translated, 'Übersicht test'); - done(); - }); - }); - - it('should handle language keys with parameters', function(done) { - var translator = new Translator('en_GB'); - - translator.translate('[[global:pagination.out_of, 1, 5]]').then(function(translated) { - assert.strictEqual(translated, '1 out of 5'); - done(); - }); - }); - - it('should handle language keys inside language keys', function(done) { - var translator = new Translator('en_GB'); - - translator.translate('[[notifications:outgoing_link_message, [[global:guest]]]]').then(function(translated) { - assert.strictEqual(translated, 'You are now leaving Guest'); - done(); - }); - }); - - it('should handle language keys inside language keys with multiple parameters', function(done) { - var translator = new Translator('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'); - done(); - }); - }); - - it('should handle language keys inside language keys with all parameters as language keys', function(done) { - var translator = new Translator('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'); - done(); - }); - }); - - it('should properly handle parameters that contain square brackets', function(done) { - var translator = new Translator('en_GB'); - - translator.translate('[[global:pagination.out_of, [guest], [[global:home]]]]').then(function(translated) { - assert.strictEqual(translated, '[guest] out of Home'); - done(); - }); - }); - - it('should properly handle parameters that contain parentheses', function(done) { - var translator = new Translator('en_GB'); - - translator.translate('[[global:pagination.out_of, (foobar), [[global:home]]]]').then(function(translated) { - assert.strictEqual(translated, '(foobar) out of Home'); - done(); - }); - }); - - it('should not translate language key parameters with HTML in them', function(done) { - var translator = new Translator('en_GB'); - - var key = '[[global:403.login, test]]'; - translator.translate(key).then(function(translated) { - assert.strictEqual(translated, 'Perhaps you should try logging in?'); - done(); - }); - }); - - it('should properly escape % and ,', function(done) { - var translator = new Translator('en_GB'); - - var title = 'Test 1, 2, 3 % salmon'; - title = title.replace(/%/g, '%').replace(/,/g, ','); - var key = "[[topic:composer.replying_to, " + title + "]]"; - translator.translate(key).then(function(translated) { - assert.strictEqual(translated, 'Replying to Test 1, 2, 3 % salmon'); - done(); - }); - }); - - 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'); - translator.translate('[[derp] some text').then(function(translated) { - assert.strictEqual('[[derp] some text', translated); - done(); - }); - }); - }); -}); - -describe('Translator.create()', function(){ - describe('.translate()', function(){ - it('should return an instance of Translator', function(done) { - var translator = Translator.create('en_GB'); - - assert(translator instanceof Translator); - done(); - }); - it('should return the same object for the same language', function(done) { - var one = Translator.create('de'); - var two = Translator.create('de'); - - assert.strictEqual(one, two); - done(); - }); - it('should default to defaultLang', function(done) { - var translator = Translator.create(); - - assert.strictEqual(translator.lang, 'en_GB'); - done(); - }); - - }); -}); diff --git a/test/translator.js b/test/translator.js index 40f5d42ec1..4dc4156e27 100644 --- a/test/translator.js +++ b/test/translator.js @@ -2,122 +2,179 @@ /*global require*/ var assert = require('assert'); -var translator = require('../public/src/modules/translator.js'); +var shim = require('../public/src/modules/translator.js'); +var Translator = shim.Translator; -var plugins = require('../src/plugins'); -var languages = require('../src/languages'); +require('../src/languages').init(function () {}); -languages.init(function(){}); +describe('translator shim', function(){ + describe('.translate()', function(){ + it('should translate correctly', function(done) { + shim.translate('[[global:pagination.out_of, (foobar), [[global:home]]]]', function(translated) { + assert.strictEqual(translated, '(foobar) out of Home'); + done(); + }); + }); + }); +}); -describe('translator adaptor', function(){ +describe('new Translator(language)', function(){ describe('.translate()', function(){ it('should handle basic translations', function(done) { - translator.translate('[[global:home]]', function(translated) { + var translator = new Translator('en_GB'); + + translator.translate('[[global:home]]').then(function(translated) { assert.strictEqual(translated, 'Home'); done(); }); }); it('should handle language keys in regular text', function(done) { - translator.translate('Let\'s go [[global:home]]', function(translated) { + var translator = new Translator('en_GB'); + + translator.translate('Let\'s go [[global:home]]').then(function(translated) { assert.strictEqual(translated, 'Let\'s go Home'); done(); }); }); it('should accept a language parameter and adjust accordingly', function(done) { - translator.translate('[[global:home]]', 'de', function(translated) { + var translator = new Translator('de'); + + translator.translate('[[global:home]]').then(function(translated) { assert.strictEqual(translated, 'Übersicht'); done(); }); }); it('should handle language keys in regular text with another language specified', function(done) { - translator.translate('[[global:home]] test', 'de', function(translated) { + var translator = new Translator('de'); + + translator.translate('[[global:home]] test').then(function(translated) { assert.strictEqual(translated, 'Übersicht test'); done(); }); }); it('should handle language keys with parameters', function(done) { - translator.translate('[[global:pagination.out_of, 1, 5]]', function(translated) { + var translator = new Translator('en_GB'); + + translator.translate('[[global:pagination.out_of, 1, 5]]').then(function(translated) { assert.strictEqual(translated, '1 out of 5'); done(); }); }); it('should handle language keys inside language keys', function(done) { - translator.translate('[[notifications:outgoing_link_message, [[global:guest]]]]', function(translated) { + var translator = new Translator('en_GB'); + + translator.translate('[[notifications:outgoing_link_message, [[global:guest]]]]').then(function(translated) { assert.strictEqual(translated, 'You are now leaving Guest'); done(); }); }); it('should handle language keys inside language keys with multiple parameters', function(done) { - translator.translate('[[notifications:user_posted_to, [[global:guest]], My Topic]]', function(translated) { + var translator = new Translator('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'); done(); }); }); it('should handle language keys inside language keys with all parameters as language keys', function(done) { - translator.translate('[[notifications:user_posted_to, [[global:guest]], [[global:guest]]]]', function(translated) { + var translator = new Translator('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'); done(); }); }); it('should properly handle parameters that contain square brackets', function(done) { - translator.translate('[[global:pagination.out_of, [guest], [[global:home]]]]', function(translated) { + var translator = new Translator('en_GB'); + + translator.translate('[[global:pagination.out_of, [guest], [[global:home]]]]').then(function(translated) { assert.strictEqual(translated, '[guest] out of Home'); done(); }); }); it('should properly handle parameters that contain parentheses', function(done) { - translator.translate('[[global:pagination.out_of, (foobar), [[global:home]]]]', function(translated) { + var translator = new Translator('en_GB'); + + translator.translate('[[global:pagination.out_of, (foobar), [[global:home]]]]').then(function(translated) { assert.strictEqual(translated, '(foobar) out of Home'); done(); }); }); it('should not translate language key parameters with HTML in them', function(done) { + var translator = new Translator('en_GB'); + var key = '[[global:403.login, test]]'; - translator.translate(key, function(translated) { + translator.translate(key).then(function(translated) { assert.strictEqual(translated, 'Perhaps you should try logging in?'); done(); }); }); it('should properly escape % and ,', function(done) { + var translator = new Translator('en_GB'); + var title = 'Test 1, 2, 3 % salmon'; title = title.replace(/%/g, '%').replace(/,/g, ','); var key = "[[topic:composer.replying_to, " + title + "]]"; - translator.translate(key, function(translated) { + translator.translate(key).then(function(translated) { assert.strictEqual(translated, 'Replying to Test 1, 2, 3 % salmon'); done(); }); }); - it('should properly handle translations added by plugins', function(done) { - plugins.customLanguages = { - 'en_GB/myplugin.json': { - 'foo': 'bar', - 'bar': 'baz, and %1' - } - }; + it('should throw if not passed a language', function(done) { + assert.throws(function () { + new Translator(); + }, /language string/); + done(); + }); - translator.translate('[[myplugin:foo]], [[myplugin:bar, quux]]', function(translated) { - assert.strictEqual(translated, 'bar, baz, and quux'); + it('should not translate [[derp] some text', function(done) { + var translator = new Translator('en_GB'); + translator.translate('[[derp] some text').then(function(translated) { + assert.strictEqual('[[derp] some text', translated); done(); }); }); - it('should not translate [[derp] some text', function(done) { - translator.translate('[[derp] some text', function(translated) { - assert.strictEqual('[[derp] some text', translated); + it('should not translate [[derp:xyz] some text', function(done) { + var translator = new Translator('en_GB'); + translator.translate('[[derp:xyz] some text').then(function(translated) { + assert.strictEqual('[[derp:xyz] some text', translated); done(); }); }); }); }); + +describe('Translator.create()', function(){ + it('should return an instance of Translator', function(done) { + var translator = Translator.create('en_GB'); + + assert(translator instanceof Translator); + done(); + }); + it('should return the same object for the same language', function(done) { + var one = Translator.create('de'); + var two = Translator.create('de'); + + assert.strictEqual(one, two); + done(); + }); + it('should default to defaultLang', function(done) { + var translator = Translator.create(); + + assert.strictEqual(translator.lang, 'en_GB'); + done(); + }); +});