| /*! |
| * angular-translate - v2.13.1 - 2016-12-06 |
| * |
| * Copyright (c) 2016 The angular-translate team, Pascal Precht; Licensed MIT |
| */ |
| (function (root, factory) { |
| if (typeof define === 'function' && define.amd) { |
| // AMD. Register as an anonymous module unless amdModuleId is set |
| define([], function () { |
| return (factory()); |
| }); |
| } else if (typeof exports === 'object') { |
| // Node. Does not work with strict CommonJS, but |
| // only CommonJS-like environments that support module.exports, |
| // like Node. |
| module.exports = factory(); |
| } else { |
| factory(); |
| } |
| }(this, function () { |
| |
| /** |
| * @ngdoc overview |
| * @name pascalprecht.translate |
| * |
| * @description |
| * The main module which holds everything together. |
| */ |
| runTranslate.$inject = ['$translate']; |
| $translate.$inject = ['$STORAGE_KEY', '$windowProvider', '$translateSanitizationProvider', 'pascalprechtTranslateOverrider']; |
| $translateDefaultInterpolation.$inject = ['$interpolate', '$translateSanitization']; |
| translateDirective.$inject = ['$translate', '$interpolate', '$compile', '$parse', '$rootScope']; |
| translateAttrDirective.$inject = ['$translate', '$rootScope']; |
| translateCloakDirective.$inject = ['$translate', '$rootScope']; |
| translateFilterFactory.$inject = ['$parse', '$translate']; |
| $translationCache.$inject = ['$cacheFactory']; |
| angular.module('pascalprecht.translate', ['ng']) |
| .run(runTranslate); |
| |
| function runTranslate($translate) { |
| |
| 'use strict'; |
| |
| var key = $translate.storageKey(), |
| storage = $translate.storage(); |
| |
| var fallbackFromIncorrectStorageValue = function () { |
| var preferred = $translate.preferredLanguage(); |
| if (angular.isString(preferred)) { |
| $translate.use(preferred); |
| // $translate.use() will also remember the language. |
| // So, we don't need to call storage.put() here. |
| } else { |
| storage.put(key, $translate.use()); |
| } |
| }; |
| |
| fallbackFromIncorrectStorageValue.displayName = 'fallbackFromIncorrectStorageValue'; |
| |
| if (storage) { |
| if (!storage.get(key)) { |
| fallbackFromIncorrectStorageValue(); |
| } else { |
| $translate.use(storage.get(key))['catch'](fallbackFromIncorrectStorageValue); |
| } |
| } else if (angular.isString($translate.preferredLanguage())) { |
| $translate.use($translate.preferredLanguage()); |
| } |
| } |
| |
| runTranslate.displayName = 'runTranslate'; |
| |
| /** |
| * @ngdoc object |
| * @name pascalprecht.translate.$translateSanitizationProvider |
| * |
| * @description |
| * |
| * Configurations for $translateSanitization |
| */ |
| angular.module('pascalprecht.translate').provider('$translateSanitization', $translateSanitizationProvider); |
| |
| function $translateSanitizationProvider () { |
| |
| 'use strict'; |
| |
| var $sanitize, |
| $sce, |
| currentStrategy = null, // TODO change to either 'sanitize', 'escape' or ['sanitize', 'escapeParameters'] in 3.0. |
| hasConfiguredStrategy = false, |
| hasShownNoStrategyConfiguredWarning = false, |
| strategies; |
| |
| /** |
| * Definition of a sanitization strategy function |
| * @callback StrategyFunction |
| * @param {string|object} value - value to be sanitized (either a string or an interpolated value map) |
| * @param {string} mode - either 'text' for a string (translation) or 'params' for the interpolated params |
| * @return {string|object} |
| */ |
| |
| /** |
| * @ngdoc property |
| * @name strategies |
| * @propertyOf pascalprecht.translate.$translateSanitizationProvider |
| * |
| * @description |
| * Following strategies are built-in: |
| * <dl> |
| * <dt>sanitize</dt> |
| * <dd>Sanitizes HTML in the translation text using $sanitize</dd> |
| * <dt>escape</dt> |
| * <dd>Escapes HTML in the translation</dd> |
| * <dt>sanitizeParameters</dt> |
| * <dd>Sanitizes HTML in the values of the interpolation parameters using $sanitize</dd> |
| * <dt>escapeParameters</dt> |
| * <dd>Escapes HTML in the values of the interpolation parameters</dd> |
| * <dt>escaped</dt> |
| * <dd>Support legacy strategy name 'escaped' for backwards compatibility (will be removed in 3.0)</dd> |
| * </dl> |
| * |
| */ |
| |
| strategies = { |
| sanitize: function (value, mode/*, context*/) { |
| if (mode === 'text') { |
| value = htmlSanitizeValue(value); |
| } |
| return value; |
| }, |
| escape: function (value, mode/*, context*/) { |
| if (mode === 'text') { |
| value = htmlEscapeValue(value); |
| } |
| return value; |
| }, |
| sanitizeParameters: function (value, mode/*, context*/) { |
| if (mode === 'params') { |
| value = mapInterpolationParameters(value, htmlSanitizeValue); |
| } |
| return value; |
| }, |
| escapeParameters: function (value, mode/*, context*/) { |
| if (mode === 'params') { |
| value = mapInterpolationParameters(value, htmlEscapeValue); |
| } |
| return value; |
| }, |
| sce: function (value, mode, context) { |
| if (mode === 'text') { |
| value = htmlTrustValue(value); |
| } else if (mode === 'params') { |
| if (context !== 'filter') { |
| // do html escape in filter context #1101 |
| value = mapInterpolationParameters(value, htmlEscapeValue); |
| } |
| } |
| return value; |
| }, |
| sceParameters: function (value, mode/*, context*/) { |
| if (mode === 'params') { |
| value = mapInterpolationParameters(value, htmlTrustValue); |
| } |
| return value; |
| } |
| }; |
| // Support legacy strategy name 'escaped' for backwards compatibility. |
| // TODO should be removed in 3.0 |
| strategies.escaped = strategies.escapeParameters; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateSanitizationProvider#addStrategy |
| * @methodOf pascalprecht.translate.$translateSanitizationProvider |
| * |
| * @description |
| * Adds a sanitization strategy to the list of known strategies. |
| * |
| * @param {string} strategyName - unique key for a strategy |
| * @param {StrategyFunction} strategyFunction - strategy function |
| * @returns {object} this |
| */ |
| this.addStrategy = function (strategyName, strategyFunction) { |
| strategies[strategyName] = strategyFunction; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateSanitizationProvider#removeStrategy |
| * @methodOf pascalprecht.translate.$translateSanitizationProvider |
| * |
| * @description |
| * Removes a sanitization strategy from the list of known strategies. |
| * |
| * @param {string} strategyName - unique key for a strategy |
| * @returns {object} this |
| */ |
| this.removeStrategy = function (strategyName) { |
| delete strategies[strategyName]; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateSanitizationProvider#useStrategy |
| * @methodOf pascalprecht.translate.$translateSanitizationProvider |
| * |
| * @description |
| * Selects a sanitization strategy. When an array is provided the strategies will be executed in order. |
| * |
| * @param {string|StrategyFunction|array} strategy The sanitization strategy / strategies which should be used. Either a name of an existing strategy, a custom strategy function, or an array consisting of multiple names and / or custom functions. |
| * @returns {object} this |
| */ |
| this.useStrategy = function (strategy) { |
| hasConfiguredStrategy = true; |
| currentStrategy = strategy; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc object |
| * @name pascalprecht.translate.$translateSanitization |
| * @requires $injector |
| * @requires $log |
| * |
| * @description |
| * Sanitizes interpolation parameters and translated texts. |
| * |
| */ |
| this.$get = ['$injector', '$log', function ($injector, $log) { |
| |
| var cachedStrategyMap = {}; |
| |
| var applyStrategies = function (value, mode, context, selectedStrategies) { |
| angular.forEach(selectedStrategies, function (selectedStrategy) { |
| if (angular.isFunction(selectedStrategy)) { |
| value = selectedStrategy(value, mode, context); |
| } else if (angular.isFunction(strategies[selectedStrategy])) { |
| value = strategies[selectedStrategy](value, mode, context); |
| } else if (angular.isString(strategies[selectedStrategy])) { |
| if (!cachedStrategyMap[strategies[selectedStrategy]]) { |
| try { |
| cachedStrategyMap[strategies[selectedStrategy]] = $injector.get(strategies[selectedStrategy]); |
| } catch (e) { |
| cachedStrategyMap[strategies[selectedStrategy]] = function() {}; |
| throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\''); |
| } |
| } |
| value = cachedStrategyMap[strategies[selectedStrategy]](value, mode, context); |
| } else { |
| throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\''); |
| } |
| }); |
| return value; |
| }; |
| |
| // TODO: should be removed in 3.0 |
| var showNoStrategyConfiguredWarning = function () { |
| if (!hasConfiguredStrategy && !hasShownNoStrategyConfiguredWarning) { |
| $log.warn('pascalprecht.translate.$translateSanitization: No sanitization strategy has been configured. This can have serious security implications. See http://angular-translate.github.io/docs/#/guide/19_security for details.'); |
| hasShownNoStrategyConfiguredWarning = true; |
| } |
| }; |
| |
| if ($injector.has('$sanitize')) { |
| $sanitize = $injector.get('$sanitize'); |
| } |
| if ($injector.has('$sce')) { |
| $sce = $injector.get('$sce'); |
| } |
| |
| return { |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateSanitization#useStrategy |
| * @methodOf pascalprecht.translate.$translateSanitization |
| * |
| * @description |
| * Selects a sanitization strategy. When an array is provided the strategies will be executed in order. |
| * |
| * @param {string|StrategyFunction|array} strategy The sanitization strategy / strategies which should be used. Either a name of an existing strategy, a custom strategy function, or an array consisting of multiple names and / or custom functions. |
| */ |
| useStrategy: (function (self) { |
| return function (strategy) { |
| self.useStrategy(strategy); |
| }; |
| })(this), |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateSanitization#sanitize |
| * @methodOf pascalprecht.translate.$translateSanitization |
| * |
| * @description |
| * Sanitizes a value. |
| * |
| * @param {string|object} value The value which should be sanitized. |
| * @param {string} mode The current sanitization mode, either 'params' or 'text'. |
| * @param {string|StrategyFunction|array} [strategy] Optional custom strategy which should be used instead of the currently selected strategy. |
| * @param {string} [context] The context of this call: filter, service. Default is service |
| * @returns {string|object} sanitized value |
| */ |
| sanitize: function (value, mode, strategy, context) { |
| if (!currentStrategy) { |
| showNoStrategyConfiguredWarning(); |
| } |
| |
| if (!strategy && strategy !== null) { |
| strategy = currentStrategy; |
| } |
| |
| if (!strategy) { |
| return value; |
| } |
| |
| if (!context) { |
| context = 'service'; |
| } |
| |
| var selectedStrategies = angular.isArray(strategy) ? strategy : [strategy]; |
| return applyStrategies(value, mode, context, selectedStrategies); |
| } |
| }; |
| }]; |
| |
| var htmlEscapeValue = function (value) { |
| var element = angular.element('<div></div>'); |
| element.text(value); // not chainable, see #1044 |
| return element.html(); |
| }; |
| |
| var htmlSanitizeValue = function (value) { |
| if (!$sanitize) { |
| throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sanitize service. Either include the ngSanitize module (https://docs.angularjs.org/api/ngSanitize) or use a sanitization strategy which does not depend on $sanitize, such as \'escape\'.'); |
| } |
| return $sanitize(value); |
| }; |
| |
| var htmlTrustValue = function (value) { |
| if (!$sce) { |
| throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sce service.'); |
| } |
| return $sce.trustAsHtml(value); |
| }; |
| |
| var mapInterpolationParameters = function (value, iteratee, stack) { |
| if (angular.isDate(value)) { |
| return value; |
| } else if (angular.isObject(value)) { |
| var result = angular.isArray(value) ? [] : {}; |
| |
| if (!stack) { |
| stack = []; |
| } else { |
| if (stack.indexOf(value) > -1) { |
| throw new Error('pascalprecht.translate.$translateSanitization: Error cannot interpolate parameter due recursive object'); |
| } |
| } |
| |
| stack.push(value); |
| angular.forEach(value, function (propertyValue, propertyKey) { |
| |
| /* Skipping function properties. */ |
| if (angular.isFunction(propertyValue)) { |
| return; |
| } |
| |
| result[propertyKey] = mapInterpolationParameters(propertyValue, iteratee, stack); |
| }); |
| stack.splice(-1, 1); // remove last |
| |
| return result; |
| } else if (angular.isNumber(value)) { |
| return value; |
| } else { |
| return iteratee(value); |
| } |
| }; |
| } |
| |
| /** |
| * @ngdoc object |
| * @name pascalprecht.translate.$translateProvider |
| * @description |
| * |
| * $translateProvider allows developers to register translation-tables, asynchronous loaders |
| * and similar to configure translation behavior directly inside of a module. |
| * |
| */ |
| angular.module('pascalprecht.translate') |
| .constant('pascalprechtTranslateOverrider', {}) |
| .provider('$translate', $translate); |
| |
| function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvider, pascalprechtTranslateOverrider) { |
| |
| 'use strict'; |
| |
| var $translationTable = {}, |
| $preferredLanguage, |
| $availableLanguageKeys = [], |
| $languageKeyAliases, |
| $fallbackLanguage, |
| $fallbackWasString, |
| $uses, |
| $nextLang, |
| $storageFactory, |
| $storageKey = $STORAGE_KEY, |
| $storagePrefix, |
| $missingTranslationHandlerFactory, |
| $interpolationFactory, |
| $interpolatorFactories = [], |
| $loaderFactory, |
| $cloakClassName = 'translate-cloak', |
| $loaderOptions, |
| $notFoundIndicatorLeft, |
| $notFoundIndicatorRight, |
| $postCompilingEnabled = false, |
| $forceAsyncReloadEnabled = false, |
| $nestedObjectDelimeter = '.', |
| $isReady = false, |
| $keepContent = false, |
| loaderCache, |
| directivePriority = 0, |
| statefulFilter = true, |
| postProcessFn, |
| uniformLanguageTagResolver = 'default', |
| languageTagResolver = { |
| 'default' : function (tag) { |
| return (tag || '').split('-').join('_'); |
| }, |
| java : function (tag) { |
| var temp = (tag || '').split('-').join('_'); |
| var parts = temp.split('_'); |
| return parts.length > 1 ? (parts[0].toLowerCase() + '_' + parts[1].toUpperCase()) : temp; |
| }, |
| bcp47 : function (tag) { |
| var temp = (tag || '').split('_').join('-'); |
| var parts = temp.split('-'); |
| return parts.length > 1 ? (parts[0].toLowerCase() + '-' + parts[1].toUpperCase()) : temp; |
| }, |
| 'iso639-1' : function (tag) { |
| var temp = (tag || '').split('_').join('-'); |
| var parts = temp.split('-'); |
| return parts[0].toLowerCase(); |
| } |
| }; |
| |
| var version = '2.13.1'; |
| |
| // tries to determine the browsers language |
| var getFirstBrowserLanguage = function () { |
| |
| // internal purpose only |
| if (angular.isFunction(pascalprechtTranslateOverrider.getLocale)) { |
| return pascalprechtTranslateOverrider.getLocale(); |
| } |
| |
| var nav = $windowProvider.$get().navigator, |
| browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'], |
| i, |
| language; |
| |
| // support for HTML 5.1 "navigator.languages" |
| if (angular.isArray(nav.languages)) { |
| for (i = 0; i < nav.languages.length; i++) { |
| language = nav.languages[i]; |
| if (language && language.length) { |
| return language; |
| } |
| } |
| } |
| |
| // support for other well known properties in browsers |
| for (i = 0; i < browserLanguagePropertyKeys.length; i++) { |
| language = nav[browserLanguagePropertyKeys[i]]; |
| if (language && language.length) { |
| return language; |
| } |
| } |
| |
| return null; |
| }; |
| getFirstBrowserLanguage.displayName = 'angular-translate/service: getFirstBrowserLanguage'; |
| |
| // tries to determine the browsers locale |
| var getLocale = function () { |
| var locale = getFirstBrowserLanguage() || ''; |
| if (languageTagResolver[uniformLanguageTagResolver]) { |
| locale = languageTagResolver[uniformLanguageTagResolver](locale); |
| } |
| return locale; |
| }; |
| getLocale.displayName = 'angular-translate/service: getLocale'; |
| |
| /** |
| * @name indexOf |
| * @private |
| * |
| * @description |
| * indexOf polyfill. Kinda sorta. |
| * |
| * @param {array} array Array to search in. |
| * @param {string} searchElement Element to search for. |
| * |
| * @returns {int} Index of search element. |
| */ |
| var indexOf = function (array, searchElement) { |
| for (var i = 0, len = array.length; i < len; i++) { |
| if (array[i] === searchElement) { |
| return i; |
| } |
| } |
| return -1; |
| }; |
| |
| /** |
| * @name trim |
| * @private |
| * |
| * @description |
| * trim polyfill |
| * |
| * @returns {string} The string stripped of whitespace from both ends |
| */ |
| var trim = function () { |
| return this.toString().replace(/^\s+|\s+$/g, ''); |
| }; |
| |
| var negotiateLocale = function (preferred) { |
| if (!preferred) { |
| return; |
| } |
| |
| var avail = [], |
| locale = angular.lowercase(preferred), |
| i = 0, |
| n = $availableLanguageKeys.length; |
| |
| for (; i < n; i++) { |
| avail.push(angular.lowercase($availableLanguageKeys[i])); |
| } |
| |
| // Check for an exact match in our list of available keys |
| if (indexOf(avail, locale) > -1) { |
| return preferred; |
| } |
| |
| if ($languageKeyAliases) { |
| var alias; |
| for (var langKeyAlias in $languageKeyAliases) { |
| if ($languageKeyAliases.hasOwnProperty(langKeyAlias)) { |
| var hasWildcardKey = false; |
| var hasExactKey = Object.prototype.hasOwnProperty.call($languageKeyAliases, langKeyAlias) && |
| angular.lowercase(langKeyAlias) === angular.lowercase(preferred); |
| |
| if (langKeyAlias.slice(-1) === '*') { |
| hasWildcardKey = langKeyAlias.slice(0, -1) === preferred.slice(0, langKeyAlias.length - 1); |
| } |
| if (hasExactKey || hasWildcardKey) { |
| alias = $languageKeyAliases[langKeyAlias]; |
| if (indexOf(avail, angular.lowercase(alias)) > -1) { |
| return alias; |
| } |
| } |
| } |
| } |
| } |
| |
| // Check for a language code without region |
| var parts = preferred.split('_'); |
| |
| if (parts.length > 1 && indexOf(avail, angular.lowercase(parts[0])) > -1) { |
| return parts[0]; |
| } |
| |
| // If everything fails, return undefined. |
| return; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#translations |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Registers a new translation table for specific language key. |
| * |
| * To register a translation table for specific language, pass a defined language |
| * key as first parameter. |
| * |
| * <pre> |
| * // register translation table for language: 'de_DE' |
| * $translateProvider.translations('de_DE', { |
| * 'GREETING': 'Hallo Welt!' |
| * }); |
| * |
| * // register another one |
| * $translateProvider.translations('en_US', { |
| * 'GREETING': 'Hello world!' |
| * }); |
| * </pre> |
| * |
| * When registering multiple translation tables for for the same language key, |
| * the actual translation table gets extended. This allows you to define module |
| * specific translation which only get added, once a specific module is loaded in |
| * your app. |
| * |
| * Invoking this method with no arguments returns the translation table which was |
| * registered with no language key. Invoking it with a language key returns the |
| * related translation table. |
| * |
| * @param {string} langKey A language key. |
| * @param {object} translationTable A plain old JavaScript object that represents a translation table. |
| * |
| */ |
| var translations = function (langKey, translationTable) { |
| |
| if (!langKey && !translationTable) { |
| return $translationTable; |
| } |
| |
| if (langKey && !translationTable) { |
| if (angular.isString(langKey)) { |
| return $translationTable[langKey]; |
| } |
| } else { |
| if (!angular.isObject($translationTable[langKey])) { |
| $translationTable[langKey] = {}; |
| } |
| angular.extend($translationTable[langKey], flatObject(translationTable)); |
| } |
| return this; |
| }; |
| |
| this.translations = translations; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#cloakClassName |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * |
| * Let's you change the class name for `translate-cloak` directive. |
| * Default class name is `translate-cloak`. |
| * |
| * @param {string} name translate-cloak class name |
| */ |
| this.cloakClassName = function (name) { |
| if (!name) { |
| return $cloakClassName; |
| } |
| $cloakClassName = name; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#nestedObjectDelimeter |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * |
| * Let's you change the delimiter for namespaced translations. |
| * Default delimiter is `.`. |
| * |
| * @param {string} delimiter namespace separator |
| */ |
| this.nestedObjectDelimeter = function (delimiter) { |
| if (!delimiter) { |
| return $nestedObjectDelimeter; |
| } |
| $nestedObjectDelimeter = delimiter; |
| return this; |
| }; |
| |
| /** |
| * @name flatObject |
| * @private |
| * |
| * @description |
| * Flats an object. This function is used to flatten given translation data with |
| * namespaces, so they are later accessible via dot notation. |
| */ |
| var flatObject = function (data, path, result, prevKey) { |
| var key, keyWithPath, keyWithShortPath, val; |
| |
| if (!path) { |
| path = []; |
| } |
| if (!result) { |
| result = {}; |
| } |
| for (key in data) { |
| if (!Object.prototype.hasOwnProperty.call(data, key)) { |
| continue; |
| } |
| val = data[key]; |
| if (angular.isObject(val)) { |
| flatObject(val, path.concat(key), result, key); |
| } else { |
| keyWithPath = path.length ? ('' + path.join($nestedObjectDelimeter) + $nestedObjectDelimeter + key) : key; |
| if (path.length && key === prevKey) { |
| // Create shortcut path (foo.bar == foo.bar.bar) |
| keyWithShortPath = '' + path.join($nestedObjectDelimeter); |
| // Link it to original path |
| result[keyWithShortPath] = '@:' + keyWithPath; |
| } |
| result[keyWithPath] = val; |
| } |
| } |
| return result; |
| }; |
| flatObject.displayName = 'flatObject'; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#addInterpolation |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Adds interpolation services to angular-translate, so it can manage them. |
| * |
| * @param {object} factory Interpolation service factory |
| */ |
| this.addInterpolation = function (factory) { |
| $interpolatorFactories.push(factory); |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useMessageFormatInterpolation |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to use interpolation functionality of messageformat.js. |
| * This is useful when having high level pluralization and gender selection. |
| */ |
| this.useMessageFormatInterpolation = function () { |
| return 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; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useSanitizeStrategy |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Simply sets a sanitation strategy type. |
| * |
| * @param {string} value Strategy type. |
| */ |
| this.useSanitizeValueStrategy = function (value) { |
| $translateSanitizationProvider.useStrategy(value); |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#preferredLanguage |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells the module which of the registered translation tables to use for translation |
| * at initial startup by passing a language key. Similar to `$translateProvider#use` |
| * only that it says which language to **prefer**. |
| * |
| * @param {string} langKey A language key. |
| */ |
| this.preferredLanguage = function (langKey) { |
| if (langKey) { |
| setupPreferredLanguage(langKey); |
| return this; |
| } |
| return $preferredLanguage; |
| }; |
| var setupPreferredLanguage = function (langKey) { |
| if (langKey) { |
| $preferredLanguage = langKey; |
| } |
| return $preferredLanguage; |
| }; |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicator |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Sets an indicator which is used when a translation isn't found. E.g. when |
| * setting the indicator as 'X' and one tries to translate a translation id |
| * called `NOT_FOUND`, this will result in `X NOT_FOUND X`. |
| * |
| * Internally this methods sets a left indicator and a right indicator using |
| * `$translateProvider.translationNotFoundIndicatorLeft()` and |
| * `$translateProvider.translationNotFoundIndicatorRight()`. |
| * |
| * **Note**: These methods automatically add a whitespace between the indicators |
| * and the translation id. |
| * |
| * @param {string} indicator An indicator, could be any string. |
| */ |
| this.translationNotFoundIndicator = function (indicator) { |
| this.translationNotFoundIndicatorLeft(indicator); |
| this.translationNotFoundIndicatorRight(indicator); |
| return this; |
| }; |
| |
| /** |
| * ngdoc function |
| * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Sets an indicator which is used when a translation isn't found left to the |
| * translation id. |
| * |
| * @param {string} indicator An indicator. |
| */ |
| this.translationNotFoundIndicatorLeft = function (indicator) { |
| if (!indicator) { |
| return $notFoundIndicatorLeft; |
| } |
| $notFoundIndicatorLeft = indicator; |
| return this; |
| }; |
| |
| /** |
| * ngdoc function |
| * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Sets an indicator which is used when a translation isn't found right to the |
| * translation id. |
| * |
| * @param {string} indicator An indicator. |
| */ |
| this.translationNotFoundIndicatorRight = function (indicator) { |
| if (!indicator) { |
| return $notFoundIndicatorRight; |
| } |
| $notFoundIndicatorRight = indicator; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#fallbackLanguage |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells the module which of the registered translation tables to use when missing translations |
| * at initial startup by passing a language key. Similar to `$translateProvider#use` |
| * only that it says which language to **fallback**. |
| * |
| * @param {string||array} langKey A language key. |
| * |
| */ |
| this.fallbackLanguage = function (langKey) { |
| fallbackStack(langKey); |
| return this; |
| }; |
| |
| var fallbackStack = function (langKey) { |
| if (langKey) { |
| if (angular.isString(langKey)) { |
| $fallbackWasString = true; |
| $fallbackLanguage = [langKey]; |
| } else if (angular.isArray(langKey)) { |
| $fallbackWasString = false; |
| $fallbackLanguage = langKey; |
| } |
| if (angular.isString($preferredLanguage) && indexOf($fallbackLanguage, $preferredLanguage) < 0) { |
| $fallbackLanguage.push($preferredLanguage); |
| } |
| |
| return this; |
| } else { |
| if ($fallbackWasString) { |
| return $fallbackLanguage[0]; |
| } else { |
| return $fallbackLanguage; |
| } |
| } |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#use |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Set which translation table to use for translation by given language key. When |
| * trying to 'use' a language which isn't provided, it'll throw an error. |
| * |
| * You actually don't have to use this method since `$translateProvider#preferredLanguage` |
| * does the job too. |
| * |
| * @param {string} langKey A language key. |
| */ |
| this.use = function (langKey) { |
| if (langKey) { |
| if (!$translationTable[langKey] && (!$loaderFactory)) { |
| // only throw an error, when not loading translation data asynchronously |
| throw new Error('$translateProvider couldn\'t find translationTable for langKey: \'' + langKey + '\''); |
| } |
| $uses = langKey; |
| return this; |
| } |
| return $uses; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#resolveClientLocale |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * This returns the current browser/client's language key. The result is processed with the configured uniform tag resolver. |
| * |
| * @returns {string} the current client/browser language key |
| */ |
| this.resolveClientLocale = function () { |
| return getLocale(); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#storageKey |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells the module which key must represent the choosed language by a user in the storage. |
| * |
| * @param {string} key A key for the storage. |
| */ |
| var storageKey = function (key) { |
| if (!key) { |
| if ($storagePrefix) { |
| return $storagePrefix + $storageKey; |
| } |
| return $storageKey; |
| } |
| $storageKey = key; |
| return this; |
| }; |
| |
| this.storageKey = storageKey; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useUrlLoader |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to use `$translateUrlLoader` extension service as loader. |
| * |
| * @param {string} url Url |
| * @param {Object=} options Optional configuration object |
| */ |
| this.useUrlLoader = function (url, options) { |
| return this.useLoader('$translateUrlLoader', angular.extend({url : url}, options)); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useStaticFilesLoader |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to use `$translateStaticFilesLoader` extension service as loader. |
| * |
| * @param {Object=} options Optional configuration object |
| */ |
| this.useStaticFilesLoader = function (options) { |
| return this.useLoader('$translateStaticFilesLoader', options); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useLoader |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to use any other service as loader. |
| * |
| * @param {string} loaderFactory Factory name to use |
| * @param {Object=} options Optional configuration object |
| */ |
| this.useLoader = function (loaderFactory, options) { |
| $loaderFactory = loaderFactory; |
| $loaderOptions = options || {}; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useLocalStorage |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to use `$translateLocalStorage` service as storage layer. |
| * |
| */ |
| this.useLocalStorage = function () { |
| return this.useStorage('$translateLocalStorage'); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useCookieStorage |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to use `$translateCookieStorage` service as storage layer. |
| */ |
| this.useCookieStorage = function () { |
| return this.useStorage('$translateCookieStorage'); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useStorage |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to use custom service as storage layer. |
| */ |
| this.useStorage = function (storageFactory) { |
| $storageFactory = storageFactory; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#storagePrefix |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Sets prefix for storage key. |
| * |
| * @param {string} prefix Storage key prefix |
| */ |
| this.storagePrefix = function (prefix) { |
| if (!prefix) { |
| return prefix; |
| } |
| $storagePrefix = prefix; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandlerLog |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to use built-in log handler when trying to translate |
| * a translation Id which doesn't exist. |
| * |
| * This is actually a shortcut method for `useMissingTranslationHandler()`. |
| * |
| */ |
| this.useMissingTranslationHandlerLog = function () { |
| return this.useMissingTranslationHandler('$translateMissingTranslationHandlerLog'); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandler |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Expects a factory name which later gets instantiated with `$injector`. |
| * This method can be used to tell angular-translate to use a custom |
| * missingTranslationHandler. Just build a factory which returns a function |
| * and expects a translation id as argument. |
| * |
| * Example: |
| * <pre> |
| * app.config(function ($translateProvider) { |
| * $translateProvider.useMissingTranslationHandler('customHandler'); |
| * }); |
| * |
| * app.factory('customHandler', function (dep1, dep2) { |
| * return function (translationId) { |
| * // something with translationId and dep1 and dep2 |
| * }; |
| * }); |
| * </pre> |
| * |
| * @param {string} factory Factory name |
| */ |
| this.useMissingTranslationHandler = function (factory) { |
| $missingTranslationHandlerFactory = factory; |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#usePostCompiling |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * If post compiling is enabled, all translated values will be processed |
| * again with AngularJS' $compile. |
| * |
| * Example: |
| * <pre> |
| * app.config(function ($translateProvider) { |
| * $translateProvider.usePostCompiling(true); |
| * }); |
| * </pre> |
| * |
| * @param {string} factory Factory name |
| */ |
| this.usePostCompiling = function (value) { |
| $postCompilingEnabled = !(!value); |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#forceAsyncReload |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * If force async reload is enabled, async loader will always be called |
| * even if $translationTable already contains the language key, adding |
| * possible new entries to the $translationTable. |
| * |
| * Example: |
| * <pre> |
| * app.config(function ($translateProvider) { |
| * $translateProvider.forceAsyncReload(true); |
| * }); |
| * </pre> |
| * |
| * @param {boolean} value - valid values are true or false |
| */ |
| this.forceAsyncReload = function (value) { |
| $forceAsyncReloadEnabled = !(!value); |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#uniformLanguageTag |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate which language tag should be used as a result when determining |
| * the current browser language. |
| * |
| * This setting must be set before invoking {@link pascalprecht.translate.$translateProvider#methods_determinePreferredLanguage determinePreferredLanguage()}. |
| * |
| * <pre> |
| * $translateProvider |
| * .uniformLanguageTag('bcp47') |
| * .determinePreferredLanguage() |
| * </pre> |
| * |
| * The resolver currently supports: |
| * * default |
| * (traditionally: hyphens will be converted into underscores, i.e. en-US => en_US) |
| * en-US => en_US |
| * en_US => en_US |
| * en-us => en_us |
| * * java |
| * like default, but the second part will be always in uppercase |
| * en-US => en_US |
| * en_US => en_US |
| * en-us => en_US |
| * * BCP 47 (RFC 4646 & 4647) |
| * en-US => en-US |
| * en_US => en-US |
| * en-us => en-US |
| * |
| * See also: |
| * * http://en.wikipedia.org/wiki/IETF_language_tag |
| * * http://www.w3.org/International/core/langtags/ |
| * * http://tools.ietf.org/html/bcp47 |
| * |
| * @param {string|object} options - options (or standard) |
| * @param {string} options.standard - valid values are 'default', 'bcp47', 'java' |
| */ |
| this.uniformLanguageTag = function (options) { |
| |
| if (!options) { |
| options = {}; |
| } else if (angular.isString(options)) { |
| options = { |
| standard : options |
| }; |
| } |
| |
| uniformLanguageTagResolver = options.standard; |
| |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#determinePreferredLanguage |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Tells angular-translate to try to determine on its own which language key |
| * to set as preferred language. When `fn` is given, angular-translate uses it |
| * to determine a language key, otherwise it uses the built-in `getLocale()` |
| * method. |
| * |
| * The `getLocale()` returns a language key in the format `[lang]_[country]` or |
| * `[lang]` depending on what the browser provides. |
| * |
| * Use this method at your own risk, since not all browsers return a valid |
| * locale (see {@link pascalprecht.translate.$translateProvider#methods_uniformLanguageTag uniformLanguageTag()}). |
| * |
| * @param {Function=} fn Function to determine a browser's locale |
| */ |
| this.determinePreferredLanguage = function (fn) { |
| |
| var locale = (fn && angular.isFunction(fn)) ? fn() : getLocale(); |
| |
| if (!$availableLanguageKeys.length) { |
| $preferredLanguage = locale; |
| } else { |
| $preferredLanguage = negotiateLocale(locale) || locale; |
| } |
| |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#registerAvailableLanguageKeys |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Registers a set of language keys the app will work with. Use this method in |
| * combination with |
| * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}. |
| * When available languages keys are registered, angular-translate |
| * tries to find the best fitting language key depending on the browsers locale, |
| * considering your language key convention. |
| * |
| * @param {object} languageKeys Array of language keys the your app will use |
| * @param {object=} aliases Alias map. |
| */ |
| this.registerAvailableLanguageKeys = function (languageKeys, aliases) { |
| if (languageKeys) { |
| $availableLanguageKeys = languageKeys; |
| if (aliases) { |
| $languageKeyAliases = aliases; |
| } |
| return this; |
| } |
| 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.$translationCache $translationCache}. |
| * 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 function |
| * @name pascalprecht.translate.$translateProvider#directivePriority |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Sets the default priority of the translate directive. The standard value is `0`. |
| * Calling this function without an argument will return the current value. |
| * |
| * @param {number} priority for the translate-directive |
| */ |
| this.directivePriority = function (priority) { |
| if (priority === undefined) { |
| // getter |
| return directivePriority; |
| } else { |
| // setter with chaining |
| directivePriority = priority; |
| return this; |
| } |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#statefulFilter |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * Since AngularJS 1.3, filters which are not stateless (depending at the scope) |
| * have to explicit define this behavior. |
| * Sets whether the translate filter should be stateful or stateless. The standard value is `true` |
| * meaning being stateful. |
| * Calling this function without an argument will return the current value. |
| * |
| * @param {boolean} state - defines the state of the filter |
| */ |
| this.statefulFilter = function (state) { |
| if (state === undefined) { |
| // getter |
| return statefulFilter; |
| } else { |
| // setter with chaining |
| statefulFilter = state; |
| return this; |
| } |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#postProcess |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * The post processor will be intercept right after the translation result. It can modify the result. |
| * |
| * @param {object} fn Function or service name (string) to be called after the translation value has been set / resolved. The function itself will enrich every value being processed and then continue the normal resolver process |
| */ |
| this.postProcess = function (fn) { |
| if (fn) { |
| postProcessFn = fn; |
| } else { |
| postProcessFn = undefined; |
| } |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateProvider#keepContent |
| * @methodOf pascalprecht.translate.$translateProvider |
| * |
| * @description |
| * If keepContent is set to true than translate directive will always use innerHTML |
| * as a default translation |
| * |
| * Example: |
| * <pre> |
| * app.config(function ($translateProvider) { |
| * $translateProvider.keepContent(true); |
| * }); |
| * </pre> |
| * |
| * @param {boolean} value - valid values are true or false |
| */ |
| this.keepContent = function (value) { |
| $keepContent = !(!value); |
| return this; |
| }; |
| |
| /** |
| * @ngdoc object |
| * @name pascalprecht.translate.$translate |
| * @requires $interpolate |
| * @requires $log |
| * @requires $rootScope |
| * @requires $q |
| * |
| * @description |
| * The `$translate` service is the actual core of angular-translate. It expects a translation id |
| * and optional interpolate parameters to translate contents. |
| * |
| * <pre> |
| * $translate('HEADLINE_TEXT').then(function (translation) { |
| * $scope.translatedText = translation; |
| * }); |
| * </pre> |
| * |
| * @param {string|array} translationId A token which represents a translation id |
| * This can be optionally an array of translation ids which |
| * results that the function returns an object where each key |
| * is the translation id and the value the translation. |
| * @param {object=} interpolateParams An object hash for dynamic values |
| * @param {string} interpolationId The id of the interpolation to use |
| * @param {string} defaultTranslationText the optional default translation text that is written as |
| * as default text in case it is not found in any configured language |
| * @param {string} forceLanguage A language to be used instead of the current language |
| * @returns {object} promise |
| */ |
| this.$get = ['$log', '$injector', '$rootScope', '$q', function ($log, $injector, $rootScope, $q) { |
| |
| var Storage, |
| defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'), |
| pendingLoader = false, |
| interpolatorHashMap = {}, |
| langPromises = {}, |
| fallbackIndex, |
| startFallbackIteration; |
| |
| var $translate = function (translationId, interpolateParams, interpolationId, defaultTranslationText, forceLanguage) { |
| if (!$uses && $preferredLanguage) { |
| $uses = $preferredLanguage; |
| } |
| var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses |
| (negotiateLocale(forceLanguage) || forceLanguage) : $uses; |
| |
| // Check forceLanguage is present |
| if (forceLanguage) { |
| loadTranslationsIfMissing(forceLanguage); |
| } |
| |
| // Duck detection: If the first argument is an array, a bunch of translations was requested. |
| // The result is an object. |
| if (angular.isArray(translationId)) { |
| // Inspired by Q.allSettled by Kris Kowal |
| // https://github.com/kriskowal/q/blob/b0fa72980717dc202ffc3cbf03b936e10ebbb9d7/q.js#L1553-1563 |
| // This transforms all promises regardless resolved or rejected |
| var translateAll = function (translationIds) { |
| var results = {}; // storing the actual results |
| var promises = []; // promises to wait for |
| // Wraps the promise a) being always resolved and b) storing the link id->value |
| var translate = function (translationId) { |
| var deferred = $q.defer(); |
| var regardless = function (value) { |
| results[translationId] = value; |
| deferred.resolve([translationId, value]); |
| }; |
| // we don't care whether the promise was resolved or rejected; just store the values |
| $translate(translationId, interpolateParams, interpolationId, defaultTranslationText, forceLanguage).then(regardless, regardless); |
| return deferred.promise; |
| }; |
| for (var i = 0, c = translationIds.length; i < c; i++) { |
| promises.push(translate(translationIds[i])); |
| } |
| // wait for all (including storing to results) |
| return $q.all(promises).then(function () { |
| // return the results |
| return results; |
| }); |
| }; |
| return translateAll(translationId); |
| } |
| |
| var deferred = $q.defer(); |
| |
| // trim off any whitespace |
| if (translationId) { |
| translationId = trim.apply(translationId); |
| } |
| |
| var promiseToWaitFor = (function () { |
| var promise = $preferredLanguage ? |
| langPromises[$preferredLanguage] : |
| langPromises[uses]; |
| |
| fallbackIndex = 0; |
| |
| if ($storageFactory && !promise) { |
| // looks like there's no pending promise for $preferredLanguage or |
| // $uses. Maybe there's one pending for a language that comes from |
| // storage. |
| var langKey = Storage.get($storageKey); |
| promise = langPromises[langKey]; |
| |
| if ($fallbackLanguage && $fallbackLanguage.length) { |
| var index = indexOf($fallbackLanguage, langKey); |
| // maybe the language from storage is also defined as fallback language |
| // we increase the fallback language index to not search in that language |
| // as fallback, since it's probably the first used language |
| // in that case the index starts after the first element |
| fallbackIndex = (index === 0) ? 1 : 0; |
| |
| // but we can make sure to ALWAYS fallback to preferred language at least |
| if (indexOf($fallbackLanguage, $preferredLanguage) < 0) { |
| $fallbackLanguage.push($preferredLanguage); |
| } |
| } |
| } |
| return promise; |
| }()); |
| |
| if (!promiseToWaitFor) { |
| // no promise to wait for? okay. Then there's no loader registered |
| // nor is a one pending for language that comes from storage. |
| // We can just translate. |
| determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText, uses).then(deferred.resolve, deferred.reject); |
| } else { |
| var promiseResolved = function () { |
| // $uses may have changed while waiting |
| if (!forceLanguage) { |
| uses = $uses; |
| } |
| determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText, uses).then(deferred.resolve, deferred.reject); |
| }; |
| promiseResolved.displayName = 'promiseResolved'; |
| |
| promiseToWaitFor['finally'](promiseResolved); |
| } |
| return deferred.promise; |
| }; |
| |
| /** |
| * @name applyNotFoundIndicators |
| * @private |
| * |
| * @description |
| * Applies not fount indicators to given translation id, if needed. |
| * This function gets only executed, if a translation id doesn't exist, |
| * which is why a translation id is expected as argument. |
| * |
| * @param {string} translationId Translation id. |
| * @returns {string} Same as given translation id but applied with not found |
| * indicators. |
| */ |
| var applyNotFoundIndicators = function (translationId) { |
| // applying notFoundIndicators |
| if ($notFoundIndicatorLeft) { |
| translationId = [$notFoundIndicatorLeft, translationId].join(' '); |
| } |
| if ($notFoundIndicatorRight) { |
| translationId = [translationId, $notFoundIndicatorRight].join(' '); |
| } |
| return translationId; |
| }; |
| |
| /** |
| * @name useLanguage |
| * @private |
| * |
| * @description |
| * Makes actual use of a language by setting a given language key as used |
| * language and informs registered interpolators to also use the given |
| * key as locale. |
| * |
| * @param {string} key Locale key. |
| */ |
| var useLanguage = function (key) { |
| $uses = key; |
| |
| // make sure to store new language key before triggering success event |
| if ($storageFactory) { |
| Storage.put($translate.storageKey(), $uses); |
| } |
| |
| $rootScope.$emit('$translateChangeSuccess', {language : key}); |
| |
| // inform default interpolator |
| defaultInterpolator.setLocale($uses); |
| |
| var eachInterpolator = function (interpolator, id) { |
| interpolatorHashMap[id].setLocale($uses); |
| }; |
| eachInterpolator.displayName = 'eachInterpolatorLocaleSetter'; |
| |
| // inform all others too! |
| angular.forEach(interpolatorHashMap, eachInterpolator); |
| $rootScope.$emit('$translateChangeEnd', {language : key}); |
| }; |
| |
| /** |
| * @name loadAsync |
| * @private |
| * |
| * @description |
| * Kicks of registered async loader using `$injector` and applies existing |
| * loader options. When resolved, it updates translation tables accordingly |
| * or rejects with given language key. |
| * |
| * @param {string} key Language key. |
| * @return {Promise} A promise. |
| */ |
| var loadAsync = function (key) { |
| if (!key) { |
| throw 'No language key specified for loading.'; |
| } |
| |
| var deferred = $q.defer(); |
| |
| $rootScope.$emit('$translateLoadingStart', {language : key}); |
| pendingLoader = true; |
| |
| var cache = loaderCache; |
| if (typeof(cache) === 'string') { |
| // getting on-demand instance of loader |
| cache = $injector.get(cache); |
| } |
| |
| var loaderOptions = angular.extend({}, $loaderOptions, { |
| key : key, |
| $http : angular.extend({}, { |
| cache : cache |
| }, $loaderOptions.$http) |
| }); |
| |
| var onLoaderSuccess = function (data) { |
| var translationTable = {}; |
| $rootScope.$emit('$translateLoadingSuccess', {language : key}); |
| |
| if (angular.isArray(data)) { |
| angular.forEach(data, function (table) { |
| angular.extend(translationTable, flatObject(table)); |
| }); |
| } else { |
| angular.extend(translationTable, flatObject(data)); |
| } |
| pendingLoader = false; |
| deferred.resolve({ |
| key : key, |
| table : translationTable |
| }); |
| $rootScope.$emit('$translateLoadingEnd', {language : key}); |
| }; |
| onLoaderSuccess.displayName = 'onLoaderSuccess'; |
| |
| var onLoaderError = function (key) { |
| $rootScope.$emit('$translateLoadingError', {language : key}); |
| deferred.reject(key); |
| $rootScope.$emit('$translateLoadingEnd', {language : key}); |
| }; |
| onLoaderError.displayName = 'onLoaderError'; |
| |
| $injector.get($loaderFactory)(loaderOptions) |
| .then(onLoaderSuccess, onLoaderError); |
| |
| return deferred.promise; |
| }; |
| |
| if ($storageFactory) { |
| Storage = $injector.get($storageFactory); |
| |
| if (!Storage.get || !Storage.put) { |
| throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or put() method!'); |
| } |
| } |
| |
| // if we have additional interpolations that were added via |
| // $translateProvider.addInterpolation(), we have to map'em |
| if ($interpolatorFactories.length) { |
| var eachInterpolationFactory = function (interpolatorFactory) { |
| var interpolator = $injector.get(interpolatorFactory); |
| // setting initial locale for each interpolation service |
| interpolator.setLocale($preferredLanguage || $uses); |
| // make'em recognizable through id |
| interpolatorHashMap[interpolator.getInterpolationIdentifier()] = interpolator; |
| }; |
| eachInterpolationFactory.displayName = 'interpolationFactoryAdder'; |
| |
| angular.forEach($interpolatorFactories, eachInterpolationFactory); |
| } |
| |
| /** |
| * @name getTranslationTable |
| * @private |
| * |
| * @description |
| * Returns a promise that resolves to the translation table |
| * or is rejected if an error occurred. |
| * |
| * @param langKey |
| * @returns {Q.promise} |
| */ |
| var getTranslationTable = function (langKey) { |
| var deferred = $q.defer(); |
| if (Object.prototype.hasOwnProperty.call($translationTable, langKey)) { |
| deferred.resolve($translationTable[langKey]); |
| } else if (langPromises[langKey]) { |
| var onResolve = function (data) { |
| translations(data.key, data.table); |
| deferred.resolve(data.table); |
| }; |
| onResolve.displayName = 'translationTableResolver'; |
| langPromises[langKey].then(onResolve, deferred.reject); |
| } else { |
| deferred.reject(); |
| } |
| return deferred.promise; |
| }; |
| |
| /** |
| * @name getFallbackTranslation |
| * @private |
| * |
| * @description |
| * Returns a promise that will resolve to the translation |
| * or be rejected if no translation was found for the language. |
| * This function is currently only used for fallback language translation. |
| * |
| * @param langKey The language to translate to. |
| * @param translationId |
| * @param interpolateParams |
| * @param Interpolator |
| * @returns {Q.promise} |
| */ |
| var getFallbackTranslation = function (langKey, translationId, interpolateParams, Interpolator) { |
| var deferred = $q.defer(); |
| |
| var onResolve = function (translationTable) { |
| if (Object.prototype.hasOwnProperty.call(translationTable, translationId)) { |
| Interpolator.setLocale(langKey); |
| var translation = translationTable[translationId]; |
| if (translation.substr(0, 2) === '@:') { |
| getFallbackTranslation(langKey, translation.substr(2), interpolateParams, Interpolator) |
| .then(deferred.resolve, deferred.reject); |
| } else { |
| var interpolatedValue = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'service'); |
| interpolatedValue = applyPostProcessing(translationId, translationTable[translationId], interpolatedValue, interpolateParams, langKey); |
| |
| deferred.resolve(interpolatedValue); |
| |
| } |
| Interpolator.setLocale($uses); |
| } else { |
| deferred.reject(); |
| } |
| }; |
| onResolve.displayName = 'fallbackTranslationResolver'; |
| |
| getTranslationTable(langKey).then(onResolve, deferred.reject); |
| |
| return deferred.promise; |
| }; |
| |
| /** |
| * @name getFallbackTranslationInstant |
| * @private |
| * |
| * @description |
| * Returns a translation |
| * This function is currently only used for fallback language translation. |
| * |
| * @param langKey The language to translate to. |
| * @param translationId |
| * @param interpolateParams |
| * @param Interpolator |
| * @param sanitizeStrategy sanitize strategy override |
| * |
| * @returns {string} translation |
| */ |
| var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy) { |
| var result, translationTable = $translationTable[langKey]; |
| |
| if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId)) { |
| Interpolator.setLocale(langKey); |
| result = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'filter', sanitizeStrategy); |
| result = applyPostProcessing(translationId, translationTable[translationId], result, interpolateParams, langKey, sanitizeStrategy); |
| // workaround for TrustedValueHolderType |
| if (!angular.isString(result) && angular.isFunction(result.$$unwrapTrustedValue)) { |
| var result2 = result.$$unwrapTrustedValue(); |
| if (result2.substr(0, 2) === '@:') { |
| return getFallbackTranslationInstant(langKey, result2.substr(2), interpolateParams, Interpolator, sanitizeStrategy); |
| } |
| } else if (result.substr(0, 2) === '@:') { |
| return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator, sanitizeStrategy); |
| } |
| Interpolator.setLocale($uses); |
| } |
| |
| return result; |
| }; |
| |
| |
| /** |
| * @name translateByHandler |
| * @private |
| * |
| * Translate by missing translation handler. |
| * |
| * @param translationId |
| * @param interpolateParams |
| * @param defaultTranslationText |
| * @param sanitizeStrategy sanitize strategy override |
| * |
| * @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is |
| * absent |
| */ |
| var translateByHandler = function (translationId, interpolateParams, defaultTranslationText, sanitizeStrategy) { |
| // If we have a handler factory - we might also call it here to determine if it provides |
| // a default text for a translationid that can't be found anywhere in our tables |
| if ($missingTranslationHandlerFactory) { |
| return $injector.get($missingTranslationHandlerFactory)(translationId, $uses, interpolateParams, defaultTranslationText, sanitizeStrategy); |
| } else { |
| return translationId; |
| } |
| }; |
| |
| /** |
| * @name resolveForFallbackLanguage |
| * @private |
| * |
| * Recursive helper function for fallbackTranslation that will sequentially look |
| * for a translation in the fallbackLanguages starting with fallbackLanguageIndex. |
| * |
| * @param fallbackLanguageIndex |
| * @param translationId |
| * @param interpolateParams |
| * @param Interpolator |
| * @returns {Q.promise} Promise that will resolve to the translation. |
| */ |
| var resolveForFallbackLanguage = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, defaultTranslationText) { |
| var deferred = $q.defer(); |
| |
| if (fallbackLanguageIndex < $fallbackLanguage.length) { |
| var langKey = $fallbackLanguage[fallbackLanguageIndex]; |
| getFallbackTranslation(langKey, translationId, interpolateParams, Interpolator).then( |
| function (data) { |
| deferred.resolve(data); |
| }, |
| function () { |
| // Look in the next fallback language for a translation. |
| // It delays the resolving by passing another promise to resolve. |
| return resolveForFallbackLanguage(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator, defaultTranslationText).then(deferred.resolve, deferred.reject); |
| } |
| ); |
| } else { |
| // No translation found in any fallback language |
| // if a default translation text is set in the directive, then return this as a result |
| if (defaultTranslationText) { |
| deferred.resolve(defaultTranslationText); |
| } else { |
| var missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, defaultTranslationText); |
| |
| // if no default translation is set and an error handler is defined, send it to the handler |
| // and then return the result if it isn't undefined |
| if ($missingTranslationHandlerFactory && missingTranslationHandlerTranslation) { |
| deferred.resolve(missingTranslationHandlerTranslation); |
| } else { |
| deferred.reject(applyNotFoundIndicators(translationId)); |
| } |
| } |
| } |
| return deferred.promise; |
| }; |
| |
| /** |
| * @name resolveForFallbackLanguageInstant |
| * @private |
| * |
| * Recursive helper function for fallbackTranslation that will sequentially look |
| * for a translation in the fallbackLanguages starting with fallbackLanguageIndex. |
| * |
| * @param fallbackLanguageIndex |
| * @param translationId |
| * @param interpolateParams |
| * @param Interpolator |
| * @param sanitizeStrategy |
| * @returns {string} translation |
| */ |
| var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, sanitizeStrategy) { |
| var result; |
| |
| if (fallbackLanguageIndex < $fallbackLanguage.length) { |
| var langKey = $fallbackLanguage[fallbackLanguageIndex]; |
| result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy); |
| if (!result && result !== '') { |
| result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator); |
| } |
| } |
| return result; |
| }; |
| |
| /** |
| * Translates with the usage of the fallback languages. |
| * |
| * @param translationId |
| * @param interpolateParams |
| * @param Interpolator |
| * @returns {Q.promise} Promise, that resolves to the translation. |
| */ |
| var fallbackTranslation = function (translationId, interpolateParams, Interpolator, defaultTranslationText) { |
| // Start with the fallbackLanguage with index 0 |
| return resolveForFallbackLanguage((startFallbackIteration > 0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, defaultTranslationText); |
| }; |
| |
| /** |
| * Translates with the usage of the fallback languages. |
| * |
| * @param translationId |
| * @param interpolateParams |
| * @param Interpolator |
| * @returns {String} translation |
| */ |
| var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator, sanitizeStrategy) { |
| // Start with the fallbackLanguage with index 0 |
| return resolveForFallbackLanguageInstant((startFallbackIteration > 0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, sanitizeStrategy); |
| }; |
| |
| var determineTranslation = function (translationId, interpolateParams, interpolationId, defaultTranslationText, uses) { |
| |
| var deferred = $q.defer(); |
| |
| var table = uses ? $translationTable[uses] : $translationTable, |
| Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator; |
| |
| // if the translation id exists, we can just interpolate it |
| if (table && Object.prototype.hasOwnProperty.call(table, translationId)) { |
| var translation = table[translationId]; |
| |
| // If using link, rerun $translate with linked translationId and return it |
| if (translation.substr(0, 2) === '@:') { |
| |
| $translate(translation.substr(2), interpolateParams, interpolationId, defaultTranslationText, uses) |
| .then(deferred.resolve, deferred.reject); |
| } else { |
| // |
| var resolvedTranslation = Interpolator.interpolate(translation, interpolateParams, 'service'); |
| resolvedTranslation = applyPostProcessing(translationId, translation, resolvedTranslation, interpolateParams, uses); |
| deferred.resolve(resolvedTranslation); |
| } |
| } else { |
| var missingTranslationHandlerTranslation; |
| // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise |
| if ($missingTranslationHandlerFactory && !pendingLoader) { |
| missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, defaultTranslationText); |
| } |
| |
| // since we couldn't translate the inital requested translation id, |
| // we try it now with one or more fallback languages, if fallback language(s) is |
| // configured. |
| if (uses && $fallbackLanguage && $fallbackLanguage.length) { |
| fallbackTranslation(translationId, interpolateParams, Interpolator, defaultTranslationText) |
| .then(function (translation) { |
| deferred.resolve(translation); |
| }, function (_translationId) { |
| deferred.reject(applyNotFoundIndicators(_translationId)); |
| }); |
| } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) { |
| // 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 (defaultTranslationText) { |
| deferred.resolve(defaultTranslationText); |
| } else { |
| deferred.resolve(missingTranslationHandlerTranslation); |
| } |
| } else { |
| if (defaultTranslationText) { |
| deferred.resolve(defaultTranslationText); |
| } else { |
| deferred.reject(applyNotFoundIndicators(translationId)); |
| } |
| } |
| } |
| return deferred.promise; |
| }; |
| |
| var determineTranslationInstant = function (translationId, interpolateParams, interpolationId, uses, sanitizeStrategy) { |
| |
| var result, table = uses ? $translationTable[uses] : $translationTable, |
| Interpolator = defaultInterpolator; |
| |
| // if the interpolation id exists use custom interpolator |
| if (interpolatorHashMap && Object.prototype.hasOwnProperty.call(interpolatorHashMap, interpolationId)) { |
| Interpolator = interpolatorHashMap[interpolationId]; |
| } |
| |
| // if the translation id exists, we can just interpolate it |
| if (table && Object.prototype.hasOwnProperty.call(table, translationId)) { |
| var translation = table[translationId]; |
| |
| // If using link, rerun $translate with linked translationId and return it |
| if (translation.substr(0, 2) === '@:') { |
| result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId, uses, sanitizeStrategy); |
| } else { |
| result = Interpolator.interpolate(translation, interpolateParams, 'filter', sanitizeStrategy); |
| result = applyPostProcessing(translationId, translation, result, interpolateParams, uses, sanitizeStrategy); |
| } |
| } else { |
| var missingTranslationHandlerTranslation; |
| // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise |
| if ($missingTranslationHandlerFactory && !pendingLoader) { |
| missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy); |
| } |
| |
| // since we couldn't translate the inital requested translation id, |
| // we try it now with one or more fallback languages, if fallback language(s) is |
| // configured. |
| if (uses && $fallbackLanguage && $fallbackLanguage.length) { |
| fallbackIndex = 0; |
| result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator, sanitizeStrategy); |
| } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) { |
| // 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 |
| result = missingTranslationHandlerTranslation; |
| } else { |
| result = applyNotFoundIndicators(translationId); |
| } |
| } |
| |
| return result; |
| }; |
| |
| var clearNextLangAndPromise = function (key) { |
| if ($nextLang === key) { |
| $nextLang = undefined; |
| } |
| langPromises[key] = undefined; |
| }; |
| |
| var applyPostProcessing = function (translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy) { |
| var fn = postProcessFn; |
| |
| if (fn) { |
| |
| if (typeof(fn) === 'string') { |
| // getting on-demand instance |
| fn = $injector.get(fn); |
| } |
| if (fn) { |
| return fn(translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy); |
| } |
| } |
| |
| return resolvedTranslation; |
| }; |
| |
| var loadTranslationsIfMissing = function (key) { |
| if (!$translationTable[key] && $loaderFactory && !langPromises[key]) { |
| langPromises[key] = loadAsync(key).then(function (translation) { |
| translations(translation.key, translation.table); |
| return translation; |
| }); |
| } |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#preferredLanguage |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns the language key for the preferred language. |
| * |
| * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime) |
| * |
| * @return {string} preferred language key |
| */ |
| $translate.preferredLanguage = function (langKey) { |
| if (langKey) { |
| setupPreferredLanguage(langKey); |
| } |
| return $preferredLanguage; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#cloakClassName |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns the configured class name for `translate-cloak` directive. |
| * |
| * @return {string} cloakClassName |
| */ |
| $translate.cloakClassName = function () { |
| return $cloakClassName; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#nestedObjectDelimeter |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns the configured delimiter for nested namespaces. |
| * |
| * @return {string} nestedObjectDelimeter |
| */ |
| $translate.nestedObjectDelimeter = function () { |
| return $nestedObjectDelimeter; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#fallbackLanguage |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns the language key for the fallback languages or sets a new fallback stack. |
| * |
| * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime) |
| * |
| * @return {string||array} fallback language key |
| */ |
| $translate.fallbackLanguage = function (langKey) { |
| if (langKey !== undefined && langKey !== null) { |
| fallbackStack(langKey); |
| |
| // as we might have an async loader initiated and a new translation language might have been defined |
| // we need to add the promise to the stack also. So - iterate. |
| if ($loaderFactory) { |
| if ($fallbackLanguage && $fallbackLanguage.length) { |
| for (var i = 0, len = $fallbackLanguage.length; i < len; i++) { |
| if (!langPromises[$fallbackLanguage[i]]) { |
| langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]); |
| } |
| } |
| } |
| } |
| $translate.use($translate.use()); |
| } |
| if ($fallbackWasString) { |
| return $fallbackLanguage[0]; |
| } else { |
| return $fallbackLanguage; |
| } |
| |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#useFallbackLanguage |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Sets the first key of the fallback language stack to be used for translation. |
| * Therefore all languages in the fallback array BEFORE this key will be skipped! |
| * |
| * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to |
| * get back to the whole stack |
| */ |
| $translate.useFallbackLanguage = function (langKey) { |
| if (langKey !== undefined && langKey !== null) { |
| if (!langKey) { |
| startFallbackIteration = 0; |
| } else { |
| var langKeyPosition = indexOf($fallbackLanguage, langKey); |
| if (langKeyPosition > -1) { |
| startFallbackIteration = langKeyPosition; |
| } |
| } |
| |
| } |
| |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#proposedLanguage |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns the language key of language that is currently loaded asynchronously. |
| * |
| * @return {string} language key |
| */ |
| $translate.proposedLanguage = function () { |
| return $nextLang; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#storage |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns registered storage. |
| * |
| * @return {object} Storage |
| */ |
| $translate.storage = function () { |
| return Storage; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#negotiateLocale |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns a language key based on available languages and language aliases. If a |
| * language key cannot be resolved, returns undefined. |
| * |
| * If no or a falsy key is given, returns undefined. |
| * |
| * @param {string} [key] Language key |
| * @return {string|undefined} Language key or undefined if no language key is found. |
| */ |
| $translate.negotiateLocale = negotiateLocale; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#use |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Tells angular-translate which language to use by given language key. This method is |
| * used to change language at runtime. It also takes care of storing the language |
| * key in a configured store to let your app remember the choosed language. |
| * |
| * When trying to 'use' a language which isn't available it tries to load it |
| * asynchronously with registered loaders. |
| * |
| * Returns promise object with loaded language file data or string of the currently used language. |
| * |
| * If no or a falsy key is given it returns the currently used language key. |
| * The returned string will be ```undefined``` if setting up $translate hasn't finished. |
| * @example |
| * $translate.use("en_US").then(function(data){ |
| * $scope.text = $translate("HELLO"); |
| * }); |
| * |
| * @param {string} [key] Language key |
| * @return {object|string} Promise with loaded language data or the language key if a falsy param was given. |
| */ |
| $translate.use = function (key) { |
| if (!key) { |
| return $uses; |
| } |
| |
| var deferred = $q.defer(); |
| |
| $rootScope.$emit('$translateChangeStart', {language : key}); |
| |
| // Try to get the aliased language key |
| var aliasedKey = negotiateLocale(key); |
| // Ensure only registered language keys will be loaded |
| if ($availableLanguageKeys.length > 0 && !aliasedKey) { |
| return $q.reject(key); |
| } |
| |
| if (aliasedKey) { |
| key = aliasedKey; |
| } |
| |
| // if there isn't a translation table for the language we've requested, |
| // we load it asynchronously |
| $nextLang = key; |
| if (($forceAsyncReloadEnabled || !$translationTable[key]) && $loaderFactory && !langPromises[key]) { |
| langPromises[key] = loadAsync(key).then(function (translation) { |
| translations(translation.key, translation.table); |
| deferred.resolve(translation.key); |
| if ($nextLang === key) { |
| useLanguage(translation.key); |
| } |
| return translation; |
| }, function (key) { |
| $rootScope.$emit('$translateChangeError', {language : key}); |
| deferred.reject(key); |
| $rootScope.$emit('$translateChangeEnd', {language : key}); |
| return $q.reject(key); |
| }); |
| langPromises[key]['finally'](function () { |
| clearNextLangAndPromise(key); |
| }); |
| } else if (langPromises[key]) { |
| // we are already loading this asynchronously |
| // resolve our new deferred when the old langPromise is resolved |
| langPromises[key].then(function (translation) { |
| if ($nextLang === translation.key) { |
| useLanguage(translation.key); |
| } |
| deferred.resolve(translation.key); |
| return translation; |
| }, function (key) { |
| // find first available fallback language if that request has failed |
| if (!$uses && $fallbackLanguage && $fallbackLanguage.length > 0 && $fallbackLanguage[0] !== key) { |
| return $translate.use($fallbackLanguage[0]).then(deferred.resolve, deferred.reject); |
| } else { |
| return deferred.reject(key); |
| } |
| }); |
| } else { |
| deferred.resolve(key); |
| useLanguage(key); |
| } |
| |
| return deferred.promise; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#resolveClientLocale |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * This returns the current browser/client's language key. The result is processed with the configured uniform tag resolver. |
| * |
| * @returns {string} the current client/browser language key |
| */ |
| $translate.resolveClientLocale = function () { |
| return getLocale(); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#storageKey |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns the key for the storage. |
| * |
| * @return {string} storage key |
| */ |
| $translate.storageKey = function () { |
| return storageKey(); |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#isPostCompilingEnabled |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns whether post compiling is enabled or not |
| * |
| * @return {bool} storage key |
| */ |
| $translate.isPostCompilingEnabled = function () { |
| return $postCompilingEnabled; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#isForceAsyncReloadEnabled |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns whether force async reload is enabled or not |
| * |
| * @return {boolean} forceAsyncReload value |
| */ |
| $translate.isForceAsyncReloadEnabled = function () { |
| return $forceAsyncReloadEnabled; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#isKeepContent |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns whether keepContent or not |
| * |
| * @return {boolean} keepContent value |
| */ |
| $translate.isKeepContent = function () { |
| return $keepContent; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#refresh |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Refreshes a translation table pointed by the given langKey. If langKey is not specified, |
| * the module will drop all existent translation tables and load new version of those which |
| * are currently in use. |
| * |
| * Refresh means that the module will drop target translation table and try to load it again. |
| * |
| * In case there are no loaders registered the refresh() method will throw an Error. |
| * |
| * If the module is able to refresh translation tables refresh() method will broadcast |
| * $translateRefreshStart and $translateRefreshEnd events. |
| * |
| * @example |
| * // this will drop all currently existent translation tables and reload those which are |
| * // currently in use |
| * $translate.refresh(); |
| * // this will refresh a translation table for the en_US language |
| * $translate.refresh('en_US'); |
| * |
| * @param {string} langKey A language key of the table, which has to be refreshed |
| * |
| * @return {promise} Promise, which will be resolved in case a translation tables refreshing |
| * process is finished successfully, and reject if not. |
| */ |
| $translate.refresh = function (langKey) { |
| if (!$loaderFactory) { |
| throw new Error('Couldn\'t refresh translation table, no loader registered!'); |
| } |
| |
| var deferred = $q.defer(); |
| |
| function resolve() { |
| deferred.resolve(); |
| $rootScope.$emit('$translateRefreshEnd', {language : langKey}); |
| } |
| |
| function reject() { |
| deferred.reject(); |
| $rootScope.$emit('$translateRefreshEnd', {language : langKey}); |
| } |
| |
| $rootScope.$emit('$translateRefreshStart', {language : langKey}); |
| |
| if (!langKey) { |
| // if there's no language key specified we refresh ALL THE THINGS! |
| var tables = [], loadingKeys = {}; |
| |
| // reload registered fallback languages |
| if ($fallbackLanguage && $fallbackLanguage.length) { |
| for (var i = 0, len = $fallbackLanguage.length; i < len; i++) { |
| tables.push(loadAsync($fallbackLanguage[i])); |
| loadingKeys[$fallbackLanguage[i]] = true; |
| } |
| } |
| |
| // reload currently used language |
| if ($uses && !loadingKeys[$uses]) { |
| tables.push(loadAsync($uses)); |
| } |
| |
| var allTranslationsLoaded = function (tableData) { |
| $translationTable = {}; |
| angular.forEach(tableData, function (data) { |
| translations(data.key, data.table); |
| }); |
| if ($uses) { |
| useLanguage($uses); |
| } |
| resolve(); |
| }; |
| allTranslationsLoaded.displayName = 'refreshPostProcessor'; |
| |
| $q.all(tables).then(allTranslationsLoaded, reject); |
| |
| } else if ($translationTable[langKey]) { |
| |
| var oneTranslationsLoaded = function (data) { |
| translations(data.key, data.table); |
| if (langKey === $uses) { |
| useLanguage($uses); |
| } |
| resolve(); |
| return data; |
| }; |
| oneTranslationsLoaded.displayName = 'refreshPostProcessor'; |
| |
| loadAsync(langKey).then(oneTranslationsLoaded, reject); |
| |
| } else { |
| reject(); |
| } |
| return deferred.promise; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#instant |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns a translation instantly from the internal state of loaded translation. All rules |
| * regarding the current language, the preferred language of even fallback languages will be |
| * used except any promise handling. If a language was not found, an asynchronous loading |
| * will be invoked in the background. |
| * |
| * @param {string|array} translationId A token which represents a translation id |
| * This can be optionally an array of translation ids which |
| * results that the function's promise returns an object where |
| * each key is the translation id and the value the translation. |
| * @param {object} interpolateParams Params |
| * @param {string} interpolationId The id of the interpolation to use |
| * @param {string} forceLanguage A language to be used instead of the current language |
| * @param {string} sanitizeStrategy force sanitize strategy for this call instead of using the configured one |
| * |
| * @return {string|object} translation |
| */ |
| $translate.instant = function (translationId, interpolateParams, interpolationId, forceLanguage, sanitizeStrategy) { |
| |
| // we don't want to re-negotiate $uses |
| var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses |
| (negotiateLocale(forceLanguage) || forceLanguage) : $uses; |
| |
| // Detect undefined and null values to shorten the execution and prevent exceptions |
| if (translationId === null || angular.isUndefined(translationId)) { |
| return translationId; |
| } |
| |
| // Check forceLanguage is present |
| if (forceLanguage) { |
| loadTranslationsIfMissing(forceLanguage); |
| } |
| |
| // Duck detection: If the first argument is an array, a bunch of translations was requested. |
| // The result is an object. |
| if (angular.isArray(translationId)) { |
| var results = {}; |
| for (var i = 0, c = translationId.length; i < c; i++) { |
| results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId, forceLanguage, sanitizeStrategy); |
| } |
| return results; |
| } |
| |
| // We discarded unacceptable values. So we just need to verify if translationId is empty String |
| if (angular.isString(translationId) && translationId.length < 1) { |
| return translationId; |
| } |
| |
| // trim off any whitespace |
| if (translationId) { |
| translationId = trim.apply(translationId); |
| } |
| |
| var result, possibleLangKeys = []; |
| if ($preferredLanguage) { |
| possibleLangKeys.push($preferredLanguage); |
| } |
| if (uses) { |
| possibleLangKeys.push(uses); |
| } |
| if ($fallbackLanguage && $fallbackLanguage.length) { |
| possibleLangKeys = possibleLangKeys.concat($fallbackLanguage); |
| } |
| for (var j = 0, d = possibleLangKeys.length; j < d; j++) { |
| var possibleLangKey = possibleLangKeys[j]; |
| if ($translationTable[possibleLangKey]) { |
| if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') { |
| result = determineTranslationInstant(translationId, interpolateParams, interpolationId, uses, sanitizeStrategy); |
| } |
| } |
| if (typeof result !== 'undefined') { |
| break; |
| } |
| } |
| |
| if (!result && result !== '') { |
| if ($notFoundIndicatorLeft || $notFoundIndicatorRight) { |
| result = applyNotFoundIndicators(translationId); |
| } else { |
| // Return translation of default interpolator if not found anything. |
| result = defaultInterpolator.interpolate(translationId, interpolateParams, 'filter', sanitizeStrategy); |
| |
| // 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 |
| var missingTranslationHandlerTranslation; |
| if ($missingTranslationHandlerFactory && !pendingLoader) { |
| missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy); |
| } |
| |
| if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) { |
| result = missingTranslationHandlerTranslation; |
| } |
| } |
| } |
| |
| return result; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#versionInfo |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns the current version information for the angular-translate library |
| * |
| * @return {string} angular-translate version |
| */ |
| $translate.versionInfo = function () { |
| 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; |
| }; |
| |
| // internal purpose only |
| $translate.directivePriority = function () { |
| return directivePriority; |
| }; |
| |
| // internal purpose only |
| $translate.statefulFilter = function () { |
| return statefulFilter; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#isReady |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns whether the service is "ready" to translate (i.e. loading 1st language). |
| * |
| * See also {@link pascalprecht.translate.$translate#methods_onReady onReady()}. |
| * |
| * @return {boolean} current value of ready |
| */ |
| $translate.isReady = function () { |
| return $isReady; |
| }; |
| |
| var $onReadyDeferred = $q.defer(); |
| $onReadyDeferred.promise.then(function () { |
| $isReady = true; |
| }); |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#onReady |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns whether the service is "ready" to translate (i.e. loading 1st language). |
| * |
| * See also {@link pascalprecht.translate.$translate#methods_isReady isReady()}. |
| * |
| * @param {Function=} fn Function to invoke when service is ready |
| * @return {object} Promise resolved when service is ready |
| */ |
| $translate.onReady = function (fn) { |
| var deferred = $q.defer(); |
| if (angular.isFunction(fn)) { |
| deferred.promise.then(fn); |
| } |
| if ($isReady) { |
| deferred.resolve(); |
| } else { |
| $onReadyDeferred.promise.then(deferred.resolve); |
| } |
| return deferred.promise; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#getAvailableLanguageKeys |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * This function simply returns the registered language keys being defined before in the config phase |
| * With this, an application can use the array to provide a language selection dropdown or similar |
| * without any additional effort |
| * |
| * @returns {object} returns the list of possibly registered language keys and mapping or null if not defined |
| */ |
| $translate.getAvailableLanguageKeys = function () { |
| if ($availableLanguageKeys.length > 0) { |
| return $availableLanguageKeys; |
| } |
| return null; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translate#getTranslationTable |
| * @methodOf pascalprecht.translate.$translate |
| * |
| * @description |
| * Returns translation table by the given language key. |
| * |
| * Unless a language is provided it returns a translation table of the current one. |
| * Note: If translation dictionary is currently downloading or in progress |
| * it will return null. |
| * |
| * @param {string} langKey A token which represents a translation id |
| * |
| * @return {object} a copy of angular-translate $translationTable |
| */ |
| $translate.getTranslationTable = function (langKey) { |
| langKey = langKey || $translate.use(); |
| if (langKey && $translationTable[langKey]) { |
| return angular.copy($translationTable[langKey]); |
| } |
| return null; |
| }; |
| |
| // Whenever $translateReady is being fired, this will ensure the state of $isReady |
| var globalOnReadyListener = $rootScope.$on('$translateReady', function () { |
| $onReadyDeferred.resolve(); |
| globalOnReadyListener(); // one time only |
| globalOnReadyListener = null; |
| }); |
| var globalOnChangeListener = $rootScope.$on('$translateChangeEnd', function () { |
| $onReadyDeferred.resolve(); |
| globalOnChangeListener(); // one time only |
| globalOnChangeListener = null; |
| }); |
| |
| if ($loaderFactory) { |
| |
| // If at least one async loader is defined and there are no |
| // (default) translations available we should try to load them. |
| if (angular.equals($translationTable, {})) { |
| if ($translate.use()) { |
| $translate.use($translate.use()); |
| } |
| } |
| |
| // Also, if there are any fallback language registered, we start |
| // loading them asynchronously as soon as we can. |
| if ($fallbackLanguage && $fallbackLanguage.length) { |
| var processAsyncResult = function (translation) { |
| translations(translation.key, translation.table); |
| $rootScope.$emit('$translateChangeEnd', {language : translation.key}); |
| return translation; |
| }; |
| for (var i = 0, len = $fallbackLanguage.length; i < len; i++) { |
| var fallbackLanguageId = $fallbackLanguage[i]; |
| if ($forceAsyncReloadEnabled || !$translationTable[fallbackLanguageId]) { |
| langPromises[fallbackLanguageId] = loadAsync(fallbackLanguageId).then(processAsyncResult); |
| } |
| } |
| } |
| } else { |
| $rootScope.$emit('$translateReady', {language : $translate.use()}); |
| } |
| |
| return $translate; |
| }]; |
| } |
| |
| $translate.displayName = 'displayName'; |
| |
| /** |
| * @ngdoc object |
| * @name pascalprecht.translate.$translateDefaultInterpolation |
| * @requires $interpolate |
| * |
| * @description |
| * Uses angular's `$interpolate` services to interpolate strings against some values. |
| * |
| * Be aware to configure a proper sanitization strategy. |
| * |
| * See also: |
| * * {@link pascalprecht.translate.$translateSanitization} |
| * |
| * @return {object} $translateDefaultInterpolation Interpolator service |
| */ |
| angular.module('pascalprecht.translate').factory('$translateDefaultInterpolation', $translateDefaultInterpolation); |
| |
| function $translateDefaultInterpolation ($interpolate, $translateSanitization) { |
| |
| 'use strict'; |
| |
| var $translateInterpolator = {}, |
| $locale, |
| $identifier = 'default'; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateDefaultInterpolation#setLocale |
| * @methodOf pascalprecht.translate.$translateDefaultInterpolation |
| * |
| * @description |
| * Sets current locale (this is currently not use in this interpolation). |
| * |
| * @param {string} locale Language key or locale. |
| */ |
| $translateInterpolator.setLocale = function (locale) { |
| $locale = locale; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateDefaultInterpolation#getInterpolationIdentifier |
| * @methodOf pascalprecht.translate.$translateDefaultInterpolation |
| * |
| * @description |
| * Returns an identifier for this interpolation service. |
| * |
| * @returns {string} $identifier |
| */ |
| $translateInterpolator.getInterpolationIdentifier = function () { |
| return $identifier; |
| }; |
| |
| /** |
| * @deprecated will be removed in 3.0 |
| * @see {@link pascalprecht.translate.$translateSanitization} |
| */ |
| $translateInterpolator.useSanitizeValueStrategy = function (value) { |
| $translateSanitization.useStrategy(value); |
| return this; |
| }; |
| |
| /** |
| * @ngdoc function |
| * @name pascalprecht.translate.$translateDefaultInterpolation#interpolate |
| * @methodOf pascalprecht.translate.$translateDefaultInterpolation |
| * |
| * @description |
| * Interpolates given value agains given interpolate params using angulars |
| * `$interpolate` service. |
| * |
| * Since AngularJS 1.5, `value` must not be a string but can be anything input. |
| * |
| * @returns {string} interpolated string. |
| */ |
| $translateInterpolator.interpolate = function (value, interpolationParams, context, sanitizeStrategy) { |
| interpolationParams = interpolationParams || {}; |
| interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params', sanitizeStrategy, context); |
| |
| var interpolatedText; |
| if (angular.isNumber(value)) { |
| // numbers are safe |
| interpolatedText = '' + value; |
| } else if (angular.isString(value)) { |
| // strings must be interpolated (that's the job here) |
| interpolatedText = $interpolate(value)(interpolationParams); |
| interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text', sanitizeStrategy, context); |
| } else { |
| // neither a number or a string, cant interpolate => empty string |
| interpolatedText = ''; |
| } |
| |
| return interpolatedText; |
| }; |
| |
| return $translateInterpolator; |
| } |
| |
| $translateDefaultInterpolation.displayName = '$translateDefaultInterpolation'; |
| |
| angular.module('pascalprecht.translate').constant('$STORAGE_KEY', 'NG_TRANSLATE_LANG_KEY'); |
| |
| angular.module('pascalprecht.translate') |
| /** |
| * @ngdoc directive |
| * @name pascalprecht.translate.directive:translate |
| * @requires $interpolate, |
| * @requires $compile, |
| * @requires $parse, |
| * @requires $rootScope |
| * @restrict AE |
| * |
| * @description |
| * Translates given translation id either through attribute or DOM content. |
| * Internally it uses $translate service to translate the translation id. It possible to |
| * pass an optional `translate-values` object literal as string into translation id. |
| * |
| * @param {string=} translate Translation id which could be either string or interpolated string. |
| * @param {string=} translate-values Values to pass into translation id. Can be passed as object literal string or interpolated object. |
| * @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute. |
| * @param {string=} translate-default will be used unless translation was successful |
| * @param {boolean=} translate-compile (default true if present) defines locally activation of {@link pascalprecht.translate.$translateProvider#methods_usePostCompiling} |
| * @param {boolean=} translate-keep-content (default true if present) defines that in case a KEY could not be translated, that the existing content is left in the innerHTML} |
| * |
| * @example |
| <example module="ngView"> |
| <file name="index.html"> |
| <div ng-controller="TranslateCtrl"> |
| |
| <pre translate="TRANSLATION_ID"></pre> |
| <pre translate>TRANSLATION_ID</pre> |
| <pre translate translate-attr-title="TRANSLATION_ID"></pre> |
| <pre translate="{{translationId}}"></pre> |
| <pre translate>{{translationId}}</pre> |
| <pre translate="WITH_VALUES" translate-values="{value: 5}"></pre> |
| <pre translate translate-values="{value: 5}">WITH_VALUES</pre> |
| <pre translate="WITH_VALUES" translate-values="{{values}}"></pre> |
| <pre translate translate-values="{{values}}">WITH_VALUES</pre> |
| <pre translate translate-attr-title="WITH_VALUES" translate-values="{{values}}"></pre> |
| <pre translate="WITH_CAMEL_CASE_KEY" translate-value-camel-case-key="Hi"></pre> |
| |
| </div> |
| </file> |
| <file name="script.js"> |
| angular.module('ngView', ['pascalprecht.translate']) |
| |
| .config(function ($translateProvider) { |
| |
| $translateProvider.translations('en',{ |
| 'TRANSLATION_ID': 'Hello there!', |
| 'WITH_VALUES': 'The following value is dynamic: {{value}}', |
| 'WITH_CAMEL_CASE_KEY': 'The interpolation key is camel cased: {{camelCaseKey}}' |
| }).preferredLanguage('en'); |
| |
| }); |
| |
| angular.module('ngView').controller('TranslateCtrl', function ($scope) { |
| $scope.translationId = 'TRANSLATION_ID'; |
| |
| $scope.values = { |
| value: 78 |
| }; |
| }); |
| </file> |
| <file name="scenario.js"> |
| it('should translate', function () { |
| inject(function ($rootScope, $compile) { |
| $rootScope.translationId = 'TRANSLATION_ID'; |
| |
| element = $compile('<p translate="TRANSLATION_ID"></p>')($rootScope); |
| $rootScope.$digest(); |
| expect(element.text()).toBe('Hello there!'); |
| |
| element = $compile('<p translate="{{translationId}}"></p>')($rootScope); |
| $rootScope.$digest(); |
| expect(element.text()).toBe('Hello there!'); |
| |
| element = $compile('<p translate>TRANSLATION_ID</p>')($rootScope); |
| $rootScope.$digest(); |
| expect(element.text()).toBe('Hello there!'); |
| |
| element = $compile('<p translate>{{translationId}}</p>')($rootScope); |
| $rootScope.$digest(); |
| expect(element.text()).toBe('Hello there!'); |
| |
| element = $compile('<p translate translate-attr-title="TRANSLATION_ID"></p>')($rootScope); |
| $rootScope.$digest(); |
| expect(element.attr('title')).toBe('Hello there!'); |
| |
| element = $compile('<p translate="WITH_CAMEL_CASE_KEY" translate-value-camel-case-key="Hello"></p>')($rootScope); |
| $rootScope.$digest(); |
| expect(element.text()).toBe('The interpolation key is camel cased: Hello'); |
| }); |
| }); |
| </file> |
| </example> |
| */ |
| .directive('translate', translateDirective); |
| function translateDirective($translate, $interpolate, $compile, $parse, $rootScope) { |
| |
| 'use strict'; |
| |
| /** |
| * @name trim |
| * @private |
| * |
| * @description |
| * trim polyfill |
| * |
| * @returns {string} The string stripped of whitespace from both ends |
| */ |
| var trim = function() { |
| return this.toString().replace(/^\s+|\s+$/g, ''); |
| }; |
| |
| return { |
| restrict: 'AE', |
| scope: true, |
| priority: $translate.directivePriority(), |
| compile: function (tElement, tAttr) { |
| |
| var translateValuesExist = (tAttr.translateValues) ? |
| tAttr.translateValues : undefined; |
| |
| var translateInterpolation = (tAttr.translateInterpolation) ? |
| tAttr.translateInterpolation : undefined; |
| |
| var translateValueExist = tElement[0].outerHTML.match(/translate-value-+/i); |
| |
| var interpolateRegExp = '^(.*)(' + $interpolate.startSymbol() + '.*' + $interpolate.endSymbol() + ')(.*)', |
| watcherRegExp = '^(.*)' + $interpolate.startSymbol() + '(.*)' + $interpolate.endSymbol() + '(.*)'; |
| |
| return function linkFn(scope, iElement, iAttr) { |
| |
| scope.interpolateParams = {}; |
| scope.preText = ''; |
| scope.postText = ''; |
| scope.translateNamespace = getTranslateNamespace(scope); |
| var translationIds = {}; |
| |
| var initInterpolationParams = function (interpolateParams, iAttr, tAttr) { |
| // initial setup |
| if (iAttr.translateValues) { |
| angular.extend(interpolateParams, $parse(iAttr.translateValues)(scope.$parent)); |
| } |
| // initially fetch all attributes if existing and fill the params |
| if (translateValueExist) { |
| for (var attr in tAttr) { |
| if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') { |
| var attributeName = angular.lowercase(attr.substr(14, 1)) + attr.substr(15); |
| interpolateParams[attributeName] = tAttr[attr]; |
| } |
| } |
| } |
| }; |
| |
| // Ensures any change of the attribute "translate" containing the id will |
| // be re-stored to the scope's "translationId". |
| // If the attribute has no content, the element's text value (white spaces trimmed off) will be used. |
| var observeElementTranslation = function (translationId) { |
| |
| // Remove any old watcher |
| if (angular.isFunction(observeElementTranslation._unwatchOld)) { |
| observeElementTranslation._unwatchOld(); |
| observeElementTranslation._unwatchOld = undefined; |
| } |
| |
| if (angular.equals(translationId , '') || !angular.isDefined(translationId)) { |
| var iElementText = trim.apply(iElement.text()); |
| |
| // Resolve translation id by inner html if required |
| var interpolateMatches = iElementText.match(interpolateRegExp); |
| // Interpolate translation id if required |
| if (angular.isArray(interpolateMatches)) { |
| scope.preText = interpolateMatches[1]; |
| scope.postText = interpolateMatches[3]; |
| translationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent); |
| var watcherMatches = iElementText.match(watcherRegExp); |
| if (angular.isArray(watcherMatches) && watcherMatches[2] && watcherMatches[2].length) { |
| observeElementTranslation._unwatchOld = scope.$watch(watcherMatches[2], function (newValue) { |
| translationIds.translate = newValue; |
| updateTranslations(); |
| }); |
| } |
| } else { |
| // do not assigne the translation id if it is empty. |
| translationIds.translate = !iElementText ? undefined : iElementText; |
| } |
| } else { |
| translationIds.translate = translationId; |
| } |
| updateTranslations(); |
| }; |
| |
| var observeAttributeTranslation = function (translateAttr) { |
| iAttr.$observe(translateAttr, function (translationId) { |
| translationIds[translateAttr] = translationId; |
| updateTranslations(); |
| }); |
| }; |
| |
| // initial setup with values |
| initInterpolationParams(scope.interpolateParams, iAttr, tAttr); |
| |
| var firstAttributeChangedEvent = true; |
| iAttr.$observe('translate', function (translationId) { |
| if (typeof translationId === 'undefined') { |
| // case of element "<translate>xyz</translate>" |
| observeElementTranslation(''); |
| } else { |
| // case of regular attribute |
| if (translationId !== '' || !firstAttributeChangedEvent) { |
| translationIds.translate = translationId; |
| updateTranslations(); |
| } |
| } |
| firstAttributeChangedEvent = false; |
| }); |
| |
| for (var translateAttr in iAttr) { |
| if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr' && translateAttr.length > 13) { |
| observeAttributeTranslation(translateAttr); |
| } |
| } |
| |
| iAttr.$observe('translateDefault', function (value) { |
| scope.defaultText = value; |
| updateTranslations(); |
| }); |
| |
| if (translateValuesExist) { |
| iAttr.$observe('translateValues', function (interpolateParams) { |
| if (interpolateParams) { |
| scope.$parent.$watch(function () { |
| angular.extend(scope.interpolateParams, $parse(interpolateParams)(scope.$parent)); |
| }); |
| } |
| }); |
| } |
| |
| if (translateValueExist) { |
| var observeValueAttribute = function (attrName) { |
| iAttr.$observe(attrName, function (value) { |
| var attributeName = angular.lowercase(attrName.substr(14, 1)) + attrName.substr(15); |
| scope.interpolateParams[attributeName] = value; |
| }); |
| }; |
| for (var attr in iAttr) { |
| if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') { |
| observeValueAttribute(attr); |
| } |
| } |
| } |
| |
| // Master update function |
| var updateTranslations = function () { |
| for (var key in translationIds) { |
| if (translationIds.hasOwnProperty(key) && translationIds[key] !== undefined) { |
| updateTranslation(key, translationIds[key], scope, scope.interpolateParams, scope.defaultText, scope.translateNamespace); |
| } |
| } |
| }; |
| |
| // Put translation processing function outside loop |
| var updateTranslation = function(translateAttr, translationId, scope, interpolateParams, defaultTranslationText, translateNamespace) { |
| if (translationId) { |
| // if translation id starts with '.' and translateNamespace given, prepend namespace |
| if (translateNamespace && translationId.charAt(0) === '.') { |
| translationId = translateNamespace + translationId; |
| } |
| |
| $translate(translationId, interpolateParams, translateInterpolation, defaultTranslationText, scope.translateLanguage) |
| .then(function (translation) { |
| applyTranslation(translation, scope, true, translateAttr); |
| }, function (translationId) { |
| applyTranslation(translationId, scope, false, translateAttr); |
| }); |
| } else { |
| // as an empty string cannot be translated, we can solve this using successful=false |
| applyTranslation(translationId, scope, false, translateAttr); |
| } |
| }; |
| |
| var applyTranslation = function (value, scope, successful, translateAttr) { |
| if (!successful) { |
| if (typeof scope.defaultText !== 'undefined') { |
| value = scope.defaultText; |
| } |
| } |
| if (translateAttr === 'translate') { |
| // default translate into innerHTML |
| if (successful || (!successful && !$translate.isKeepContent() && typeof iAttr.translateKeepContent === 'undefined')) { |
| iElement.empty().append(scope.preText + value + scope.postText); |
| } |
| var globallyEnabled = $translate.isPostCompilingEnabled(); |
| var locallyDefined = typeof tAttr.translateCompile !== 'undefined'; |
| var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false'; |
| if ((globallyEnabled && !locallyDefined) || locallyEnabled) { |
| $compile(iElement.contents())(scope); |
| } |
| } else { |
| // translate attribute |
| var attributeName = iAttr.$attr[translateAttr]; |
| if (attributeName.substr(0, 5) === 'data-') { |
| // ensure html5 data prefix is stripped |
| attributeName = attributeName.substr(5); |
| } |
| attributeName = attributeName.substr(15); |
| iElement.attr(attributeName, value); |
| } |
| }; |
| |
| if (translateValuesExist || translateValueExist || iAttr.translateDefault) { |
| scope.$watch('interpolateParams', updateTranslations, true); |
| } |
| |
| // Replaced watcher on translateLanguage with event listener |
| scope.$on('translateLanguageChanged', updateTranslations); |
| |
| // Ensures the text will be refreshed after the current language was changed |
| // w/ $translate.use(...) |
| var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations); |
| |
| // ensure translation will be looked up at least one |
| if (iElement.text().length) { |
| if (iAttr.translate) { |
| observeElementTranslation(iAttr.translate); |
| } else { |
| observeElementTranslation(''); |
| } |
| } else if (iAttr.translate) { |
| // ensure attribute will be not skipped |
| observeElementTranslation(iAttr.translate); |
| } |
| updateTranslations(); |
| scope.$on('$destroy', unbind); |
| }; |
| } |
| }; |
| } |
| |
| /** |
| * Returns the scope's namespace. |
| * @private |
| * @param scope |
| * @returns {string} |
| */ |
| function getTranslateNamespace(scope) { |
| 'use strict'; |
| if (scope.translateNamespace) { |
| return scope.translateNamespace; |
| } |
| if (scope.$parent) { |
| return getTranslateNamespace(scope.$parent); |
| } |
| } |
| |
| translateDirective.displayName = 'translateDirective'; |
| |
| angular.module('pascalprecht.translate') |
| /** |
| * @ngdoc directive |
| * @name pascalprecht.translate.directive:translate-attr |
| * @restrict A |
| * |
| * @description |
| * Translates attributes like translate-attr-ATTR, but with an object like ng-class. |
| * Internally it uses `translate` service to translate translation id. It possible to |
| * pass an optional `translate-values` object literal as string into translation id. |
| * |
| * @param {string=} translate-attr Object literal mapping attributes to translation ids. |
| * @param {string=} translate-values Values to pass into the translation ids. Can be passed as object literal string. |
| * |
| * @example |
| <example module="ngView"> |
| <file name="index.html"> |
| <div ng-controller="TranslateCtrl"> |
| |
| <input translate-attr="{ placeholder: translationId, title: 'WITH_VALUES' }" translate-values="{value: 5}" /> |
| |
| </div> |
| </file> |
| <file name="script.js"> |
| angular.module('ngView', ['pascalprecht.translate']) |
| |
| .config(function ($translateProvider) { |
| |
| $translateProvider.translations('en',{ |
| 'TRANSLATION_ID': 'Hello there!', |
| 'WITH_VALUES': 'The following value is dynamic: {{value}}', |
| }).preferredLanguage('en'); |
| |
| }); |
| |
| angular.module('ngView').controller('TranslateCtrl', function ($scope) { |
| $scope.translationId = 'TRANSLATION_ID'; |
| |
| $scope.values = { |
| value: 78 |
| }; |
| }); |
| </file> |
| <file name="scenario.js"> |
| it('should translate', function () { |
| inject(function ($rootScope, $compile) { |
| $rootScope.translationId = 'TRANSLATION_ID'; |
| |
| element = $compile('<input translate-attr="{ placeholder: translationId, title: 'WITH_VALUES' }" translate-values="{ value: 5 }" />')($rootScope); |
| $rootScope.$digest(); |
| expect(element.attr('placeholder)).toBe('Hello there!'); |
| expect(element.attr('title)).toBe('The following value is dynamic: 5'); |
| }); |
| }); |
| </file> |
| </example> |
| */ |
| .directive('translateAttr', translateAttrDirective); |
| function translateAttrDirective($translate, $rootScope) { |
| |
| 'use strict'; |
| |
| return { |
| restrict: 'A', |
| priority: $translate.directivePriority(), |
| link: function linkFn(scope, element, attr) { |
| |
| var translateAttr, |
| translateValues, |
| previousAttributes = {}; |
| |
| // Main update translations function |
| var updateTranslations = function () { |
| angular.forEach(translateAttr, function (translationId, attributeName) { |
| if (!translationId) { |
| return; |
| } |
| previousAttributes[attributeName] = true; |
| |
| // if translation id starts with '.' and translateNamespace given, prepend namespace |
| if (scope.translateNamespace && translationId.charAt(0) === '.') { |
| translationId = scope.translateNamespace + translationId; |
| } |
| $translate(translationId, translateValues, attr.translateInterpolation, undefined, scope.translateLanguage) |
| .then(function (translation) { |
| element.attr(attributeName, translation); |
| }, function (translationId) { |
| element.attr(attributeName, translationId); |
| }); |
| }); |
| |
| // Removing unused attributes that were previously used |
| angular.forEach(previousAttributes, function (flag, attributeName) { |
| if (!translateAttr[attributeName]) { |
| element.removeAttr(attributeName); |
| delete previousAttributes[attributeName]; |
| } |
| }); |
| }; |
| |
| // Watch for attribute changes |
| watchAttribute( |
| scope, |
| attr.translateAttr, |
| function (newValue) { translateAttr = newValue; }, |
| updateTranslations |
| ); |
| // Watch for value changes |
| watchAttribute( |
| scope, |
| attr.translateValues, |
| function (newValue) { translateValues = newValue; }, |
| updateTranslations |
| ); |
| |
| if (attr.translateValues) { |
| scope.$watch(attr.translateValues, updateTranslations, true); |
| } |
| |
| // Replaced watcher on translateLanguage with event listener |
| scope.$on('translateLanguageChanged', updateTranslations); |
| |
| // Ensures the text will be refreshed after the current language was changed |
| // w/ $translate.use(...) |
| var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations); |
| |
| updateTranslations(); |
| scope.$on('$destroy', unbind); |
| } |
| }; |
| } |
| |
| function watchAttribute(scope, attribute, valueCallback, changeCallback) { |
| 'use strict'; |
| if (!attribute) { |
| return; |
| } |
| if (attribute.substr(0, 2) === '::') { |
| attribute = attribute.substr(2); |
| } else { |
| scope.$watch(attribute, function(newValue) { |
| valueCallback(newValue); |
| changeCallback(); |
| }, true); |
| } |
| valueCallback(scope.$eval(attribute)); |
| } |
| |
| translateAttrDirective.displayName = 'translateAttrDirective'; |
| |
| angular.module('pascalprecht.translate') |
| /** |
| * @ngdoc directive |
| * @name pascalprecht.translate.directive:translateCloak |
| * @requires $rootScope |
| * @requires $translate |
| * @restrict A |
| * |
| * $description |
| * Adds a `translate-cloak` class name to the given element where this directive |
| * is applied initially and removes it, once a loader has finished loading. |
| * |
| * This directive can be used to prevent initial flickering when loading translation |
| * data asynchronously. |
| * |
| * The class name is defined in |
| * {@link pascalprecht.translate.$translateProvider#cloakClassName $translate.cloakClassName()}. |
| * |
| * @param {string=} translate-cloak If a translationId is provided, it will be used for showing |
| * or hiding the cloak. Basically it relies on the translation |
| * resolve. |
| */ |
| .directive('translateCloak', translateCloakDirective); |
| |
| function translateCloakDirective($translate, $rootScope) { |
| |
| 'use strict'; |
| |
| return { |
| compile: function (tElement) { |
| var applyCloak = function () { |
| tElement.addClass($translate.cloakClassName()); |
| }, |
| removeCloak = function () { |
| tElement.removeClass($translate.cloakClassName()); |
| }; |
| $translate.onReady(function () { |
| removeCloak(); |
| }); |
| applyCloak(); |
| |
| return function linkFn(scope, iElement, iAttr) { |
| if (iAttr.translateCloak && iAttr.translateCloak.length) { |
| // Register a watcher for the defined translation allowing a fine tuned cloak |
| iAttr.$observe('translateCloak', function (translationId) { |
| $translate(translationId).then(removeCloak, applyCloak); |
| }); |
| // Register for change events as this is being another indicicator revalidating the cloak) |
| $rootScope.$on('$translateChangeSuccess', function () { |
| $translate(iAttr.translateCloak).then(removeCloak, applyCloak); |
| }); |
| } |
| }; |
| } |
| }; |
| } |
| |
| translateCloakDirective.displayName = 'translateCloakDirective'; |
| |
| angular.module('pascalprecht.translate') |
| /** |
| * @ngdoc directive |
| * @name pascalprecht.translate.directive:translateNamespace |
| * @restrict A |
| * |
| * @description |
| * Translates given translation id either through attribute or DOM content. |
| * Internally it uses `translate` filter to translate translation id. It possible to |
| * pass an optional `translate-values` object literal as string into translation id. |
| * |
| * @param {string=} translate namespace name which could be either string or interpolated string. |
| * |
| * @example |
| <example module="ngView"> |
| <file name="index.html"> |
| <div translate-namespace="CONTENT"> |
| |
| <div> |
| <h1 translate>.HEADERS.TITLE</h1> |
| <h1 translate>.HEADERS.WELCOME</h1> |
| </div> |
| |
| <div translate-namespace=".HEADERS"> |
| <h1 translate>.TITLE</h1> |
| <h1 translate>.WELCOME</h1> |
| </div> |
| |
| </div> |
| </file> |
| <file name="script.js"> |
| angular.module('ngView', ['pascalprecht.translate']) |
| |
| .config(function ($translateProvider) { |
| |
| $translateProvider.translations('en',{ |
| 'TRANSLATION_ID': 'Hello there!', |
| 'CONTENT': { |
| 'HEADERS': { |
| TITLE: 'Title' |
| } |
| }, |
| 'CONTENT.HEADERS.WELCOME': 'Welcome' |
| }).preferredLanguage('en'); |
| |
| }); |
| |
| </file> |
| </example> |
| */ |
| .directive('translateNamespace', translateNamespaceDirective); |
| |
| function translateNamespaceDirective() { |
| |
| 'use strict'; |
| |
| return { |
| restrict: 'A', |
| scope: true, |
| compile: function () { |
| return { |
| pre: function (scope, iElement, iAttrs) { |
| scope.translateNamespace = getTranslateNamespace(scope); |
| |
| if (scope.translateNamespace && iAttrs.translateNamespace.charAt(0) === '.') { |
| scope.translateNamespace += iAttrs.translateNamespace; |
| } else { |
| scope.translateNamespace = iAttrs.translateNamespace; |
| } |
| } |
| }; |
| } |
| }; |
| } |
| |
| /** |
| * Returns the scope's namespace. |
| * @private |
| * @param scope |
| * @returns {string} |
| */ |
| function getTranslateNamespace(scope) { |
| 'use strict'; |
| if (scope.translateNamespace) { |
| return scope.translateNamespace; |
| } |
| if (scope.$parent) { |
| return getTranslateNamespace(scope.$parent); |
| } |
| } |
| |
| translateNamespaceDirective.displayName = 'translateNamespaceDirective'; |
| |
| angular.module('pascalprecht.translate') |
| /** |
| * @ngdoc directive |
| * @name pascalprecht.translate.directive:translateLanguage |
| * @restrict A |
| * |
| * @description |
| * Forces the language to the directives in the underlying scope. |
| * |
| * @param {string=} translate language that will be negotiated. |
| * |
| * @example |
| <example module="ngView"> |
| <file name="index.html"> |
| <div> |
| |
| <div> |
| <h1 translate>HELLO</h1> |
| </div> |
| |
| <div translate-language="de"> |
| <h1 translate>HELLO</h1> |
| </div> |
| |
| </div> |
| </file> |
| <file name="script.js"> |
| angular.module('ngView', ['pascalprecht.translate']) |
| |
| .config(function ($translateProvider) { |
| |
| $translateProvider |
| .translations('en',{ |
| 'HELLO': 'Hello world!' |
| }) |
| .translations('de',{ |
| 'HELLO': 'Hallo Welt!' |
| }) |
| .preferredLanguage('en'); |
| |
| }); |
| |
| </file> |
| </example> |
| */ |
| .directive('translateLanguage', translateLanguageDirective); |
| |
| function translateLanguageDirective() { |
| |
| 'use strict'; |
| |
| return { |
| restrict: 'A', |
| scope: true, |
| compile: function () { |
| return function linkFn(scope, iElement, iAttrs) { |
| |
| iAttrs.$observe('translateLanguage', function (newTranslateLanguage) { |
| scope.translateLanguage = newTranslateLanguage; |
| }); |
| |
| scope.$watch('translateLanguage', function(){ |
| scope.$broadcast('translateLanguageChanged'); |
| }); |
| }; |
| } |
| }; |
| } |
| |
| translateLanguageDirective.displayName = 'translateLanguageDirective'; |
| |
| angular.module('pascalprecht.translate') |
| /** |
| * @ngdoc filter |
| * @name pascalprecht.translate.filter:translate |
| * @requires $parse |
| * @requires pascalprecht.translate.$translate |
| * @function |
| * |
| * @description |
| * Uses `$translate` service to translate contents. Accepts interpolate parameters |
| * to pass dynamized values though translation. |
| * |
| * @param {string} translationId A translation id to be translated. |
| * @param {*=} interpolateParams Optional object literal (as hash or string) to pass values into translation. |
| * |
| * @returns {string} Translated text. |
| * |
| * @example |
| <example module="ngView"> |
| <file name="index.html"> |
| <div ng-controller="TranslateCtrl"> |
| |
| <pre>{{ 'TRANSLATION_ID' | translate }}</pre> |
| <pre>{{ translationId | translate }}</pre> |
| <pre>{{ 'WITH_VALUES' | translate:'{value: 5}' }}</pre> |
| <pre>{{ 'WITH_VALUES' | translate:values }}</pre> |
| |
| </div> |
| </file> |
| <file name="script.js"> |
| angular.module('ngView', ['pascalprecht.translate']) |
| |
| .config(function ($translateProvider) { |
| |
| $translateProvider.translations('en', { |
| 'TRANSLATION_ID': 'Hello there!', |
| 'WITH_VALUES': 'The following value is dynamic: {{value}}' |
| }); |
| $translateProvider.preferredLanguage('en'); |
| |
| }); |
| |
| angular.module('ngView').controller('TranslateCtrl', function ($scope) { |
| $scope.translationId = 'TRANSLATION_ID'; |
| |
| $scope.values = { |
| value: 78 |
| }; |
| }); |
| </file> |
| </example> |
| */ |
| .filter('translate', translateFilterFactory); |
| |
| function translateFilterFactory($parse, $translate) { |
| |
| 'use strict'; |
| |
| var translateFilter = function (translationId, interpolateParams, interpolation, forceLanguage) { |
| if (!angular.isObject(interpolateParams)) { |
| interpolateParams = $parse(interpolateParams)(this); |
| } |
| |
| return $translate.instant(translationId, interpolateParams, interpolation, forceLanguage); |
| }; |
| |
| if ($translate.statefulFilter()) { |
| translateFilter.$stateful = true; |
| } |
| |
| return translateFilter; |
| } |
| |
| translateFilterFactory.displayName = 'translateFilterFactory'; |
| |
| angular.module('pascalprecht.translate') |
| |
| /** |
| * @ngdoc object |
| * @name pascalprecht.translate.$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. |
| */ |
| .factory('$translationCache', $translationCache); |
| |
| function $translationCache($cacheFactory) { |
| |
| 'use strict'; |
| |
| return $cacheFactory('translations'); |
| } |
| |
| $translationCache.displayName = '$translationCache'; |
| return 'pascalprecht.translate'; |
| |
| })); |