diff --git a/src/service/loader-partial.js b/src/service/loader-partial.js index 5c5f13521..731e35626 100644 --- a/src/service/loader-partial.js +++ b/src/service/loader-partial.js @@ -40,16 +40,16 @@ angular.module('pascalprecht.translate') return urlTemplate.replace(/\{part\}/g, this.name).replace(/\{lang\}/g, targetLang); }; - Part.prototype.getTable = function(lang, $q, $http, urlTemplate, errorHandler) { + Part.prototype.getTable = function(lang, $q, $http, $httpOptions, urlTemplate, errorHandler) { var deferred = $q.defer(); if (!this.tables[lang]) { var self = this; - $http({ + $http(angular.extend({}, $httpOptions, { method : 'GET', - url : this.parseUrl(urlTemplate, lang) - }).success(function(data){ + url: this.parseUrl(urlTemplate, lang) + })).success(function(data){ self.tables[lang] = data; deferred.resolve(data); }).error(function() { @@ -226,6 +226,7 @@ angular.module('pascalprecht.translate') * @requires $http * @requires $injector * @requires $rootScope + * @requires $translate * * @description * @@ -233,8 +234,8 @@ angular.module('pascalprecht.translate') * * @throws {TypeError} */ - this.$get = ['$rootScope', '$injector', '$q', '$http', - function($rootScope, $injector, $q, $http) { + this.$get = ['$rootScope', '$injector', '$q', '$http', '$translate', + function($rootScope, $injector, $q, $http, $translate) { /** * @ngdoc event @@ -278,9 +279,10 @@ angular.module('pascalprecht.translate') if (hasPart(part) && parts[part].isActive) { loaders.push( parts[part] - .getTable(options.key, $q, $http, options.urlTemplate, errorHandler) + .getTable(options.key, $q, $http, options.$http, options.urlTemplate, errorHandler) .then(addTablePart) ); + parts[part].urlTemplate = options.urlTemplate; } } @@ -386,6 +388,17 @@ angular.module('pascalprecht.translate') if (hasPart(name)) { var wasActive = parts[name].isActive; if (removeData) { + var cache = $translate.loaderCache(); + if (typeof(cache) === 'string') { + // getting on-demand instance of loader + cache = $injector.get(cache); + } + // Purging items from cache... + if (typeof(cache) === 'object') { + angular.forEach(parts[name].tables, function(value, key) { + cache.remove(parts[name].parseUrl(parts[name].urlTemplate, key)); + }); + } delete parts[name]; } else { parts[name].isActive = false; diff --git a/src/service/loader-static-files.js b/src/service/loader-static-files.js index 720d33657..de6b8f6c4 100644 --- a/src/service/loader-static-files.js +++ b/src/service/loader-static-files.js @@ -22,7 +22,7 @@ angular.module('pascalprecht.translate') var deferred = $q.defer(); - $http({ + $http(angular.extend({}, options.$http, { url: [ options.prefix, options.key, @@ -30,7 +30,7 @@ angular.module('pascalprecht.translate') ].join(''), method: 'GET', params: '' - }).success(function (data) { + })).success(function (data) { deferred.resolve(data); }).error(function (data) { deferred.reject(options.key); diff --git a/src/service/loader-url.js b/src/service/loader-url.js index 30f29513e..759a1e069 100644 --- a/src/service/loader-url.js +++ b/src/service/loader-url.js @@ -24,11 +24,11 @@ angular.module('pascalprecht.translate') var deferred = $q.defer(); - $http({ + $http(angular.extend({}, options.$http, { url: options.url, params: { lang: options.key }, method: 'GET' - }).success(function (data) { + })).success(function (data) { deferred.resolve(data); }).error(function (data) { deferred.reject(options.key); diff --git a/src/service/translate.js b/src/service/translate.js index 28f877d3c..084447a19 100644 --- a/src/service/translate.js +++ b/src/service/translate.js @@ -30,7 +30,8 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', $notFoundIndicatorLeft, $notFoundIndicatorRight, $postCompilingEnabled = false, - NESTED_OBJECT_DELIMITER = '.'; + NESTED_OBJECT_DELIMITER = '.', + loaderCache; var version = 'x.y.z'; @@ -705,6 +706,37 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', return $availableLanguageKeys; }; + /** + * @ngdoc function + * @name pascalprecht.translate.$translateProvider#useLoaderCache + * @methodOf pascalprecht.translate.$translateProvider + * + * @description + * Registers a cache for internal $http based loaders. + * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}. + * When false the cache will be disabled (default). When true or undefined + * the cache will be a default (see $cacheFactory). When an object it will + * be treat as a cache object itself: the usage is $http({cache: cache}) + * + * @param {object} cache boolean, string or cache-object + */ + this.useLoaderCache = function (cache) { + if (cache === false) { + // disable cache + loaderCache = undefined; + } else if (cache === true) { + // enable cache using AJS defaults + loaderCache = true; + } else if (typeof(cache) === 'undefined') { + // enable cache using default + loaderCache = '$translationCache'; + } else if (cache) { + // enable cache using given one (see $cacheFactory) + loaderCache = cache; + } + return this; + }; + /** * @ngdoc object * @name pascalprecht.translate.$translate @@ -903,8 +935,17 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', $rootScope.$emit('$translateLoadingStart', {language: key}); pendingLoader = true; + var cache = loaderCache; + if (typeof(cache) === 'string') { + // getting on-demand instance of loader + cache = $injector.get(cache); + } + $injector.get($loaderFactory)(angular.extend($loaderOptions, { - key: key + key: key, + $http: { + cache: cache + } })).then(function (data) { var translationTable = {}; $rootScope.$emit('$translateLoadingSuccess', {language: key}); @@ -1646,6 +1687,20 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', return version; }; + /** + * @ngdoc function + * @name pascalprecht.translate.$translate#loaderCache + * @methodOf pascalprecht.translate.$translate + * + * @description + * Returns the defined loaderCache. + * + * @return {boolean|string|object} current value of loaderCache + */ + $translate.loaderCache = function () { + return loaderCache; + }; + if ($loaderFactory) { // If at least one async loader is defined and there are no @@ -1667,5 +1722,6 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', } return $translate; - }]; + } + ]; }]); diff --git a/src/service/translationCache.js b/src/service/translationCache.js new file mode 100644 index 000000000..6babce309 --- /dev/null +++ b/src/service/translationCache.js @@ -0,0 +1,15 @@ +/** + * @ngdoc service + * @name $translationCache + * @requires $cacheFactory + * + * @description + * The first time a translation table is used, it is loaded in the translation cache for quick retrieval. You + * can load translation tables directly into the cache by consuming the + * `$translationCache` service directly. + * + * @return {object} $cacheFactory object. + */ +angular.module('pascalprecht.translate').factory('$translationCache', ['$cacheFactory', function ($cacheFactory) { + return $cacheFactory('translations'); +}]); \ No newline at end of file diff --git a/test/unit/service/loader-partial.spec.js b/test/unit/service/loader-partial.spec.js index 669ad9f7a..d24b12714 100644 --- a/test/unit/service/loader-partial.spec.js +++ b/test/unit/service/loader-partial.spec.js @@ -598,5 +598,30 @@ describe('pascalprecht.translate', function() { expect(counter).toEqual(2); }); }); + + it('should put a part into a cache and remove from the cache if the part was deleted', function() { + module(function($httpProvider, $translateProvider) { + $httpProvider.defaults.transformRequest.push(CounterHttpInterceptor); + $translateProvider.useLoaderCache(); + }); + + inject(function($translatePartialLoader, $httpBackend, $translationCache) { + $httpBackend.whenGET('/locales/part-en.json').respond(200, '{}'); + + $translatePartialLoader.addPart('part'); + $translatePartialLoader({ + key : 'en', + urlTemplate : '/locales/{part}-{lang}.json', + $http: { + cache: $translationCache + } + }); + $httpBackend.flush(); + expect($translationCache.info().size).toEqual(1); + + $translatePartialLoader.deletePart('part', true); + expect($translationCache.info().size).toEqual(0); + }); + }); }); }); diff --git a/test/unit/service/loader-static-files.spec.js b/test/unit/service/loader-static-files.spec.js index c6cf8e638..c0633780a 100644 --- a/test/unit/service/loader-static-files.spec.js +++ b/test/unit/service/loader-static-files.spec.js @@ -6,10 +6,11 @@ describe('pascalprecht.translate', function () { beforeEach(module('pascalprecht.translate')); - beforeEach(inject(function (_$translate_, _$httpBackend_, _$translateStaticFilesLoader_) { + beforeEach(inject(function (_$translate_, _$httpBackend_, _$translateStaticFilesLoader_, _$translationCache_) { $httpBackend = _$httpBackend_; $translate = _$translate_; $translateStaticFilesLoader = _$translateStaticFilesLoader_; + $translationCache = _$translationCache_; $httpBackend.when('GET', 'lang_de_DE.json').respond({HEADER: 'Ueberschrift'}); $httpBackend.when('GET', 'lang_en_US.json').respond({HEADER:'Header'}); @@ -55,5 +56,19 @@ describe('pascalprecht.translate', function () { expect(typeof promise.then).toBe('function'); $httpBackend.flush(); }); + + it('should put a translation table into a cache', function() { + $httpBackend.expectGET('lang_de_DE.json'); + $translateStaticFilesLoader({ + key: 'de_DE', + prefix: 'lang_', + suffix: '.json', + $http: { + cache: $translationCache + } + }); + $httpBackend.flush(); + expect($translationCache.info().size).toEqual(1); + }); }); }); diff --git a/test/unit/service/loader-url.spec.js b/test/unit/service/loader-url.spec.js index d1f8ebc83..6f20b3e08 100644 --- a/test/unit/service/loader-url.spec.js +++ b/test/unit/service/loader-url.spec.js @@ -6,10 +6,11 @@ describe('pascalprecht.translate', function () { beforeEach(module('pascalprecht.translate')); - beforeEach(inject(function (_$translate_, _$httpBackend_, _$translateUrlLoader_) { + beforeEach(inject(function (_$translate_, _$httpBackend_, _$translateUrlLoader_, _$translationCache_) { $translate = _$translate_; $httpBackend = _$httpBackend_; $translateUrlLoader = _$translateUrlLoader_; + $translationCache = _$translationCache_; $httpBackend.when('GET', 'foo/bar.json?lang=de_DE').respond({ it: 'works' @@ -44,6 +45,19 @@ describe('pascalprecht.translate', function () { $httpBackend.flush(); }); + it('should put a translation table into a cache', function() { + $httpBackend.expectGET('foo/bar.json?lang=de_DE'); + $translateUrlLoader({ + key: 'de_DE', + url: 'foo/bar.json', + $http: { + cache: $translationCache + } + }); + $httpBackend.flush(); + expect($translationCache.info().size).toEqual(1); + }); + it('should return a promise', function () { var promise = $translateUrlLoader({ key: 'de_DE',