From e59b141bc0785d6f0a44a7b454016e370f6f62e4 Mon Sep 17 00:00:00 2001 From: Pascal Precht Date: Fri, 19 Jul 2013 10:46:39 +0200 Subject: [PATCH] feat(translateService): informs interpolator when locale has changed - this commit also teaches interpolators to temporarly use fallback language if needed --- src/translate.js | 83 ++++++++++++++++++++++++------- test/unit/translateServiceSpec.js | 49 +++++++++++++++++- 2 files changed, 112 insertions(+), 20 deletions(-) diff --git a/src/translate.js b/src/translate.js index e4a7c92dd..29e6966d7 100644 --- a/src/translate.js +++ b/src/translate.js @@ -176,6 +176,18 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', this.useInterpolation('$translateMessageFormatInterpolation'); }; + /** + * @ngdoc function + * @name pascalprecht.translate.$translateProvider#useInterpolation + * @methodOf pascalprecht.translate.$translateProvider + * + * @description + * Tells angular-translate which interpolation style to use as default, application-wide. + * Simply pass a factory/service name. The interpolation service has to implement + * the correct interface. + * + * @param {string} factory Interpolation service name. + */ this.useInterpolation = function (factory) { $interpolationFactory = factory; }; @@ -506,14 +518,24 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', function ($log, $injector, $rootScope, $q) { var Storage, - interpolateFn = $injector.get($interpolationFactory || '$translateDefaultInterpolation').interpolate, + defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'); pendingLoader = false, interpolatorHashMap = {}; + if ($storageFactory) { + Storage = $injector.get($storageFactory); + + if (!Storage.get || !Storage.set) { + throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or set() method!'); + } + } + // if we have additional interpolations that were added via // $translateProvider.addInterpolation(), we have to map'em - if ($interpolatorFactories.length) { + if ($interpolatorFactories.length > 0) { + angular.forEach($interpolatorFactories, function (interpolatorFactory) { + var interpolator = $injector.get(interpolatorFactory); // setting initial locale for each interpolation service interpolator.setLocale($preferredLanguage || $uses); @@ -522,35 +544,44 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', }); } - if ($storageFactory) { - Storage = $injector.get($storageFactory); - - if (!Storage.get || !Storage.set) { - throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or set() method!'); - } - } - var $translate = function (translationId, interpolateParams, interpolationId) { + // determine translation table and current Interpolator var table = $uses ? $translationTable[$uses] : $translationTable, - interpolate = (interpolationId) ? interpolatorHashMap[interpolationId].interpolate : interpolateFn; - + Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator; + + // if the translation id exists, we can just interpolate it if (table && table.hasOwnProperty(translationId)) { - return interpolate(table[translationId], interpolateParams); + return Interpolator.interpolate(table[translationId], interpolateParams); + } + + // looks like the requested translation id doesn't exists. + // Now, if there is a registered handler for missing translations and no + // asyncLoader is pending, we execute the handler + if ($missingTranslationHandlerFactory && !pendingLoader) { + $injector.get($missingTranslationHandlerFactory)(translationId, $uses); } + // since we couldn't translate the inital requested translation id, + // we try it now with a fallback language, if a fallback language is + // configured. if ($uses && $fallbackLanguage && $uses !== $fallbackLanguage){ + var translation = $translationTable[$fallbackLanguage][translationId]; + + // check if a translation for the fallback language exists if (translation) { - return interpolate(translation, interpolateParams); + var returnVal; + // temporarly letting Interpolator know we're using fallback language now. + Interpolator.setLocale($fallbackLanguage); + returnVal = Interpolator.interpolate(translation, interpolateParams); + // after we've interpolated the translation, we reset Interpolator to proper locale. + Interpolator.setLocale($uses); + return returnVal; } } - if ($missingTranslationHandlerFactory && !pendingLoader) { - $injector.get($missingTranslationHandlerFactory)(translationId, $uses); - } - - + // applying notFoundIndicators if ($notFoundIndicatorLeft) { translationId = [$notFoundIndicatorLeft, translationId].join(' '); } @@ -653,6 +684,13 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', Storage.set($translate.storageKey(), $uses); } + // inform default interpolator + defaultInterpolator.setLocale($uses); + // inform all others to! + angular.forEach(interpolatorHashMap, function (interpolator, id) { + interpolatorHashMap[id].setLocale($uses); + }); + pendingLoader = false; $rootScope.$broadcast('translationChangeSuccess'); deferred.resolve($uses); @@ -670,6 +708,13 @@ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', Storage.set($translate.storageKey(), $uses); } + // inform default interpolator + defaultInterpolator.setLocale($uses); + // inform all others to! + angular.forEach(interpolatorHashMap, function (interpolator, id) { + interpolatorHashMap[id].setLocale($uses); + }); + deferred.resolve($uses); $rootScope.$broadcast('translationChangeSuccess'); return deferred.promise; diff --git a/test/unit/translateServiceSpec.js b/test/unit/translateServiceSpec.js index 4194c77ae..e2de84725 100644 --- a/test/unit/translateServiceSpec.js +++ b/test/unit/translateServiceSpec.js @@ -646,6 +646,33 @@ describe('pascalprecht.translate', function () { describe('interpolations', function () { + /*ddescribe('incorrect interpolation service given', function () { + + var $translate; + + beforeEach(module('pascalprecht.translate', function ($translateProvider, $provide) { + $provide.factory('incorrectInterpolationService', function () { + return {}; + }); + + $translateProvider.translations('en', { + FOO: 'FOO' + }); + $translateProvider.useInterpolation('incorrectInterpolationService'); + $translateProvider.preferredLanguage('en'); + })); + + beforeEach(inject(function (_$translate_) { + $translate = _$translate_; + })); + + it('should throw an error when interpolation service interface isn\'t implemented', function () { + expect(function () { + $translate('FOO'); + }).toThrow("Couldn\'t interpolate! Interpolation service doesn\'t implement the correct interface!"); + }); + });*/ + describe('addInterpolation', function () { var $translate; @@ -670,7 +697,11 @@ describe('pascalprecht.translate', function () { // defining the actual interpolate function translateInterpolator.interpolate = function (string, interpolateParams) { - return 'custom interpolation'; + if ($locale == 'de') { + return 'foo'; + } else { + return 'custom interpolation'; + } }; return translateInterpolator; @@ -684,8 +715,13 @@ describe('pascalprecht.translate', function () { 'FOO': 'Some text' }); + $translateProvider.translations('de', { + 'FOO': 'Irgendwas', + 'BAR': 'yupp' + }); // set default language $translateProvider.preferredLanguage('en'); + $translateProvider.fallbackLanguage('de'); })); beforeEach(inject(function (_$translate_) { @@ -699,6 +735,17 @@ describe('pascalprecht.translate', function () { it('should use custom interpolation', function () { expect($translate('FOO', {}, 'custom')).toEqual('custom interpolation'); }); + + it('should inform custom interpolation when language has been changed', function () { + expect($translate('FOO', {}, 'custom')).toEqual('custom interpolation'); + $translate.uses('de'); + expect($translate('FOO', {}, 'custom')).toEqual('foo'); + }); + + it('should use fallback language, if configured', function () { + expect($translate('BAR', {}, 'custom')).toEqual('foo'); + expect($translate('FOO', {}, 'custom')).toEqual('custom interpolation'); + }); }); describe('addInterpolation messageformat', function () {