From 152da9796b0268069a10b73d65781301a307fcdd Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 18 Nov 2014 12:13:44 +0100 Subject: [PATCH 1/4] Added function to load translations from JS For apps that support async translation loading, a new function OC.L10N.load() can be used to asynchronously load the translations for a given app. --- core/js/js.js | 24 +++++++++++++++-- core/js/l10n.js | 41 ++++++++++++++++++++++++++++ core/js/tests/specs/l10nSpec.js | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/core/js/js.js b/core/js/js.js index 39e382b544..eb2f10b51f 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -252,6 +252,17 @@ var OC={ } }, + /** + * Loads translations for the given app asynchronously. + * + * @param {String} app app name + * @param {Function} callback callback to call after loading + * @return {Promise} + */ + addTranslations: function(app, callback) { + return OC.L10N.load(app, callback); + }, + /** * Returns the base name of the given path. * For example for "/abc/somefile.txt" it will return "somefile.txt" @@ -475,6 +486,15 @@ var OC={ return window.matchMedia(media); } return false; + }, + + /** + * Returns the user's locale + * + * @return {String} locale string + */ + getLocale: function() { + return $('html').prop('lang'); } }; @@ -869,9 +889,9 @@ function object(o) { function initCore() { /** - * Set users local to moment.js as soon as possible + * Set users locale to moment.js as soon as possible */ - moment.locale($('html').prop('lang')); + moment.locale(OC.getLocale()); /** diff --git a/core/js/l10n.js b/core/js/l10n.js index e375b7eca8..d091acea04 100644 --- a/core/js/l10n.js +++ b/core/js/l10n.js @@ -26,6 +26,47 @@ OC.L10N = { */ _pluralFunctions: {}, + /** + * Load an app's translation bundle if not loaded already. + * + * @param {String} appName name of the app + * @param {Function} callback callback to be called when + * the translations are loaded + * @return {Promise} promise + */ + load: function(appName, callback) { + // already available ? + if (this._bundles[appName] || OC.getLocale() === 'en') { + if (callback) { + callback(); + } + return; + } + + var self = this; + var deferred = $.Deferred(); + var url = OC.generateUrl( + 'apps/{app}/l10n/{locale}.json', + {app: appName, locale: OC.getLocale()} + ); + + var url = OC.filePath(appName, 'l10n', OC.getLocale() + '.json'); + + // load JSON translation bundle per AJAX + $.get(url, + function(result) { + if (result.translations) { + self.register(appName, result.translations, result.pluralForm); + } + if (callback) { + callback(); + deferred.resolve(); + } + } + ); + return deferred.promise(); + }, + /** * Register an app's translation bundle. * diff --git a/core/js/tests/specs/l10nSpec.js b/core/js/tests/specs/l10nSpec.js index d5b0363ea3..dc021a0baa 100644 --- a/core/js/tests/specs/l10nSpec.js +++ b/core/js/tests/specs/l10nSpec.js @@ -11,8 +11,12 @@ describe('OC.L10N tests', function() { var TEST_APP = 'jsunittestapp'; + beforeEach(function() { + OC.appswebroots[TEST_APP] = OC.webroot + '/apps3/jsunittestapp'; + }); afterEach(function() { delete OC.L10N._bundles[TEST_APP]; + delete OC.appswebroots[TEST_APP]; }); describe('text translation', function() { @@ -98,4 +102,48 @@ describe('OC.L10N tests', function() { checkPlurals(); }); }); + describe('async loading of translations', function() { + it('loads bundle for given app and calls callback', function() { + var localeStub = sinon.stub(OC, 'getLocale').returns('zh_CN'); + var callbackStub = sinon.stub(); + var promiseStub = sinon.stub(); + OC.L10N.load(TEST_APP, callbackStub).then(promiseStub); + expect(callbackStub.notCalled).toEqual(true); + expect(promiseStub.notCalled).toEqual(true); + expect(fakeServer.requests.length).toEqual(1); + var req = fakeServer.requests[0]; + expect(req.url).toEqual( + OC.webroot + '/apps3/' + TEST_APP + '/l10n/zh_CN.json' + ); + req.respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({ + translations: {'Hello world!': '你好世界!'}, + pluralForm: 'nplurals=2; plural=(n != 1);' + }) + ); + + expect(callbackStub.calledOnce).toEqual(true); + expect(promiseStub.calledOnce).toEqual(true); + expect(t(TEST_APP, 'Hello world!')).toEqual('你好世界!'); + localeStub.restore(); + }); + it('calls callback if translation already available', function() { + var callbackStub = sinon.stub(); + OC.L10N.register(TEST_APP, { + 'Hello world!': 'Hallo Welt!' + }); + OC.L10N.load(TEST_APP, callbackStub); + expect(callbackStub.calledOnce).toEqual(true); + expect(fakeServer.requests.length).toEqual(0); + }); + it('calls callback if locale is en', function() { + var localeStub = sinon.stub(OC, 'getLocale').returns('en'); + var callbackStub = sinon.stub(); + OC.L10N.load(TEST_APP, callbackStub); + expect(callbackStub.calledOnce).toEqual(true); + expect(fakeServer.requests.length).toEqual(0); + }); + }); }); From cd60a27ad671b0e2c5dc117436836da012e0d13e Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 19 Nov 2014 10:59:38 +0100 Subject: [PATCH 2/4] Remove stray generateUrl --- core/js/l10n.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/js/l10n.js b/core/js/l10n.js index d091acea04..2db4609ded 100644 --- a/core/js/l10n.js +++ b/core/js/l10n.js @@ -45,11 +45,6 @@ OC.L10N = { var self = this; var deferred = $.Deferred(); - var url = OC.generateUrl( - 'apps/{app}/l10n/{locale}.json', - {app: appName, locale: OC.getLocale()} - ); - var url = OC.filePath(appName, 'l10n', OC.getLocale() + '.json'); // load JSON translation bundle per AJAX From 1c5933c96cd68f353165a02a76dd47760e8d493e Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 19 Nov 2014 14:49:15 +0100 Subject: [PATCH 3/4] Better use of promise in OC.L10N.load() --- core/js/l10n.js | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/core/js/l10n.js b/core/js/l10n.js index 2db4609ded..3e37da0136 100644 --- a/core/js/l10n.js +++ b/core/js/l10n.js @@ -44,22 +44,17 @@ OC.L10N = { } var self = this; - var deferred = $.Deferred(); var url = OC.filePath(appName, 'l10n', OC.getLocale() + '.json'); // load JSON translation bundle per AJAX - $.get(url, - function(result) { - if (result.translations) { - self.register(appName, result.translations, result.pluralForm); - } - if (callback) { - callback(); - deferred.resolve(); - } - } - ); - return deferred.promise(); + return $.get(url) + .then( + function(result) { + if (result.translations) { + self.register(appName, result.translations, result.pluralForm); + } + }) + .then(callback); }, /** From ffe57d89e425771a5c027a3484d772319919d15d Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 19 Nov 2014 17:02:17 +0100 Subject: [PATCH 4/4] Fix l10n promises --- core/js/l10n.js | 9 +++++---- core/js/tests/specs/l10nSpec.js | 8 ++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/core/js/l10n.js b/core/js/l10n.js index 3e37da0136..0c66058432 100644 --- a/core/js/l10n.js +++ b/core/js/l10n.js @@ -37,10 +37,11 @@ OC.L10N = { load: function(appName, callback) { // already available ? if (this._bundles[appName] || OC.getLocale() === 'en') { - if (callback) { - callback(); - } - return; + var deferred = $.Deferred(); + var promise = deferred.promise(); + promise.then(callback); + deferred.resolve(); + return promise; } var self = this; diff --git a/core/js/tests/specs/l10nSpec.js b/core/js/tests/specs/l10nSpec.js index dc021a0baa..cf7c8b11b1 100644 --- a/core/js/tests/specs/l10nSpec.js +++ b/core/js/tests/specs/l10nSpec.js @@ -130,19 +130,23 @@ describe('OC.L10N tests', function() { localeStub.restore(); }); it('calls callback if translation already available', function() { + var promiseStub = sinon.stub(); var callbackStub = sinon.stub(); OC.L10N.register(TEST_APP, { 'Hello world!': 'Hallo Welt!' }); - OC.L10N.load(TEST_APP, callbackStub); + OC.L10N.load(TEST_APP, callbackStub).then(promiseStub); expect(callbackStub.calledOnce).toEqual(true); + expect(promiseStub.calledOnce).toEqual(true); expect(fakeServer.requests.length).toEqual(0); }); it('calls callback if locale is en', function() { var localeStub = sinon.stub(OC, 'getLocale').returns('en'); + var promiseStub = sinon.stub(); var callbackStub = sinon.stub(); - OC.L10N.load(TEST_APP, callbackStub); + OC.L10N.load(TEST_APP, callbackStub).then(promiseStub); expect(callbackStub.calledOnce).toEqual(true); + expect(promiseStub.calledOnce).toEqual(true); expect(fakeServer.requests.length).toEqual(0); }); });