Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Commit

Permalink
feat(translateProvider): add use*() methods for async loaders
Browse files Browse the repository at this point in the history
- add translateProvider.useUrlLoader(),
- add translateProvider.useStaticFilesLoader(),
- add translateProvider.useLoader()

One is now able to define a loader service factory and just use it via useLoader().
This makes code more testable and maintainable. registerLoader() is now
deprecated.

Closes #58
  • Loading branch information
0x-r4bbit committed May 22, 2013
1 parent dd192b6 commit f2329cc
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 13 deletions.
61 changes: 48 additions & 13 deletions src/translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY',
$storageKey = $STORAGE_KEY,
$storagePrefix,
$missingTranslationHandlerFactory,
$loaderFactory,
$loaderOptions,
$asyncLoaders = [],
NESTED_OBJECT_DELIMITER = '.';

Expand Down Expand Up @@ -260,7 +262,7 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY',
*/
this.uses = function (langKey) {
if (langKey) {
if (!$translationTable[langKey] && (!$asyncLoaders.length)) {
if (!$translationTable[langKey] && (!$asyncLoaders.length && !$loaderFactory)) {
// only throw an error, when not loading translation data asynchronously
throw new Error("$translateProvider couldn't find translationTable for langKey: '" + langKey + "'");
}
Expand Down Expand Up @@ -414,6 +416,19 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY',
$asyncLoaders.push($loader);
};

this.useUrlLoader = function (url) {
this.useLoader('$translateUrlLoader', { url: url });
};

this.useStaticFilesLoader = function (options) {
this.useLoader('$translateStaticFilesLoader', options);
};

this.useLoader = function (loaderFactory, options) {
$loaderFactory = loaderFactory;
$loaderOptions = options;
};

/**
* @ngdoc function
* @name pascalprecht.translate.$translateProvider#registerLoaderFactory
Expand Down Expand Up @@ -636,18 +651,38 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY',
var deferred = $q.defer();

if (!$translationTable[key]) {
invokeLoading($injector, key).then(function (data) {
$uses = key;

if ($storageFactory) {
Storage.set($translate.storageKey(), $uses);
}
$rootScope.$broadcast('translationChangeSuccess');
deferred.resolve($uses);
}, function (key) {
$rootScope.$broadcast('translationChangeError');
deferred.reject(key);
});

if ($loaderFactory) {
$injector.get($loaderFactory)(angular.extend($loaderOptions, {
key: key
})).then(function (data) {
$uses = key;
translations(key, data);

if ($storageFactory) {
Storage.set($translate.storageKey(), $uses);
}
$rootScope.$broadcast('translationChangeSuccess');
deferred.resolve($uses);
}, function (key) {
$rootScope.$broadcast('translationChangeError');
deferred.reject(key);
});
} else {

invokeLoading($injector, key).then(function (data) {
$uses = key;

if ($storageFactory) {
Storage.set($translate.storageKey(), $uses);
}
$rootScope.$broadcast('translationChangeSuccess');
deferred.resolve($uses);
}, function (key) {
$rootScope.$broadcast('translationChangeError');
deferred.reject(key);
});
}
return deferred.promise;
}

Expand Down
41 changes: 41 additions & 0 deletions src/translateStaticFilesLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
angular.module('pascalprecht.translate')
/**
* @ngdoc factory
* @name pascalprecht.translate.factory:$translateUrlLoader
* @requires $q
* @requires $http
*
* @description
* Creates a loading function for a typical static file url pattern:
* "lang-en_US.json", "lang-de_DE.json", etc. Using this builder,
* the response of these urls must be an object of key-value pairs.
*
* @param {object} options Options object, which gets prefix, suffix and key.
*/
.factory('$translateStaticFilesLoader', ['$q', '$http', function ($q, $http) {

return function (options) {

if (!options || (!options.prefix || !options.suffix)) {
throw new Error('Couldn\'t load static files, no prefix or suffix specified!');
}

var deferred = $q.defer();

$http({
url: [
options.prefix,
options.key,
options.suffix
].join(''),
method: 'GET',
params: ''
}).success(function (data) {
deferred.resolve(data);
}).error(function (data) {
deferred.reject(options.key);
});

return deferred.promise;
};
}]);
39 changes: 39 additions & 0 deletions src/translateUrlLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
angular.module('pascalprecht.translate')
/**
* @ngdoc factory
* @name pascalprecht.translate.factory:$translateUrlLoader
* @requires $q
* @requires $http
*
* @description
* Creates a loading function for a typical dynamic url pattern:
* "locale.php?lang=en_US", "locale.php?lang=de_DE", etc. Prefixing the specified
* url, the current requested, language id will be applied with "?lang={key}".
* Using this service, the response of these urls must be an object of
* key-value pairs.
*
* @param {object} options Options object, which gets the url and key.
*/
.factory('$translateUrlLoader', ['$q', '$http', function ($q, $http) {

return function (options) {

if (!options || !options.url) {
throw new Error('Couldn\'t use urlLoader since no url is given!');
}

var deferred = $q.defer();

$http({
url: options.url,
params: { lang: options.key },
method: 'GET'
}).success(function (data) {
deferred.resolve(data);
}).error(function (data) {
deferred.reject(options.key);
});

return deferred.promise;
};
}]);
73 changes: 73 additions & 0 deletions test/unit/translateServiceSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,79 @@ describe('pascalprecht.translate', function () {
}));
});

describe('useUrlLoader()', function () {
beforeEach(module('pascalprecht.translate', function ($translateProvider) {
$translateProvider.useUrlLoader('foo/bar.json');
}));

var $translate, $httpBackend;

beforeEach(inject(function (_$translate_, _$httpBackend_) {
$httpBackend = _$httpBackend_;
$translate = _$translate_;

$httpBackend.when('GET', 'foo/bar.json?lang=de_DE').respond({it: 'works'});
}));

afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});

it('should fetch url when invoking #uses', function () {
$httpBackend.expectGET('foo/bar.json?lang=de_DE');
$translate.uses('de_DE');
$httpBackend.flush();
});
});

describe('useStaticFilesLoader()', function () {

beforeEach(module('pascalprecht.translate', function ($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'lang_',
suffix: '.json'
});
}));

var $translate, $httpBackend;

beforeEach(inject(function (_$translate_, _$httpBackend_) {
$httpBackend = _$httpBackend_;
$translate = _$translate_;

$httpBackend.when('GET', 'lang_de_DE.json').respond({HEADER: 'Ueberschrift'});
$httpBackend.when('GET', 'lang_en_US.json').respond({HEADER:'Header'});
$httpBackend.when('GET', 'lang_nt_VD.json').respond(404);
}));

afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});

it('should fetch url when invoking #uses(de_DE)', function () {
$httpBackend.expectGET('lang_de_DE.json');
$translate.uses('de_DE');
$httpBackend.flush();
expect($translate('HEADER')).toEqual('Ueberschrift');
});

it('should fetch url when invoking #uses(en_US)', function () {
$httpBackend.expectGET('lang_en_US.json');
$translate.uses('en_US');
$httpBackend.flush();
expect($translate('HEADER')).toEqual('Header');
});

it('should fetch url when invoking #uses invalid', function () {
$httpBackend.expectGET('lang_nt_VD.json');
$translate.uses('nt_VD');
$httpBackend.flush();
expect($translate('HEADER')).toEqual('HEADER');
});
});

describe('register loader as url string', function () {

beforeEach(module('pascalprecht.translate', function ($translateProvider) {
Expand Down
68 changes: 68 additions & 0 deletions test/unit/translateStaticFilesLoaderSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
describe('pascalprecht.translate', function () {

describe('$translateStaticFilesLoader', function () {

var $translate, $httpBackend;

beforeEach(module('pascalprecht.translate'));

beforeEach(inject(function (_$translate_, _$httpBackend_) {
$httpBackend = _$httpBackend_;
$translate = _$translate_;

$httpBackend.when('GET', 'lang_de_DE.json').respond({HEADER: 'Ueberschrift'});
$httpBackend.when('GET', 'lang_en_US.json').respond({HEADER:'Header'});
$httpBackend.when('GET', 'lang_nt_VD.json').respond(404);
}));

afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});

it('should be defined', function () {
inject(function ($translateStaticFilesLoader) {
expect($translateStaticFilesLoader).toBeDefined();
});
});

it('should be a function', function () {
inject(function ($translateStaticFilesLoader) {
expect(typeof $translateStaticFilesLoader).toBe('function');
});
});

it('should throw an error when called without prefix or suffix', function () {
inject(function ($translateStaticFilesLoader) {
expect(function () {
$translateStaticFilesLoader();
}).toThrow('Couldn\'t load static files, no prefix or suffix specified!');
});
});

it('should fetch static files when invoking', function () {
inject(function ($translateStaticFilesLoader) {
$httpBackend.expectGET('lang_de_DE.json');
$translateStaticFilesLoader({
key: 'de_DE',
prefix: 'lang_',
suffix: '.json'
});
$httpBackend.flush();
});
});

it('should return a promise', function () {
inject(function ($translateStaticFilesLoader) {
var promise = $translateStaticFilesLoader({
key: 'de_DE',
prefix: 'lang_',
suffix: '.json'
});
expect(promise.then).toBeDefined();
expect(typeof promise.then).toBe('function');
$httpBackend.flush();
});
});
});
});
66 changes: 66 additions & 0 deletions test/unit/translateUrlLoaderSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
describe('pascalprecht.translate', function () {

describe('$translateUrlLoader', function () {

var $translate, $httpBackend;

beforeEach(module('pascalprecht.translate'));

beforeEach(inject(function (_$translate_, _$httpBackend_) {
$translate = _$translate_;
$httpBackend = _$httpBackend_;

$httpBackend.when('GET', 'foo/bar.json?lang=de_DE').respond({
it: 'works'
});
}));

afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});

it('should be defined', function () {
inject(function ($translateUrlLoader) {
expect($translateUrlLoader).toBeDefined();
});
});

it('should be a function', function () {
inject(function ($translateUrlLoader) {
expect(typeof $translateUrlLoader).toBe('function');
});
});

it('should throw an error when called without url option', function () {
inject(function ($translateUrlLoader) {
expect(function () {
$translateUrlLoader();
}).toThrow('Couldn\'t use urlLoader since no url is given!');
});
});

it('should fetch url when invoking', function () {
inject(function ($translateUrlLoader) {
$httpBackend.expectGET('foo/bar.json?lang=de_DE');
$translateUrlLoader({
key: 'de_DE',
url: 'foo/bar.json'
});
$httpBackend.flush();
});
});

it('should return a promise', function () {
inject(function ($translateUrlLoader) {
var promise = $translateUrlLoader({
key: 'de_DE',
url: 'foo/bar.json'
});
expect(promise.then).toBeDefined();
expect(typeof promise.then).toBe('function');
$httpBackend.flush();
});
});
});
});

0 comments on commit f2329cc

Please sign in to comment.