Use Localization in AngularJS Using Internationalization

In today's web-based applications internationalization is an important feature. For fulfilling this requirement we need to first understand the two terms localization and internationalization.

Localization

The definition of Localization differs from person to person, but the main concept remains same. That is, it refers to the adaptation of a product, application or document, depending on the required language and culture setting (that is normally called the locale). Except for that, localization consists of the following topics depending on the language setting.

  1. Numeric value format.
  2. Date and time format.
  3. Currency format.
  4. Symbols, icons and colors.

Internationalization

Just as for Localization, the definition of Internationalization always varies. The actual concept is to design a product, application or document in such a way that it can be used for the localization settings depending on culture, region or language. Sometimes it is also called globalization.

Internationalization is often written i18n, where 18 is the number of letters between i and n in the English word.

This article explains how to use this concept in AngularJs. Likely, AngularJs directly supports internationalization and it also provides various locale setting files for various locales.

For downloading an AngulaJS Locale file, please visit the URL link and download your required locale setting file.

For doing this, we will first create a blank web site project and then 3 folders within the project with the following name:

  1. HTML
  2. Scripts
  3. UserScripts 

Now add a HTML file named index.html with the following code:

  1. <!DOCTYPE html>  
  2. <html xmlns="http://www.w3.org/1999/xhtml">  
  3. <head>  
  4.     <title></title>      
  5.     <script src="../Scripts/angular.min.js"></script>  
  6.     <script src="../Scripts/angular-route.js"></script>  
  7.     <script src="../Scripts/angular-translate.js"></script>  
  8.     <script src="../UserScript/MyApp.js"></script>  
  9.     <script src="../UserScript/Index.js"></script>  
  10. </head>  
  11. <body ng-app="MyApp" ng-controller="TranslateController">  
  12.     <h1>Localization</h1>  
  13.     <div>  
  14.         <button ng-click="changeLanguage('en-de')" translate="BUTTON_TEXT_DE"></button>  
  15.         <button ng-click="changeLanguage('en')" translate="BUTTON_TEXT_EN"></button>  
  16.         <button ng-click="changeLanguage('en-ar')" translate="BUTTON_TEXT_AE"></button>  
  17.     </div>  
  18.     <div>  
  19.         <h2>{{ 'HEADLINE' | translate }}</h2>  
  20.         <p>{{ 'INTRO_TEXT' | translate }}</p>  
  21.     </div>  
  22.     <div>  
  23.         <input type="date" />  
  24.     </div>  
  25. </body>  
  26. </html>  
Now in the preceding file, we use the reference of Angular.js and angular-route file. Since an Angular route file is required, we need to provide the localization code within the config section. We will explain it a little later. The third file that we called is the angular-translate file. Actually this file is responsible for providing the translate service for Angular. The following is the code for the angular-translate file:
  1. /*! 
  2.  * angular-translate - v2.7.2 - 2015-06-01 
  3.  * http://github.com/angular-translate/angular-translate 
  4.  * Copyright (c) 2015 ; Licensed MIT 
  5.  */  
  6. (function (root, factory) {  
  7.   if (typeof define === 'function' && define.amd) {  
  8.     // AMD. Register as an anonymous module unless amdModuleId is set  
  9.     define([], function () {  
  10.       return (factory());  
  11.     });  
  12.   } else if (typeof exports === 'object') {  
  13.     // Node. Does not work with strict CommonJS, but  
  14.     // only CommonJS-like environments that support module.exports,  
  15.     // like Node.  
  16.     module.exports = factory();  
  17.   } else {  
  18.     factory();  
  19.   }  
  20. }(thisfunction () {  
  21.   
  22. /** 
  23.  * @ngdoc overview 
  24.  * @name translate 
  25.  * 
  26.  * @description 
  27.  * The main module which holds everything together. 
  28.  */  
  29. angular.module('translate', ['ng'])  
  30.   .run(runTranslate);  
  31.   
  32. function runTranslate($translate) {  
  33.   
  34.   'use strict';  
  35.   
  36.   var key = $translate.storageKey(),  
  37.     storage = $translate.storage();  
  38.   
  39.   var fallbackFromIncorrectStorageValue = function () {  
  40.     var preferred = $translate.preferredLanguage();  
  41.     if (angular.isString(preferred)) {  
  42.       $translate.use(preferred);  
  43.       // $translate.use() will also remember the language.  
  44.       // So, we don't need to call storage.put() here.  
  45.     } else {  
  46.       storage.put(key, $translate.use());  
  47.     }  
  48.   };  
  49.   
  50.   fallbackFromIncorrectStorageValue.displayName = 'fallbackFromIncorrectStorageValue';  
  51.   
  52.   if (storage) {  
  53.     if (!storage.get(key)) {  
  54.       fallbackFromIncorrectStorageValue();  
  55.     } else {  
  56.       $translate.use(storage.get(key))['catch'](fallbackFromIncorrectStorageValue);  
  57.     }  
  58.   } else if (angular.isString($translate.preferredLanguage())) {  
  59.     $translate.use($translate.preferredLanguage());  
  60.   }  
  61. }  
  62. runTranslate.$inject = ['$translate'];  
  63.   
  64. runTranslate.displayName = 'runTranslate';  
  65.   
  66. /** 
  67.  * @ngdoc object 
  68.  * @name translate.$translateSanitizationProvider 
  69.  * 
  70.  * @description 
  71.  * 
  72.  * Configurations for $translateSanitization 
  73.  */  
  74. angular.module('translate').provider('$translateSanitization', $translateSanitizationProvider);  
  75.   
  76. function $translateSanitizationProvider () {  
  77.   
  78.   'use strict';  
  79.   
  80.   var $sanitize,  
  81.       currentStrategy = null// TODO change to either 'sanitize', 'escape' or ['sanitize', 'escapeParameters'] in 3.0.  
  82.       hasConfiguredStrategy = false,  
  83.       hasShownNoStrategyConfiguredWarning = false,  
  84.       strategies;  
  85.   
  86.   /** 
  87.    * Definition of a sanitization strategy function 
  88.    * @callback StrategyFunction 
  89.    * @param {string|object} value - value to be sanitized (either a string or an interpolated value map) 
  90.    * @param {string} mode - either 'text' for a string (translation) or 'params' for the interpolated params 
  91.    * @return {string|object} 
  92.    */  
  93.   
  94.   /** 
  95.    * @ngdoc property 
  96.    * @name strategies 
  97.    * @propertyOf translate.$translateSanitizationProvider 
  98.    * 
  99.    * @description 
  100.    * Following strategies are built-in: 
  101.    * <dl> 
  102.    *   <dt>sanitize</dt> 
  103.    *   <dd>Sanitizes HTML in the translation text using $sanitize</dd> 
  104.    *   <dt>escape</dt> 
  105.    *   <dd>Escapes HTML in the translation</dd> 
  106.    *   <dt>sanitizeParameters</dt> 
  107.    *   <dd>Sanitizes HTML in the values of the interpolation parameters using $sanitize</dd> 
  108.    *   <dt>escapeParameters</dt> 
  109.    *   <dd>Escapes HTML in the values of the interpolation parameters</dd> 
  110.    *   <dt>escaped</dt> 
  111.    *   <dd>Support legacy strategy name 'escaped' for backwards compatibility (will be removed in 3.0)</dd> 
  112.    * </dl> 
  113.    * 
  114.    */  
  115.   
  116.   strategies = {  
  117.     sanitize: function (value, mode) {  
  118.       if (mode === 'text') {  
  119.         value = htmlSanitizeValue(value);  
  120.       }  
  121.       return value;  
  122.     },  
  123.     escape: function (value, mode) {  
  124.       if (mode === 'text') {  
  125.         value = htmlEscapeValue(value);  
  126.       }  
  127.       return value;  
  128.     },  
  129.     sanitizeParameters: function (value, mode) {  
  130.       if (mode === 'params') {  
  131.         value = mapInterpolationParameters(value, htmlSanitizeValue);  
  132.       }  
  133.       return value;  
  134.     },  
  135.     escapeParameters: function (value, mode) {  
  136.       if (mode === 'params') {  
  137.         value = mapInterpolationParameters(value, htmlEscapeValue);  
  138.       }  
  139.       return value;  
  140.     }  
  141.   };  
  142.   // Support legacy strategy name 'escaped' for backwards compatibility.  
  143.   // TODO should be removed in 3.0  
  144.   strategies.escaped = strategies.escapeParameters;  
  145.   
  146.   /** 
  147.    * @ngdoc function 
  148.    * @name translate.$translateSanitizationProvider#addStrategy 
  149.    * @methodOf translate.$translateSanitizationProvider 
  150.    * 
  151.    * @description 
  152.    * Adds a sanitization strategy to the list of known strategies. 
  153.    * 
  154.    * @param {string} strategyName - unique key for a strategy 
  155.    * @param {StrategyFunction} strategyFunction - strategy function 
  156.    * @returns {object} this 
  157.    */  
  158.   this.addStrategy = function (strategyName, strategyFunction) {  
  159.     strategies[strategyName] = strategyFunction;  
  160.     return this;  
  161.   };  
  162.   
  163.   /** 
  164.    * @ngdoc function 
  165.    * @name translate.$translateSanitizationProvider#removeStrategy 
  166.    * @methodOf translate.$translateSanitizationProvider 
  167.    * 
  168.    * @description 
  169.    * Removes a sanitization strategy from the list of known strategies. 
  170.    * 
  171.    * @param {string} strategyName - unique key for a strategy 
  172.    * @returns {object} this 
  173.    */  
  174.   this.removeStrategy = function (strategyName) {  
  175.     delete strategies[strategyName];  
  176.     return this;  
  177.   };  
  178.   
  179.   /** 
  180.    * @ngdoc function 
  181.    * @name translate.$translateSanitizationProvider#useStrategy 
  182.    * @methodOf translate.$translateSanitizationProvider 
  183.    * 
  184.    * @description 
  185.    * Selects a sanitization strategy. When an array is provided the strategies will be executed in order. 
  186.    * 
  187.    * @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. 
  188.    * @returns {object} this 
  189.    */  
  190.   this.useStrategy = function (strategy) {  
  191.     hasConfiguredStrategy = true;  
  192.     currentStrategy = strategy;  
  193.     return this;  
  194.   };  
  195.   
  196.   /** 
  197.    * @ngdoc object 
  198.    * @name translate.$translateSanitization 
  199.    * @requires $injector 
  200.    * @requires $log 
  201.    * 
  202.    * @description 
  203.    * Sanitizes interpolation parameters and translated texts. 
  204.    * 
  205.    */  
  206.   this.$get = ['$injector''$log'function ($injector, $log) {  
  207.   
  208.     var applyStrategies = function (value, mode, selectedStrategies) {  
  209.       angular.forEach(selectedStrategies, function (selectedStrategy) {  
  210.         if (angular.isFunction(selectedStrategy)) {  
  211.           value = selectedStrategy(value, mode);  
  212.         } else if (angular.isFunction(strategies[selectedStrategy])) {  
  213.           value = strategies[selectedStrategy](value, mode);  
  214.         } else {  
  215.           throw new Error('translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\'');  
  216.         }  
  217.       });  
  218.       return value;  
  219.     };  
  220.   
  221.     // TODO: should be removed in 3.0  
  222.     var showNoStrategyConfiguredWarning = function () {  
  223.       if (!hasConfiguredStrategy && !hasShownNoStrategyConfiguredWarning) {  
  224.         $log.warn('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.');  
  225.         hasShownNoStrategyConfiguredWarning = true;  
  226.       }  
  227.     };  
  228.   
  229.     if ($injector.has('$sanitize')) {  
  230.       $sanitize = $injector.get('$sanitize');  
  231.     }  
  232.   
  233.     return {  
  234.       /** 
  235.        * @ngdoc function 
  236.        * @name translate.$translateSanitization#useStrategy 
  237.        * @methodOf translate.$translateSanitization 
  238.        * 
  239.        * @description 
  240.        * Selects a sanitization strategy. When an array is provided the strategies will be executed in order. 
  241.        * 
  242.        * @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. 
  243.        */  
  244.       useStrategy: (function (self) {  
  245.         return function (strategy) {  
  246.           self.useStrategy(strategy);  
  247.         };  
  248.       })(this),  
  249.   
  250.       /** 
  251.        * @ngdoc function 
  252.        * @name translate.$translateSanitization#sanitize 
  253.        * @methodOf translate.$translateSanitization 
  254.        * 
  255.        * @description 
  256.        * Sanitizes a value. 
  257.        * 
  258.        * @param {string|object} value The value which should be sanitized. 
  259.        * @param {string} mode The current sanitization mode, either 'params' or 'text'. 
  260.        * @param {string|StrategyFunction|array} [strategy] Optional custom strategy which should be used instead of the currently selected strategy. 
  261.        * @returns {string|object} sanitized value 
  262.        */  
  263.       sanitize: function (value, mode, strategy) {  
  264.         if (!currentStrategy) {  
  265.           showNoStrategyConfiguredWarning();  
  266.         }  
  267.   
  268.         if (arguments.length < 3) {  
  269.           strategy = currentStrategy;  
  270.         }  
  271.   
  272.         if (!strategy) {  
  273.           return value;  
  274.         }  
  275.   
  276.         var selectedStrategies = angular.isArray(strategy) ? strategy : [strategy];  
  277.         return applyStrategies(value, mode, selectedStrategies);  
  278.       }  
  279.     };  
  280.   }];  
  281.   
  282.   var htmlEscapeValue = function (value) {  
  283.     var element = angular.element('<div></div>');  
  284.     element.text(value); // not chainable, see #1044  
  285.     return element.html();  
  286.   };  
  287.   
  288.   var htmlSanitizeValue = function (value) {  
  289.     if (!$sanitize) {  
  290.       throw new Error('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\'.');  
  291.     }  
  292.     return $sanitize(value);  
  293.   };  
  294.   
  295.   var mapInterpolationParameters = function (value, iteratee) {  
  296.     if (angular.isObject(value)) {  
  297.       var result = angular.isArray(value) ? [] : {};  
  298.   
  299.       angular.forEach(value, function (propertyValue, propertyKey) {  
  300.         result[propertyKey] = mapInterpolationParameters(propertyValue, iteratee);  
  301.       });  
  302.   
  303.       return result;  
  304.     } else if (angular.isNumber(value)) {  
  305.       return value;  
  306.     } else {  
  307.       return iteratee(value);  
  308.     }  
  309.   };  
  310. }  
  311.   
  312. /** 
  313.  * @ngdoc object 
  314.  * @name translate.$translateProvider 
  315.  * @description 
  316.  * 
  317.  * $translateProvider allows developers to register translation-tables, asynchronous loaders 
  318.  * and similar to configure translation behavior directly inside of a module. 
  319.  * 
  320.  */  
  321. angular.module('translate')  
  322. .constant('pascalprechtTranslateOverrider', {})  
  323. .provider('$translate', $translate);  
  324.   
  325. function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvider, pascalprechtTranslateOverrider) {  
  326.   
  327.   'use strict';  
  328.   
  329.   var $translationTable = {},  
  330.       $preferredLanguage,  
  331.       $availableLanguageKeys = [],  
  332.       $languageKeyAliases,  
  333.       $fallbackLanguage,  
  334.       $fallbackWasString,  
  335.       $uses,  
  336.       $nextLang,  
  337.       $storageFactory,  
  338.       $storageKey = $STORAGE_KEY,  
  339.       $storagePrefix,  
  340.       $missingTranslationHandlerFactory,  
  341.       $interpolationFactory,  
  342.       $interpolatorFactories = [],  
  343.       $loaderFactory,  
  344.       $cloakClassName = 'translate-cloak',  
  345.       $loaderOptions,  
  346.       $notFoundIndicatorLeft,  
  347.       $notFoundIndicatorRight,  
  348.       $postCompilingEnabled = false,  
  349.       $forceAsyncReloadEnabled = false,  
  350.       NESTED_OBJECT_DELIMITER = '.',  
  351.       loaderCache,  
  352.       directivePriority = 0,  
  353.       statefulFilter = true,  
  354.       uniformLanguageTagResolver = 'default',  
  355.       languageTagResolver = {  
  356.         'default'function (tag) {  
  357.           return (tag || '').split('-').join('_');  
  358.         },  
  359.         java: function (tag) {  
  360.           var temp = (tag || '').split('-').join('_');  
  361.           var parts = temp.split('_');  
  362.           return parts.length > 1 ? (parts[0].toLowerCase() + '_' + parts[1].toUpperCase()) : temp;  
  363.         },  
  364.         bcp47: function (tag) {  
  365.           var temp = (tag || '').split('_').join('-');  
  366.           var parts = temp.split('-');  
  367.           return parts.length > 1 ? (parts[0].toLowerCase() + '-' + parts[1].toUpperCase()) : temp;  
  368.         }  
  369.       };  
  370.   
  371.   var version = '2.7.2';  
  372.   
  373.   // tries to determine the browsers language  
  374.   var getFirstBrowserLanguage = function () {  
  375.   
  376.     // internal purpose only  
  377.     if (angular.isFunction(pascalprechtTranslateOverrider.getLocale)) {  
  378.       return pascalprechtTranslateOverrider.getLocale();  
  379.     }  
  380.   
  381.     var nav = $windowProvider.$get().navigator,  
  382.         browserLanguagePropertyKeys = ['language''browserLanguage''systemLanguage''userLanguage'],  
  383.         i,  
  384.         language;  
  385.   
  386.     // support for HTML 5.1 "navigator.languages"  
  387.     if (angular.isArray(nav.languages)) {  
  388.       for (i = 0; i < nav.languages.length; i++) {  
  389.         language = nav.languages[i];  
  390.         if (language && language.length) {  
  391.           return language;  
  392.         }  
  393.       }  
  394.     }  
  395.   
  396.     // support for other well known properties in browsers  
  397.     for (i = 0; i < browserLanguagePropertyKeys.length; i++) {  
  398.       language = nav[browserLanguagePropertyKeys[i]];  
  399.       if (language && language.length) {  
  400.         return language;  
  401.       }  
  402.     }  
  403.   
  404.     return null;  
  405.   };  
  406.   getFirstBrowserLanguage.displayName = 'angular-translate/service: getFirstBrowserLanguage';  
  407.   
  408.   // tries to determine the browsers locale  
  409.   var getLocale = function () {  
  410.     var locale = getFirstBrowserLanguage() || '';  
  411.     if (languageTagResolver[uniformLanguageTagResolver]) {  
  412.       locale = languageTagResolver[uniformLanguageTagResolver](locale);  
  413.     }  
  414.     return locale;  
  415.   };  
  416.   getLocale.displayName = 'angular-translate/service: getLocale';  
  417.   
  418.   /** 
  419.    * @name indexOf 
  420.    * @private 
  421.    * 
  422.    * @description 
  423.    * indexOf polyfill. Kinda sorta. 
  424.    * 
  425.    * @param {array} array Array to search in. 
  426.    * @param {string} searchElement Element to search for. 
  427.    * 
  428.    * @returns {int} Index of search element. 
  429.    */  
  430.   var indexOf = function(array, searchElement) {  
  431.     for (var i = 0, len = array.length; i < len; i++) {  
  432.       if (array[i] === searchElement) {  
  433.         return i;  
  434.       }  
  435.     }  
  436.     return -1;  
  437.   };  
  438.   
  439.   /** 
  440.    * @name trim 
  441.    * @private 
  442.    * 
  443.    * @description 
  444.    * trim polyfill 
  445.    * 
  446.    * @returns {string} The string stripped of whitespace from both ends 
  447.    */  
  448.   var trim = function() {  
  449.     return this.toString().replace(/^\s+|\s+$/g, '');  
  450.   };  
  451.   
  452.   var negotiateLocale = function (preferred) {  
  453.   
  454.     var avail = [],  
  455.         locale = angular.lowercase(preferred),  
  456.         i = 0,  
  457.         n = $availableLanguageKeys.length;  
  458.   
  459.     for (; i < n; i++) {  
  460.       avail.push(angular.lowercase($availableLanguageKeys[i]));  
  461.     }  
  462.   
  463.     if (indexOf(avail, locale) > -1) {  
  464.       return preferred;  
  465.     }  
  466.   
  467.     if ($languageKeyAliases) {  
  468.       var alias;  
  469.       for (var langKeyAlias in $languageKeyAliases) {  
  470.         var hasWildcardKey = false;  
  471.         var hasExactKey = Object.prototype.hasOwnProperty.call($languageKeyAliases, langKeyAlias) &&  
  472.           angular.lowercase(langKeyAlias) === angular.lowercase(preferred);  
  473.   
  474.         if (langKeyAlias.slice(-1) === '*') {  
  475.           hasWildcardKey = langKeyAlias.slice(0, -1) === preferred.slice(0, langKeyAlias.length-1);  
  476.         }  
  477.         if (hasExactKey || hasWildcardKey) {  
  478.           alias = $languageKeyAliases[langKeyAlias];  
  479.           if (indexOf(avail, angular.lowercase(alias)) > -1) {  
  480.             return alias;  
  481.           }  
  482.         }  
  483.       }  
  484.     }  
  485.   
  486.     if (preferred) {  
  487.       var parts = preferred.split('_');  
  488.   
  489.       if (parts.length > 1 && indexOf(avail, angular.lowercase(parts[0])) > -1) {  
  490.         return parts[0];  
  491.       }  
  492.     }  
  493.   
  494.     // If everything fails, just return the preferred, unchanged.  
  495.     return preferred;  
  496.   };  
  497.   
  498.   /** 
  499.    * @ngdoc function 
  500.    * @name translate.$translateProvider#translations 
  501.    * @methodOf translate.$translateProvider 
  502.    * 
  503.    * @description 
  504.    * Registers a new translation table for specific language key. 
  505.    * 
  506.    * To register a translation table for specific language,  a defined language 
  507.    * key as first parameter. 
  508.    * 
  509.    * <pre> 
  510.    *  // register translation table for language: 'de_DE' 
  511.    *  $translateProvider.translations('de_DE', { 
  512.    *    'GREETING': 'Hallo Welt!' 
  513.    *  }); 
  514.    * 
  515.    *  // register another one 
  516.    *  $translateProvider.translations('en_US', { 
  517.    *    'GREETING': 'Hello world!' 
  518.    *  }); 
  519.    * </pre> 
  520.    * 
  521.    * When registering multiple translation tables for for the same language key, 
  522.    * the actual translation table gets extended. This allows you to define module 
  523.    * specific translation which only get added, once a specific module is loaded in 
  524.    * your app. 
  525.    * 
  526.    * Invoking this method with no arguments returns the translation table which was 
  527.    * registered with no language key. Invoking it with a language key returns the 
  528.    * related translation table. 
  529.    * 
  530.    * @param {string} key A language key. 
  531.    * @param {object} translationTable A plain old JavaScript object that represents a translation table. 
  532.    * 
  533.    */  
  534.   var translations = function (langKey, translationTable) {  
  535.   
  536.     if (!langKey && !translationTable) {  
  537.       return $translationTable;  
  538.     }  
  539.   
  540.     if (langKey && !translationTable) {  
  541.       if (angular.isString(langKey)) {  
  542.         return $translationTable[langKey];  
  543.       }  
  544.     } else {  
  545.       if (!angular.isObject($translationTable[langKey])) {  
  546.         $translationTable[langKey] = {};  
  547.       }  
  548.       angular.extend($translationTable[langKey], flatObject(translationTable));  
  549.     }  
  550.     return this;  
  551.   };  
  552.   
  553.   this.translations = translations;  
  554.   
  555.   /** 
  556.    * @ngdoc function 
  557.    * @name translate.$translateProvider#cloakClassName 
  558.    * @methodOf translate.$translateProvider 
  559.    * 
  560.    * @description 
  561.    * 
  562.    * Let's you change the class name for `translate-cloak` directive. 
  563.    * Default class name is `translate-cloak`. 
  564.    * 
  565.    * @param {string} name translate-cloak class name 
  566.    */  
  567.   this.cloakClassName = function (name) {  
  568.     if (!name) {  
  569.       return $cloakClassName;  
  570.     }  
  571.     $cloakClassName = name;  
  572.     return this;  
  573.   };  
  574.   
  575.   /** 
  576.    * @name flatObject 
  577.    * @private 
  578.    * 
  579.    * @description 
  580.    * Flats an object. This function is used to flatten given translation data with 
  581.    * namespaces, so they are later accessible via dot notation. 
  582.    */  
  583.   var flatObject = function (data, path, result, prevKey) {  
  584.     var key, keyWithPath, keyWithShortPath, val;  
  585.   
  586.     if (!path) {  
  587.       path = [];  
  588.     }  
  589.     if (!result) {  
  590.       result = {};  
  591.     }  
  592.     for (key in data) {  
  593.       if (!Object.prototype.hasOwnProperty.call(data, key)) {  
  594.         continue;  
  595.       }  
  596.       val = data[key];  
  597.       if (angular.isObject(val)) {  
  598.         flatObject(val, path.concat(key), result, key);  
  599.       } else {  
  600.         keyWithPath = path.length ? ('' + path.join(NESTED_OBJECT_DELIMITER) + NESTED_OBJECT_DELIMITER + key) : key;  
  601.         if(path.length && key === prevKey){  
  602.           // Create shortcut path (foo.bar == foo.bar.bar)  
  603.           keyWithShortPath = '' + path.join(NESTED_OBJECT_DELIMITER);  
  604.           // Link it to original path  
  605.           result[keyWithShortPath] = '@:' + keyWithPath;  
  606.         }  
  607.         result[keyWithPath] = val;  
  608.       }  
  609.     }  
  610.     return result;  
  611.   };  
  612.   flatObject.displayName = 'flatObject';  
  613.   
  614.   /** 
  615.    * @ngdoc function 
  616.    * @name translate.$translateProvider#addInterpolation 
  617.    * @methodOf translate.$translateProvider 
  618.    * 
  619.    * @description 
  620.    * Adds interpolation services to angular-translate, so it can manage them. 
  621.    * 
  622.    * @param {object} factory Interpolation service factory 
  623.    */  
  624.   this.addInterpolation = function (factory) {  
  625.     $interpolatorFactories.push(factory);  
  626.     return this;  
  627.   };  
  628.   
  629.   /** 
  630.    * @ngdoc function 
  631.    * @name translate.$translateProvider#useMessageFormatInterpolation 
  632.    * @methodOf translate.$translateProvider 
  633.    * 
  634.    * @description 
  635.    * Tells angular-translate to use interpolation functionality of messageformat.js. 
  636.    * This is useful when having high level pluralization and gender selection. 
  637.    */  
  638.   this.useMessageFormatInterpolation = function () {  
  639.     return this.useInterpolation('$translateMessageFormatInterpolation');  
  640.   };  
  641.   
  642.   /** 
  643.    * @ngdoc function 
  644.    * @name translate.$translateProvider#useInterpolation 
  645.    * @methodOf translate.$translateProvider 
  646.    * 
  647.    * @description 
  648.    * Tells angular-translate which interpolation style to use as default, application-wide. 
  649.    * Simply  a factory/service name. The interpolation service has to implement 
  650.    * the correct interface. 
  651.    * 
  652.    * @param {string} factory Interpolation service name. 
  653.    */  
  654.   this.useInterpolation = function (factory) {  
  655.     $interpolationFactory = factory;  
  656.     return this;  
  657.   };  
  658.   
  659.   /** 
  660.    * @ngdoc function 
  661.    * @name translate.$translateProvider#useSanitizeStrategy 
  662.    * @methodOf translate.$translateProvider 
  663.    * 
  664.    * @description 
  665.    * Simply sets a sanitation strategy type. 
  666.    * 
  667.    * @param {string} value Strategy type. 
  668.    */  
  669.   this.useSanitizeValueStrategy = function (value) {  
  670.     $translateSanitizationProvider.useStrategy(value);  
  671.     return this;  
  672.   };  
  673.   
  674.  /** 
  675.    * @ngdoc function 
  676.    * @name translate.$translateProvider#preferredLanguage 
  677.    * @methodOf translate.$translateProvider 
  678.    * 
  679.    * @description 
  680.    * Tells the module which of the registered translation tables to use for translation 
  681.    * at initial startup by ing a language key. Similar to `$translateProvider#use` 
  682.    * only that it says which language to **prefer**. 
  683.    * 
  684.    * @param {string} langKey A language key. 
  685.    * 
  686.    */  
  687.   this.preferredLanguage = function(langKey) {  
  688.     setupPreferredLanguage(langKey);  
  689.     return this;  
  690.   
  691.   };  
  692.   var setupPreferredLanguage = function (langKey) {  
  693.     if (langKey) {  
  694.       $preferredLanguage = langKey;  
  695.     }  
  696.     return $preferredLanguage;  
  697.   };  
  698.   /** 
  699.    * @ngdoc function 
  700.    * @name translate.$translateProvider#translationNotFoundIndicator 
  701.    * @methodOf translate.$translateProvider 
  702.    * 
  703.    * @description 
  704.    * Sets an indicator which is used when a translation isn't found. E.g. when 
  705.    * setting the indicator as 'X' and one tries to translate a translation id 
  706.    * called `NOT_FOUND`, this will result in `X NOT_FOUND X`. 
  707.    * 
  708.    * Internally this methods sets a left indicator and a right indicator using 
  709.    * `$translateProvider.translationNotFoundIndicatorLeft()` and 
  710.    * `$translateProvider.translationNotFoundIndicatorRight()`. 
  711.    * 
  712.    * **Note**: These methods automatically add a whitespace between the indicators 
  713.    * and the translation id. 
  714.    * 
  715.    * @param {string} indicator An indicator, could be any string. 
  716.    */  
  717.   this.translationNotFoundIndicator = function (indicator) {  
  718.     this.translationNotFoundIndicatorLeft(indicator);  
  719.     this.translationNotFoundIndicatorRight(indicator);  
  720.     return this;  
  721.   };  
  722.   
  723.   /** 
  724.    * ngdoc function 
  725.    * @name translate.$translateProvider#translationNotFoundIndicatorLeft 
  726.    * @methodOf translate.$translateProvider 
  727.    * 
  728.    * @description 
  729.    * Sets an indicator which is used when a translation isn't found left to the 
  730.    * translation id. 
  731.    * 
  732.    * @param {string} indicator An indicator. 
  733.    */  
  734.   this.translationNotFoundIndicatorLeft = function (indicator) {  
  735.     if (!indicator) {  
  736.       return $notFoundIndicatorLeft;  
  737.     }  
  738.     $notFoundIndicatorLeft = indicator;  
  739.     return this;  
  740.   };  
  741.   
  742.   /** 
  743.    * ngdoc function 
  744.    * @name translate.$translateProvider#translationNotFoundIndicatorLeft 
  745.    * @methodOf translate.$translateProvider 
  746.    * 
  747.    * @description 
  748.    * Sets an indicator which is used when a translation isn't found right to the 
  749.    * translation id. 
  750.    * 
  751.    * @param {string} indicator An indicator. 
  752.    */  
  753.   this.translationNotFoundIndicatorRight = function (indicator) {  
  754.     if (!indicator) {  
  755.       return $notFoundIndicatorRight;  
  756.     }  
  757.     $notFoundIndicatorRight = indicator;  
  758.     return this;  
  759.   };  
  760.   
  761.   /** 
  762.    * @ngdoc function 
  763.    * @name translate.$translateProvider#fallbackLanguage 
  764.    * @methodOf translate.$translateProvider 
  765.    * 
  766.    * @description 
  767.    * Tells the module which of the registered translation tables to use when missing translations 
  768.    * at initial startup by ing a language key. Similar to `$translateProvider#use` 
  769.    * only that it says which language to **fallback**. 
  770.    * 
  771.    * @param {string||array} langKey A language key. 
  772.    * 
  773.    */  
  774.   this.fallbackLanguage = function (langKey) {  
  775.     fallbackStack(langKey);  
  776.     return this;  
  777.   };  
  778.   
  779.   var fallbackStack = function (langKey) {  
  780.     if (langKey) {  
  781.       if (angular.isString(langKey)) {  
  782.         $fallbackWasString = true;  
  783.         $fallbackLanguage = [ langKey ];  
  784.       } else if (angular.isArray(langKey)) {  
  785.         $fallbackWasString = false;  
  786.         $fallbackLanguage = langKey;  
  787.       }  
  788.       if (angular.isString($preferredLanguage)  && indexOf($fallbackLanguage, $preferredLanguage) < 0) {  
  789.         $fallbackLanguage.push($preferredLanguage);  
  790.       }  
  791.   
  792.       return this;  
  793.     } else {  
  794.       if ($fallbackWasString) {  
  795.         return $fallbackLanguage[0];  
  796.       } else {  
  797.         return $fallbackLanguage;  
  798.       }  
  799.     }  
  800.   };  
  801.   
  802.   /** 
  803.    * @ngdoc function 
  804.    * @name translate.$translateProvider#use 
  805.    * @methodOf translate.$translateProvider 
  806.    * 
  807.    * @description 
  808.    * Set which translation table to use for translation by given language key. When 
  809.    * trying to 'use' a language which isn't provided, it'll throw an error. 
  810.    * 
  811.    * You actually don't have to use this method since `$translateProvider#preferredLanguage` 
  812.    * does the job too. 
  813.    * 
  814.    * @param {string} langKey A language key. 
  815.    */  
  816.   this.use = function (langKey) {  
  817.     if (langKey) {  
  818.       if (!$translationTable[langKey] && (!$loaderFactory)) {  
  819.         // only throw an error, when not loading translation data asynchronously  
  820.         throw new Error('$translateProvider couldn\'t find translationTable for langKey: \'' + langKey + '\'');  
  821.       }  
  822.       $uses = langKey;  
  823.       return this;  
  824.     }  
  825.     return $uses;  
  826.   };  
  827.   
  828.  /** 
  829.    * @ngdoc function 
  830.    * @name translate.$translateProvider#storageKey 
  831.    * @methodOf translate.$translateProvider 
  832.    * 
  833.    * @description 
  834.    * Tells the module which key must represent the choosed language by a user in the storage. 
  835.    * 
  836.    * @param {string} key A key for the storage. 
  837.    */  
  838.   var storageKey = function(key) {  
  839.     if (!key) {  
  840.       if ($storagePrefix) {  
  841.         return $storagePrefix + $storageKey;  
  842.       }  
  843.       return $storageKey;  
  844.     }  
  845.     $storageKey = key;  
  846.     return this;  
  847.   };  
  848.   
  849.   this.storageKey = storageKey;  
  850.   
  851.   /** 
  852.    * @ngdoc function 
  853.    * @name translate.$translateProvider#useUrlLoader 
  854.    * @methodOf translate.$translateProvider 
  855.    * 
  856.    * @description 
  857.    * Tells angular-translate to use `$translateUrlLoader` extension service as loader. 
  858.    * 
  859.    * @param {string} url Url 
  860.    * @param {Object=} options Optional configuration object 
  861.    */  
  862.   this.useUrlLoader = function (url, options) {  
  863.     return this.useLoader('$translateUrlLoader', angular.extend({ url: url }, options));  
  864.   };  
  865.   
  866.   /** 
  867.    * @ngdoc function 
  868.    * @name translate.$translateProvider#useStaticFilesLoader 
  869.    * @methodOf translate.$translateProvider 
  870.    * 
  871.    * @description 
  872.    * Tells angular-translate to use `$translateStaticFilesLoader` extension service as loader. 
  873.    * 
  874.    * @param {Object=} options Optional configuration object 
  875.    */  
  876.   this.useStaticFilesLoader = function (options) {  
  877.     return this.useLoader('$translateStaticFilesLoader', options);  
  878.   };  
  879.   
  880.   /** 
  881.    * @ngdoc function 
  882.    * @name translate.$translateProvider#useLoader 
  883.    * @methodOf translate.$translateProvider 
  884.    * 
  885.    * @description 
  886.    * Tells angular-translate to use any other service as loader. 
  887.    * 
  888.    * @param {string} loaderFactory Factory name to use 
  889.    * @param {Object=} options Optional configuration object 
  890.    */  
  891.   this.useLoader = function (loaderFactory, options) {  
  892.     $loaderFactory = loaderFactory;  
  893.     $loaderOptions = options || {};  
  894.     return this;  
  895.   };  
  896.   
  897.   /** 
  898.    * @ngdoc function 
  899.    * @name translate.$translateProvider#useLocalStorage 
  900.    * @methodOf translate.$translateProvider 
  901.    * 
  902.    * @description 
  903.    * Tells angular-translate to use `$translateLocalStorage` service as storage layer. 
  904.    * 
  905.    */  
  906.   this.useLocalStorage = function () {  
  907.     return this.useStorage('$translateLocalStorage');  
  908.   };  
  909.   
  910.   /** 
  911.    * @ngdoc function 
  912.    * @name translate.$translateProvider#useCookieStorage 
  913.    * @methodOf translate.$translateProvider 
  914.    * 
  915.    * @description 
  916.    * Tells angular-translate to use `$translateCookieStorage` service as storage layer. 
  917.    */  
  918.   this.useCookieStorage = function () {  
  919.     return this.useStorage('$translateCookieStorage');  
  920.   };  
  921.   
  922.   /** 
  923.    * @ngdoc function 
  924.    * @name translate.$translateProvider#useStorage 
  925.    * @methodOf translate.$translateProvider 
  926.    * 
  927.    * @description 
  928.    * Tells angular-translate to use custom service as storage layer. 
  929.    */  
  930.   this.useStorage = function (storageFactory) {  
  931.     $storageFactory = storageFactory;  
  932.     return this;  
  933.   };  
  934.   
  935.   /** 
  936.    * @ngdoc function 
  937.    * @name translate.$translateProvider#storagePrefix 
  938.    * @methodOf translate.$translateProvider 
  939.    * 
  940.    * @description 
  941.    * Sets prefix for storage key. 
  942.    * 
  943.    * @param {string} prefix Storage key prefix 
  944.    */  
  945.   this.storagePrefix = function (prefix) {  
  946.     if (!prefix) {  
  947.       return prefix;  
  948.     }  
  949.     $storagePrefix = prefix;  
  950.     return this;  
  951.   };  
  952.   
  953.   /** 
  954.    * @ngdoc function 
  955.    * @name translate.$translateProvider#useMissingTranslationHandlerLog 
  956.    * @methodOf translate.$translateProvider 
  957.    * 
  958.    * @description 
  959.    * Tells angular-translate to use built-in log handler when trying to translate 
  960.    * a translation Id which doesn't exist. 
  961.    * 
  962.    * This is actually a shortcut method for `useMissingTranslationHandler()`. 
  963.    * 
  964.    */  
  965.   this.useMissingTranslationHandlerLog = function () {  
  966.     return this.useMissingTranslationHandler('$translateMissingTranslationHandlerLog');  
  967.   };  
  968.   
  969.   /** 
  970.    * @ngdoc function 
  971.    * @name translate.$translateProvider#useMissingTranslationHandler 
  972.    * @methodOf translate.$translateProvider 
  973.    * 
  974.    * @description 
  975.    * Expects a factory name which later gets instantiated with `$injector`. 
  976.    * This method can be used to tell angular-translate to use a custom 
  977.    * missingTranslationHandler. Just build a factory which returns a function 
  978.    * and expects a translation id as argument. 
  979.    * 
  980.    * Example: 
  981.    * <pre> 
  982.    *  app.config(function ($translateProvider) { 
  983.    *    $translateProvider.useMissingTranslationHandler('customHandler'); 
  984.    *  }); 
  985.    * 
  986.    *  app.factory('customHandler', function (dep1, dep2) { 
  987.    *    return function (translationId) { 
  988.    *      // something with translationId and dep1 and dep2 
  989.    *    }; 
  990.    *  }); 
  991.    * </pre> 
  992.    * 
  993.    * @param {string} factory Factory name 
  994.    */  
  995.   this.useMissingTranslationHandler = function (factory) {  
  996.     $missingTranslationHandlerFactory = factory;  
  997.     return this;  
  998.   };  
  999.   
  1000.   /** 
  1001.    * @ngdoc function 
  1002.    * @name translate.$translateProvider#usePostCompiling 
  1003.    * @methodOf translate.$translateProvider 
  1004.    * 
  1005.    * @description 
  1006.    * If post compiling is enabled, all translated values will be processed 
  1007.    * again with AngularJS' $compile. 
  1008.    * 
  1009.    * Example: 
  1010.    * <pre> 
  1011.    *  app.config(function ($translateProvider) { 
  1012.    *    $translateProvider.usePostCompiling(true); 
  1013.    *  }); 
  1014.    * </pre> 
  1015.    * 
  1016.    * @param {string} factory Factory name 
  1017.    */  
  1018.   this.usePostCompiling = function (value) {  
  1019.     $postCompilingEnabled = !(!value);  
  1020.     return this;  
  1021.   };  
  1022.   
  1023.   /** 
  1024.    * @ngdoc function 
  1025.    * @name translate.$translateProvider#forceAsyncReload 
  1026.    * @methodOf translate.$translateProvider 
  1027.    * 
  1028.    * @description 
  1029.    * If force async reload is enabled, async loader will always be called 
  1030.    * even if $translationTable already contains the language key, adding 
  1031.    * possible new entries to the $translationTable. 
  1032.    * 
  1033.    * Example: 
  1034.    * <pre> 
  1035.    *  app.config(function ($translateProvider) { 
  1036.    *    $translateProvider.forceAsyncReload(true); 
  1037.    *  }); 
  1038.    * </pre> 
  1039.    * 
  1040.    * @param {boolean} value - valid values are true or false 
  1041.    */  
  1042.   this.forceAsyncReload = function (value) {  
  1043.     $forceAsyncReloadEnabled = !(!value);  
  1044.     return this;  
  1045.   };  
  1046.   
  1047.   /** 
  1048.    * @ngdoc function 
  1049.    * @name translate.$translateProvider#uniformLanguageTag 
  1050.    * @methodOf translate.$translateProvider 
  1051.    * 
  1052.    * @description 
  1053.    * Tells angular-translate which language tag should be used as a result when determining 
  1054.    * the current browser language. 
  1055.    * 
  1056.    * This setting must be set before invoking {@link translate.$translateProvider#methods_determinePreferredLanguage determinePreferredLanguage()}. 
  1057.    * 
  1058.    * <pre> 
  1059.    * $translateProvider 
  1060.    *   .uniformLanguageTag('bcp47') 
  1061.    *   .determinePreferredLanguage() 
  1062.    * </pre> 
  1063.    * 
  1064.    * The resolver currently supports: 
  1065.    * * default 
  1066.    *     (traditionally: hyphens will be converted into underscores, i.e. en-US => en_US) 
  1067.    *     en-US => en_US 
  1068.    *     en_US => en_US 
  1069.    *     en-us => en_us 
  1070.    * * java 
  1071.    *     like default, but the second part will be always in uppercase 
  1072.    *     en-US => en_US 
  1073.    *     en_US => en_US 
  1074.    *     en-us => en_US 
  1075.    * * BCP 47 (RFC 4646 & 4647) 
  1076.    *     en-US => en-US 
  1077.    *     en_US => en-US 
  1078.    *     en-us => en-US 
  1079.    * 
  1080.    * See also: 
  1081.    * * http://en.wikipedia.org/wiki/IETF_language_tag 
  1082.    * * http://www.w3.org/International/core/langtags/ 
  1083.    * * http://tools.ietf.org/html/bcp47 
  1084.    * 
  1085.    * @param {string|object} options - options (or standard) 
  1086.    * @param {string} options.standard - valid values are 'default', 'bcp47', 'java' 
  1087.    */  
  1088.   this.uniformLanguageTag = function (options) {  
  1089.   
  1090.     if (!options) {  
  1091.       options = {};  
  1092.     } else if (angular.isString(options)) {  
  1093.       options = {  
  1094.         standard: options  
  1095.       };  
  1096.     }  
  1097.   
  1098.     uniformLanguageTagResolver = options.standard;  
  1099.   
  1100.     return this;  
  1101.   };  
  1102.   
  1103.   /** 
  1104.    * @ngdoc function 
  1105.    * @name translate.$translateProvider#determinePreferredLanguage 
  1106.    * @methodOf translate.$translateProvider 
  1107.    * 
  1108.    * @description 
  1109.    * Tells angular-translate to try to determine on its own which language key 
  1110.    * to set as preferred language. When `fn` is given, angular-translate uses it 
  1111.    * to determine a language key, otherwise it uses the built-in `getLocale()` 
  1112.    * method. 
  1113.    * 
  1114.    * The `getLocale()` returns a language key in the format `[lang]_[country]` or 
  1115.    * `[lang]` depending on what the browser provides. 
  1116.    * 
  1117.    * Use this method at your own risk, since not all browsers return a valid 
  1118.    * locale (see {@link translate.$translateProvider#methods_uniformLanguageTag uniformLanguageTag()}). 
  1119.    * 
  1120.    * @param {Function=} fn Function to determine a browser's locale 
  1121.    */  
  1122.   this.determinePreferredLanguage = function (fn) {  
  1123.   
  1124.     var locale = (fn && angular.isFunction(fn)) ? fn() : getLocale();  
  1125.   
  1126.     if (!$availableLanguageKeys.length) {  
  1127.       $preferredLanguage = locale;  
  1128.     } else {  
  1129.       $preferredLanguage = negotiateLocale(locale);  
  1130.     }  
  1131.   
  1132.     return this;  
  1133.   };  
  1134.   
  1135.   /** 
  1136.    * @ngdoc function 
  1137.    * @name translate.$translateProvider#registerAvailableLanguageKeys 
  1138.    * @methodOf translate.$translateProvider 
  1139.    * 
  1140.    * @description 
  1141.    * Registers a set of language keys the app will work with. Use this method in 
  1142.    * combination with 
  1143.    * {@link translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}. 
  1144.    * When available languages keys are registered, angular-translate 
  1145.    * tries to find the best fitting language key depending on the browsers locale, 
  1146.    * considering your language key convention. 
  1147.    * 
  1148.    * @param {object} languageKeys Array of language keys the your app will use 
  1149.    * @param {object=} aliases Alias map. 
  1150.    */  
  1151.   this.registerAvailableLanguageKeys = function (languageKeys, aliases) {  
  1152.     if (languageKeys) {  
  1153.       $availableLanguageKeys = languageKeys;  
  1154.       if (aliases) {  
  1155.         $languageKeyAliases = aliases;  
  1156.       }  
  1157.       return this;  
  1158.     }  
  1159.     return $availableLanguageKeys;  
  1160.   };  
  1161.   
  1162.   /** 
  1163.    * @ngdoc function 
  1164.    * @name translate.$translateProvider#useLoaderCache 
  1165.    * @methodOf translate.$translateProvider 
  1166.    * 
  1167.    * @description 
  1168.    * Registers a cache for internal $http based loaders. 
  1169.    * {@link translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}. 
  1170.    * When false the cache will be disabled (default). When true or undefined 
  1171.    * the cache will be a default (see $cacheFactory). When an object it will 
  1172.    * be treat as a cache object itself: the usage is $http({cache: cache}) 
  1173.    * 
  1174.    * @param {object} cache boolean, string or cache-object 
  1175.    */  
  1176.   this.useLoaderCache = function (cache) {  
  1177.     if (cache === false) {  
  1178.       // disable cache  
  1179.       loaderCache = undefined;  
  1180.     } else if (cache === true) {  
  1181.       // enable cache using AJS defaults  
  1182.       loaderCache = true;  
  1183.     } else if (typeof(cache) === 'undefined') {  
  1184.       // enable cache using default  
  1185.       loaderCache = '$translationCache';  
  1186.     } else if (cache) {  
  1187.       // enable cache using given one (see $cacheFactory)  
  1188.       loaderCache = cache;  
  1189.     }  
  1190.     return this;  
  1191.   };  
  1192.   
  1193.   /** 
  1194.    * @ngdoc function 
  1195.    * @name translate.$translateProvider#directivePriority 
  1196.    * @methodOf translate.$translateProvider 
  1197.    * 
  1198.    * @description 
  1199.    * Sets the default priority of the translate directive. The standard value is `0`. 
  1200.    * Calling this function without an argument will return the current value. 
  1201.    * 
  1202.    * @param {number} priority for the translate-directive 
  1203.    */  
  1204.   this.directivePriority = function (priority) {  
  1205.     if (priority === undefined) {  
  1206.       // getter  
  1207.       return directivePriority;  
  1208.     } else {  
  1209.       // setter with chaining  
  1210.       directivePriority = priority;  
  1211.       return this;  
  1212.     }  
  1213.   };  
  1214.   
  1215.   /** 
  1216.    * @ngdoc function 
  1217.    * @name translate.$translateProvider#statefulFilter 
  1218.    * @methodOf translate.$translateProvider 
  1219.    * 
  1220.    * @description 
  1221.    * Since AngularJS 1.3, filters which are not stateless (depending at the scope) 
  1222.    * have to explicit define this behavior. 
  1223.    * Sets whether the translate filter should be stateful or stateless. The standard value is `true` 
  1224.    * meaning being stateful. 
  1225.    * Calling this function without an argument will return the current value. 
  1226.    * 
  1227.    * @param {boolean} state - defines the state of the filter 
  1228.    */  
  1229.   this.statefulFilter = function (state) {  
  1230.     if (state === undefined) {  
  1231.       // getter  
  1232.       return statefulFilter;  
  1233.     } else {  
  1234.       // setter with chaining  
  1235.       statefulFilter = state;  
  1236.       return this;  
  1237.     }  
  1238.   };  
  1239.   
  1240.   /** 
  1241.    * @ngdoc object 
  1242.    * @name translate.$translate 
  1243.    * @requires $interpolate 
  1244.    * @requires $log 
  1245.    * @requires $rootScope 
  1246.    * @requires $q 
  1247.    * 
  1248.    * @description 
  1249.    * The `$translate` service is the actual core of angular-translate. It expects a translation id 
  1250.    * and optional interpolate parameters to translate contents. 
  1251.    * 
  1252.    * <pre> 
  1253.    *  $translate('HEADLINE_TEXT').then(function (translation) { 
  1254.    *    $scope.translatedText = translation; 
  1255.    *  }); 
  1256.    * </pre> 
  1257.    * 
  1258.    * @param {string|array} translationId A token which represents a translation id 
  1259.    *                                     This can be optionally an array of translation ids which 
  1260.    *                                     results that the function returns an object where each key 
  1261.    *                                     is the translation id and the value the translation. 
  1262.    * @param {object=} interpolateParams An object hash for dynamic values 
  1263.    * @param {string} interpolationId The id of the interpolation to use 
  1264.    * @returns {object} promise 
  1265.    */  
  1266.   this.$get = [  
  1267.     '$log',  
  1268.     '$injector',  
  1269.     '$rootScope',  
  1270.     '$q',  
  1271.     function ($log, $injector, $rootScope, $q) {  
  1272.   
  1273.       var Storage,  
  1274.           defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'),  
  1275.           pendingLoader = false,  
  1276.           interpolatorHashMap = {},  
  1277.           langPromises = {},  
  1278.           fallbackIndex,  
  1279.           startFallbackIteration;  
  1280.   
  1281.       var $translate = function (translationId, interpolateParams, interpolationId, defaultTranslationText) {  
  1282.   
  1283.         // Duck detection: If the first argument is an array, a bunch of translations was requested.  
  1284.         // The result is an object.  
  1285.         if (angular.isArray(translationId)) {  
  1286.           // Inspired by Q.allSettled by Kris Kowal  
  1287.           // https://github.com/kriskowal/q/blob/b0fa72980717dc202ffc3cbf03b936e10ebbb9d7/q.js#L1553-1563  
  1288.           // This transforms all promises regardless resolved or rejected  
  1289.           var translateAll = function (translationIds) {  
  1290.             var results = {}; // storing the actual results  
  1291.             var promises = []; // promises to wait for  
  1292.             // Wraps the promise a) being always resolved and b) storing the link id->value  
  1293.             var translate = function (translationId) {  
  1294.               var deferred = $q.defer();  
  1295.               var regardless = function (value) {  
  1296.                 results[translationId] = value;  
  1297.                 deferred.resolve([translationId, value]);  
  1298.               };  
  1299.               // we don't care whether the promise was resolved or rejected; just store the values  
  1300.               $translate(translationId, interpolateParams, interpolationId, defaultTranslationText).then(regardless, regardless);  
  1301.               return deferred.promise;  
  1302.             };  
  1303.             for (var i = 0, c = translationIds.length; i < c; i++) {  
  1304.               promises.push(translate(translationIds[i]));  
  1305.             }  
  1306.             // wait for all (including storing to results)  
  1307.             return $q.all(promises).then(function () {  
  1308.               // return the results  
  1309.               return results;  
  1310.             });  
  1311.           };  
  1312.           return translateAll(translationId);  
  1313.         }  
  1314.   
  1315.         var deferred = $q.defer();  
  1316.   
  1317.         // trim off any whitespace  
  1318.         if (translationId) {  
  1319.           translationId = trim.apply(translationId);  
  1320.         }  
  1321.   
  1322.         var promiseToWaitFor = (function () {  
  1323.           var promise = $preferredLanguage ?  
  1324.             langPromises[$preferredLanguage] :  
  1325.             langPromises[$uses];  
  1326.   
  1327.           fallbackIndex = 0;  
  1328.   
  1329.           if ($storageFactory && !promise) {  
  1330.             // looks like there's no pending promise for $preferredLanguage or  
  1331.             // $uses. Maybe there's one pending for a language that comes from  
  1332.             // storage.  
  1333.             var langKey = Storage.get($storageKey);  
  1334.             promise = langPromises[langKey];  
  1335.   
  1336.             if ($fallbackLanguage && $fallbackLanguage.length) {  
  1337.                 var index = indexOf($fallbackLanguage, langKey);  
  1338.                 // maybe the language from storage is also defined as fallback language  
  1339.                 // we increase the fallback language index to not search in that language  
  1340.                 // as fallback, since it's probably the first used language  
  1341.                 // in that case the index starts after the first element  
  1342.                 fallbackIndex = (index === 0) ? 1 : 0;  
  1343.   
  1344.                 // but we can make sure to ALWAYS fallback to preferred language at least  
  1345.                 if (indexOf($fallbackLanguage, $preferredLanguage) < 0) {  
  1346.                   $fallbackLanguage.push($preferredLanguage);  
  1347.                 }  
  1348.             }  
  1349.           }  
  1350.           return promise;  
  1351.         }());  
  1352.   
  1353.         if (!promiseToWaitFor) {  
  1354.           // no promise to wait for? okay. Then there's no loader registered  
  1355.           // nor is a one pending for language that comes from storage.  
  1356.           // We can just translate.  
  1357.           determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText).then(deferred.resolve, deferred.reject);  
  1358.         } else {  
  1359.           var promiseResolved = function () {  
  1360.             determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText).then(deferred.resolve, deferred.reject);  
  1361.           };  
  1362.           promiseResolved.displayName = 'promiseResolved';  
  1363.   
  1364.           promiseToWaitFor['finally'](promiseResolved, deferred.reject);  
  1365.         }  
  1366.         return deferred.promise;  
  1367.       };  
  1368.   
  1369.       /** 
  1370.        * @name applyNotFoundIndicators 
  1371.        * @private 
  1372.        * 
  1373.        * @description 
  1374.        * Applies not fount indicators to given translation id, if needed. 
  1375.        * This function gets only executed, if a translation id doesn't exist, 
  1376.        * which is why a translation id is expected as argument. 
  1377.        * 
  1378.        * @param {string} translationId Translation id. 
  1379.        * @returns {string} Same as given translation id but applied with not found 
  1380.        * indicators. 
  1381.        */  
  1382.       var applyNotFoundIndicators = function (translationId) {  
  1383.         // applying notFoundIndicators  
  1384.         if ($notFoundIndicatorLeft) {  
  1385.           translationId = [$notFoundIndicatorLeft, translationId].join(' ');  
  1386.         }  
  1387.         if ($notFoundIndicatorRight) {  
  1388.           translationId = [translationId, $notFoundIndicatorRight].join(' ');  
  1389.         }  
  1390.         return translationId;  
  1391.       };  
  1392.   
  1393.       /** 
  1394.        * @name useLanguage 
  1395.        * @private 
  1396.        * 
  1397.        * @description 
  1398.        * Makes actual use of a language by setting a given language key as used 
  1399.        * language and informs registered interpolators to also use the given 
  1400.        * key as locale. 
  1401.        * 
  1402.        * @param {key} Locale key. 
  1403.        */  
  1404.       var useLanguage = function (key) {  
  1405.         $uses = key;  
  1406.         $rootScope.$emit('$translateChangeSuccess', {language: key});  
  1407.   
  1408.         if ($storageFactory) {  
  1409.           Storage.put($translate.storageKey(), $uses);  
  1410.         }  
  1411.         // inform default interpolator  
  1412.         defaultInterpolator.setLocale($uses);  
  1413.   
  1414.         var eachInterpolator = function (interpolator, id) {  
  1415.           interpolatorHashMap[id].setLocale($uses);  
  1416.         };  
  1417.         eachInterpolator.displayName = 'eachInterpolatorLocaleSetter';  
  1418.   
  1419.         // inform all others too!  
  1420.         angular.forEach(interpolatorHashMap, eachInterpolator);  
  1421.         $rootScope.$emit('$translateChangeEnd', {language: key});  
  1422.       };  
  1423.   
  1424.       /** 
  1425.        * @name loadAsync 
  1426.        * @private 
  1427.        * 
  1428.        * @description 
  1429.        * Kicks of registered async loader using `$injector` and applies existing 
  1430.        * loader options. When resolved, it updates translation tables accordingly 
  1431.        * or rejects with given language key. 
  1432.        * 
  1433.        * @param {string} key Language key. 
  1434.        * @return {Promise} A promise. 
  1435.        */  
  1436.       var loadAsync = function (key) {  
  1437.         if (!key) {  
  1438.           throw 'No language key specified for loading.';  
  1439.         }  
  1440.   
  1441.         var deferred = $q.defer();  
  1442.   
  1443.         $rootScope.$emit('$translateLoadingStart', {language: key});  
  1444.         pendingLoader = true;  
  1445.   
  1446.         var cache = loaderCache;  
  1447.         if (typeof(cache) === 'string') {  
  1448.           // getting on-demand instance of loader  
  1449.           cache = $injector.get(cache);  
  1450.         }  
  1451.   
  1452.         var loaderOptions = angular.extend({}, $loaderOptions, {  
  1453.           key: key,  
  1454.           $http: angular.extend({}, {  
  1455.             cache: cache  
  1456.           }, $loaderOptions.$http)  
  1457.         });  
  1458.   
  1459.         var onLoaderSuccess = function (data) {  
  1460.           var translationTable = {};  
  1461.           $rootScope.$emit('$translateLoadingSuccess', {language: key});  
  1462.   
  1463.           if (angular.isArray(data)) {  
  1464.             angular.forEach(data, function (table) {  
  1465.               angular.extend(translationTable, flatObject(table));  
  1466.             });  
  1467.           } else {  
  1468.             angular.extend(translationTable, flatObject(data));  
  1469.           }  
  1470.           pendingLoader = false;  
  1471.           deferred.resolve({  
  1472.             key: key,  
  1473.             table: translationTable  
  1474.           });  
  1475.           $rootScope.$emit('$translateLoadingEnd', {language: key});  
  1476.         };  
  1477.         onLoaderSuccess.displayName = 'onLoaderSuccess';  
  1478.   
  1479.         var onLoaderError = function (key) {  
  1480.           $rootScope.$emit('$translateLoadingError', {language: key});  
  1481.           deferred.reject(key);  
  1482.           $rootScope.$emit('$translateLoadingEnd', {language: key});  
  1483.         };  
  1484.         onLoaderError.displayName = 'onLoaderError';  
  1485.   
  1486.         $injector.get($loaderFactory)(loaderOptions)  
  1487.           .then(onLoaderSuccess, onLoaderError);  
  1488.   
  1489.         return deferred.promise;  
  1490.       };  
  1491.   
  1492.       if ($storageFactory) {  
  1493.         Storage = $injector.get($storageFactory);  
  1494.   
  1495.         if (!Storage.get || !Storage.put) {  
  1496.           throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or put() method!');  
  1497.         }  
  1498.       }  
  1499.   
  1500.       // if we have additional interpolations that were added via  
  1501.       // $translateProvider.addInterpolation(), we have to map'em  
  1502.       if ($interpolatorFactories.length) {  
  1503.         var eachInterpolationFactory = function (interpolatorFactory) {  
  1504.           var interpolator = $injector.get(interpolatorFactory);  
  1505.           // setting initial locale for each interpolation service  
  1506.           interpolator.setLocale($preferredLanguage || $uses);  
  1507.           // make'em recognizable through id  
  1508.           interpolatorHashMap[interpolator.getInterpolationIdentifier()] = interpolator;  
  1509.         };  
  1510.         eachInterpolationFactory.displayName = 'interpolationFactoryAdder';  
  1511.   
  1512.         angular.forEach($interpolatorFactories, eachInterpolationFactory);  
  1513.       }  
  1514.   
  1515.       /** 
  1516.        * @name getTranslationTable 
  1517.        * @private 
  1518.        * 
  1519.        * @description 
  1520.        * Returns a promise that resolves to the translation table 
  1521.        * or is rejected if an error occurred. 
  1522.        * 
  1523.        * @param langKey 
  1524.        * @returns {Q.promise} 
  1525.        */  
  1526.       var getTranslationTable = function (langKey) {  
  1527.         var deferred = $q.defer();  
  1528.         if (Object.prototype.hasOwnProperty.call($translationTable, langKey)) {  
  1529.           deferred.resolve($translationTable[langKey]);  
  1530.         } else if (langPromises[langKey]) {  
  1531.           var onResolve = function (data) {  
  1532.             translations(data.key, data.table);  
  1533.             deferred.resolve(data.table);  
  1534.           };  
  1535.           onResolve.displayName = 'translationTableResolver';  
  1536.           langPromises[langKey].then(onResolve, deferred.reject);  
  1537.         } else {  
  1538.           deferred.reject();  
  1539.         }  
  1540.         return deferred.promise;  
  1541.       };  
  1542.   
  1543.       /** 
  1544.        * @name getFallbackTranslation 
  1545.        * @private 
  1546.        * 
  1547.        * @description 
  1548.        * Returns a promise that will resolve to the translation 
  1549.        * or be rejected if no translation was found for the language. 
  1550.        * This function is currently only used for fallback language translation. 
  1551.        * 
  1552.        * @param langKey The language to translate to. 
  1553.        * @param translationId 
  1554.        * @param interpolateParams 
  1555.        * @param Interpolator 
  1556.        * @returns {Q.promise} 
  1557.        */  
  1558.       var getFallbackTranslation = function (langKey, translationId, interpolateParams, Interpolator) {  
  1559.         var deferred = $q.defer();  
  1560.   
  1561.         var onResolve = function (translationTable) {  
  1562.           if (Object.prototype.hasOwnProperty.call(translationTable, translationId)) {  
  1563.             Interpolator.setLocale(langKey);  
  1564.             var translation = translationTable[translationId];  
  1565.             if (translation.substr(0, 2) === '@:') {  
  1566.               getFallbackTranslation(langKey, translation.substr(2), interpolateParams, Interpolator)  
  1567.                 .then(deferred.resolve, deferred.reject);  
  1568.             } else {  
  1569.               deferred.resolve(Interpolator.interpolate(translationTable[translationId], interpolateParams));  
  1570.             }  
  1571.             Interpolator.setLocale($uses);  
  1572.           } else {  
  1573.             deferred.reject();  
  1574.           }  
  1575.         };  
  1576.         onResolve.displayName = 'fallbackTranslationResolver';  
  1577.   
  1578.         getTranslationTable(langKey).then(onResolve, deferred.reject);  
  1579.   
  1580.         return deferred.promise;  
  1581.       };  
  1582.   
  1583.       /** 
  1584.        * @name getFallbackTranslationInstant 
  1585.        * @private 
  1586.        * 
  1587.        * @description 
  1588.        * Returns a translation 
  1589.        * This function is currently only used for fallback language translation. 
  1590.        * 
  1591.        * @param langKey The language to translate to. 
  1592.        * @param translationId 
  1593.        * @param interpolateParams 
  1594.        * @param Interpolator 
  1595.        * @returns {string} translation 
  1596.        */  
  1597.       var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator) {  
  1598.         var result, translationTable = $translationTable[langKey];  
  1599.   
  1600.         if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId)) {  
  1601.           Interpolator.setLocale(langKey);  
  1602.           result = Interpolator.interpolate(translationTable[translationId], interpolateParams);  
  1603.           if (result.substr(0, 2) === '@:') {  
  1604.             return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator);  
  1605.           }  
  1606.           Interpolator.setLocale($uses);  
  1607.         }  
  1608.   
  1609.         return result;  
  1610.       };  
  1611.   
  1612.   
  1613.       /** 
  1614.        * @name translateByHandler 
  1615.        * @private 
  1616.        * 
  1617.        * Translate by missing translation handler. 
  1618.        * 
  1619.        * @param translationId 
  1620.        * @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is 
  1621.        * absent 
  1622.        */  
  1623.       var translateByHandler = function (translationId, interpolateParams) {  
  1624.         // If we have a handler factory - we might also call it here to determine if it provides  
  1625.         // a default text for a translationid that can't be found anywhere in our tables  
  1626.         if ($missingTranslationHandlerFactory) {  
  1627.           var resultString = $injector.get($missingTranslationHandlerFactory)(translationId, $uses, interpolateParams);  
  1628.           if (resultString !== undefined) {  
  1629.             return resultString;  
  1630.           } else {  
  1631.             return translationId;  
  1632.           }  
  1633.         } else {  
  1634.           return translationId;  
  1635.         }  
  1636.       };  
  1637.   
  1638.       /** 
  1639.        * @name resolveForFallbackLanguage 
  1640.        * @private 
  1641.        * 
  1642.        * Recursive helper function for fallbackTranslation that will sequentially look 
  1643.        * for a translation in the fallbackLanguages starting with fallbackLanguageIndex. 
  1644.        * 
  1645.        * @param fallbackLanguageIndex 
  1646.        * @param translationId 
  1647.        * @param interpolateParams 
  1648.        * @param Interpolator 
  1649.        * @returns {Q.promise} Promise that will resolve to the translation. 
  1650.        */  
  1651.       var resolveForFallbackLanguage = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, defaultTranslationText) {  
  1652.         var deferred = $q.defer();  
  1653.   
  1654.         if (fallbackLanguageIndex < $fallbackLanguage.length) {  
  1655.           var langKey = $fallbackLanguage[fallbackLanguageIndex];  
  1656.           getFallbackTranslation(langKey, translationId, interpolateParams, Interpolator).then(  
  1657.             deferred.resolve,  
  1658.             function () {  
  1659.               // Look in the next fallback language for a translation.  
  1660.               // It delays the resolving by ing another promise to resolve.  
  1661.               resolveForFallbackLanguage(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator, defaultTranslationText).then(deferred.resolve);  
  1662.             }  
  1663.           );  
  1664.         } else {  
  1665.           // No translation found in any fallback language  
  1666.           // if a default translation text is set in the directive, then return this as a result  
  1667.           if (defaultTranslationText) {  
  1668.             deferred.resolve(defaultTranslationText);  
  1669.           } else {  
  1670.             // if no default translation is set and an error handler is defined, send it to the handler  
  1671.             // and then return the result  
  1672.             deferred.resolve(translateByHandler(translationId, interpolateParams));  
  1673.           }  
  1674.         }  
  1675.         return deferred.promise;  
  1676.       };  
  1677.   
  1678.       /** 
  1679.        * @name resolveForFallbackLanguageInstant 
  1680.        * @private 
  1681.        * 
  1682.        * Recursive helper function for fallbackTranslation that will sequentially look 
  1683.        * for a translation in the fallbackLanguages starting with fallbackLanguageIndex. 
  1684.        * 
  1685.        * @param fallbackLanguageIndex 
  1686.        * @param translationId 
  1687.        * @param interpolateParams 
  1688.        * @param Interpolator 
  1689.        * @returns {string} translation 
  1690.        */  
  1691.       var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator) {  
  1692.         var result;  
  1693.   
  1694.         if (fallbackLanguageIndex < $fallbackLanguage.length) {  
  1695.           var langKey = $fallbackLanguage[fallbackLanguageIndex];  
  1696.           result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator);  
  1697.           if (!result) {  
  1698.             result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator);  
  1699.           }  
  1700.         }  
  1701.         return result;  
  1702.       };  
  1703.   
  1704.       /** 
  1705.        * Translates with the usage of the fallback languages. 
  1706.        * 
  1707.        * @param translationId 
  1708.        * @param interpolateParams 
  1709.        * @param Interpolator 
  1710.        * @returns {Q.promise} Promise, that resolves to the translation. 
  1711.        */  
  1712.       var fallbackTranslation = function (translationId, interpolateParams, Interpolator, defaultTranslationText) {  
  1713.         // Start with the fallbackLanguage with index 0  
  1714.         return resolveForFallbackLanguage((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, defaultTranslationText);  
  1715.       };  
  1716.   
  1717.       /** 
  1718.        * Translates with the usage of the fallback languages. 
  1719.        * 
  1720.        * @param translationId 
  1721.        * @param interpolateParams 
  1722.        * @param Interpolator 
  1723.        * @returns {String} translation 
  1724.        */  
  1725.       var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator) {  
  1726.         // Start with the fallbackLanguage with index 0  
  1727.         return resolveForFallbackLanguageInstant((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator);  
  1728.       };  
  1729.   
  1730.       var determineTranslation = function (translationId, interpolateParams, interpolationId, defaultTranslationText) {  
  1731.   
  1732.         var deferred = $q.defer();  
  1733.   
  1734.         var table = $uses ? $translationTable[$uses] : $translationTable,  
  1735.             Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator;  
  1736.   
  1737.         // if the translation id exists, we can just interpolate it  
  1738.         if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {  
  1739.           var translation = table[translationId];  
  1740.   
  1741.           // If using link, rerun $translate with linked translationId and return it  
  1742.           if (translation.substr(0, 2) === '@:') {  
  1743.   
  1744.             $translate(translation.substr(2), interpolateParams, interpolationId, defaultTranslationText)  
  1745.               .then(deferred.resolve, deferred.reject);  
  1746.           } else {  
  1747.             deferred.resolve(Interpolator.interpolate(translation, interpolateParams));  
  1748.           }  
  1749.         } else {  
  1750.           var missingTranslationHandlerTranslation;  
  1751.           // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise  
  1752.           if ($missingTranslationHandlerFactory && !pendingLoader) {  
  1753.             missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams);  
  1754.           }  
  1755.   
  1756.           // since we couldn't translate the inital requested translation id,  
  1757.           // we try it now with one or more fallback languages, if fallback language(s) is  
  1758.           // configured.  
  1759.           if ($uses && $fallbackLanguage && $fallbackLanguage.length) {  
  1760.             fallbackTranslation(translationId, interpolateParams, Interpolator, defaultTranslationText)  
  1761.                 .then(function (translation) {  
  1762.                   deferred.resolve(translation);  
  1763.                 }, function (_translationId) {  
  1764.                   deferred.reject(applyNotFoundIndicators(_translationId));  
  1765.                 });  
  1766.           } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {  
  1767.             // looks like the requested translation id doesn't exists.  
  1768.             // Now, if there is a registered handler for missing translations and no  
  1769.             // asyncLoader is pending, we execute the handler  
  1770.             if (defaultTranslationText) {  
  1771.               deferred.resolve(defaultTranslationText);  
  1772.               } else {  
  1773.                 deferred.resolve(missingTranslationHandlerTranslation);  
  1774.               }  
  1775.           } else {  
  1776.             if (defaultTranslationText) {  
  1777.               deferred.resolve(defaultTranslationText);  
  1778.             } else {  
  1779.               deferred.reject(applyNotFoundIndicators(translationId));  
  1780.             }  
  1781.           }  
  1782.         }  
  1783.         return deferred.promise;  
  1784.       };  
  1785.   
  1786.       var determineTranslationInstant = function (translationId, interpolateParams, interpolationId) {  
  1787.   
  1788.         var result, table = $uses ? $translationTable[$uses] : $translationTable,  
  1789.             Interpolator = defaultInterpolator;  
  1790.   
  1791.         // if the interpolation id exists use custom interpolator  
  1792.         if (interpolatorHashMap && Object.prototype.hasOwnProperty.call(interpolatorHashMap, interpolationId)) {  
  1793.           Interpolator = interpolatorHashMap[interpolationId];  
  1794.         }  
  1795.   
  1796.         // if the translation id exists, we can just interpolate it  
  1797.         if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {  
  1798.           var translation = table[translationId];  
  1799.   
  1800.           // If using link, rerun $translate with linked translationId and return it  
  1801.           if (translation.substr(0, 2) === '@:') {  
  1802.             result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId);  
  1803.           } else {  
  1804.             result = Interpolator.interpolate(translation, interpolateParams);  
  1805.           }  
  1806.         } else {  
  1807.           var missingTranslationHandlerTranslation;  
  1808.           // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise  
  1809.           if ($missingTranslationHandlerFactory && !pendingLoader) {  
  1810.             missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams);  
  1811.           }  
  1812.   
  1813.           // since we couldn't translate the inital requested translation id,  
  1814.           // we try it now with one or more fallback languages, if fallback language(s) is  
  1815.           // configured.  
  1816.           if ($uses && $fallbackLanguage && $fallbackLanguage.length) {  
  1817.             fallbackIndex = 0;  
  1818.             result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator);  
  1819.           } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {  
  1820.             // looks like the requested translation id doesn't exists.  
  1821.             // Now, if there is a registered handler for missing translations and no  
  1822.             // asyncLoader is pending, we execute the handler  
  1823.             result = missingTranslationHandlerTranslation;  
  1824.           } else {  
  1825.             result = applyNotFoundIndicators(translationId);  
  1826.           }  
  1827.         }  
  1828.   
  1829.         return result;  
  1830.       };  
  1831.   
  1832.       var clearNextLangAndPromise = function(key) {  
  1833.         if ($nextLang === key) {  
  1834.           $nextLang = undefined;  
  1835.         }  
  1836.         langPromises[key] = undefined;  
  1837.       };  
  1838.   
  1839.       /** 
  1840.        * @ngdoc function 
  1841.        * @name translate.$translate#preferredLanguage 
  1842.        * @methodOf translate.$translate 
  1843.        * 
  1844.        * @description 
  1845.        * Returns the language key for the preferred language. 
  1846.        * 
  1847.        * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime) 
  1848.        * 
  1849.        * @return {string} preferred language key 
  1850.        */  
  1851.       $translate.preferredLanguage = function (langKey) {  
  1852.         if(langKey) {  
  1853.           setupPreferredLanguage(langKey);  
  1854.         }  
  1855.         return $preferredLanguage;  
  1856.       };  
  1857.   
  1858.       /** 
  1859.        * @ngdoc function 
  1860.        * @name translate.$translate#cloakClassName 
  1861.        * @methodOf translate.$translate 
  1862.        * 
  1863.        * @description 
  1864.        * Returns the configured class name for `translate-cloak` directive. 
  1865.        * 
  1866.        * @return {string} cloakClassName 
  1867.        */  
  1868.       $translate.cloakClassName = function () {  
  1869.         return $cloakClassName;  
  1870.       };  
  1871.   
  1872.       /** 
  1873.        * @ngdoc function 
  1874.        * @name translate.$translate#fallbackLanguage 
  1875.        * @methodOf translate.$translate 
  1876.        * 
  1877.        * @description 
  1878.        * Returns the language key for the fallback languages or sets a new fallback stack. 
  1879.        * 
  1880.        * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime) 
  1881.        * 
  1882.        * @return {string||array} fallback language key 
  1883.        */  
  1884.       $translate.fallbackLanguage = function (langKey) {  
  1885.         if (langKey !== undefined && langKey !== null) {  
  1886.           fallbackStack(langKey);  
  1887.   
  1888.           // as we might have an async loader initiated and a new translation language might have been defined  
  1889.           // we need to add the promise to the stack also. So - iterate.  
  1890.           if ($loaderFactory) {  
  1891.             if ($fallbackLanguage && $fallbackLanguage.length) {  
  1892.               for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {  
  1893.                 if (!langPromises[$fallbackLanguage[i]]) {  
  1894.                   langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]);  
  1895.                 }  
  1896.               }  
  1897.             }  
  1898.           }  
  1899.           $translate.use($translate.use());  
  1900.         }  
  1901.         if ($fallbackWasString) {  
  1902.           return $fallbackLanguage[0];  
  1903.         } else {  
  1904.           return $fallbackLanguage;  
  1905.         }  
  1906.   
  1907.       };  
  1908.   
  1909.       /** 
  1910.        * @ngdoc function 
  1911.        * @name translate.$translate#useFallbackLanguage 
  1912.        * @methodOf translate.$translate 
  1913.        * 
  1914.        * @description 
  1915.        * Sets the first key of the fallback language stack to be used for translation. 
  1916.        * Therefore all languages in the fallback array BEFORE this key will be skipped! 
  1917.        * 
  1918.        * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to 
  1919.        * get back to the whole stack 
  1920.        */  
  1921.       $translate.useFallbackLanguage = function (langKey) {  
  1922.         if (langKey !== undefined && langKey !== null) {  
  1923.           if (!langKey) {  
  1924.             startFallbackIteration = 0;  
  1925.           } else {  
  1926.             var langKeyPosition = indexOf($fallbackLanguage, langKey);  
  1927.             if (langKeyPosition > -1) {  
  1928.               startFallbackIteration = langKeyPosition;  
  1929.             }  
  1930.           }  
  1931.   
  1932.         }  
  1933.   
  1934.       };  
  1935.   
  1936.       /** 
  1937.        * @ngdoc function 
  1938.        * @name translate.$translate#proposedLanguage 
  1939.        * @methodOf translate.$translate 
  1940.        * 
  1941.        * @description 
  1942.        * Returns the language key of language that is currently loaded asynchronously. 
  1943.        * 
  1944.        * @return {string} language key 
  1945.        */  
  1946.       $translate.proposedLanguage = function () {  
  1947.         return $nextLang;  
  1948.       };  
  1949.   
  1950.       /** 
  1951.        * @ngdoc function 
  1952.        * @name translate.$translate#storage 
  1953.        * @methodOf translate.$translate 
  1954.        * 
  1955.        * @description 
  1956.        * Returns registered storage. 
  1957.        * 
  1958.        * @return {object} Storage 
  1959.        */  
  1960.       $translate.storage = function () {  
  1961.         return Storage;  
  1962.       };  
  1963.   
  1964.       /** 
  1965.        * @ngdoc function 
  1966.        * @name translate.$translate#use 
  1967.        * @methodOf translate.$translate 
  1968.        * 
  1969.        * @description 
  1970.        * Tells angular-translate which language to use by given language key. This method is 
  1971.        * used to change language at runtime. It also takes care of storing the language 
  1972.        * key in a configured store to let your app remember the choosed language. 
  1973.        * 
  1974.        * When trying to 'use' a language which isn't available it tries to load it 
  1975.        * asynchronously with registered loaders. 
  1976.        * 
  1977.        * Returns promise object with loaded language file data 
  1978.        * @example 
  1979.        * $translate.use("en_US").then(function(data){ 
  1980.        *   $scope.text = $translate("HELLO"); 
  1981.        * }); 
  1982.        * 
  1983.        * @param {string} key Language key 
  1984.        * @return {string} Language key 
  1985.        */  
  1986.       $translate.use = function (key) {  
  1987.         if (!key) {  
  1988.           return $uses;  
  1989.         }  
  1990.   
  1991.         var deferred = $q.defer();  
  1992.   
  1993.         $rootScope.$emit('$translateChangeStart', {language: key});  
  1994.   
  1995.         // Try to get the aliased language key  
  1996.         var aliasedKey = negotiateLocale(key);  
  1997.         if (aliasedKey) {  
  1998.           key = aliasedKey;  
  1999.         }  
  2000.   
  2001.         // if there isn't a translation table for the language we've requested,  
  2002.         // we load it asynchronously  
  2003.         if (($forceAsyncReloadEnabled || !$translationTable[key]) && $loaderFactory && !langPromises[key]) {  
  2004.           $nextLang = key;  
  2005.           langPromises[key] = loadAsync(key).then(function (translation) {  
  2006.             translations(translation.key, translation.table);  
  2007.             deferred.resolve(translation.key);  
  2008.             useLanguage(translation.key);  
  2009.             return translation;  
  2010.           }, function (key) {  
  2011.             $rootScope.$emit('$translateChangeError', {language: key});  
  2012.             deferred.reject(key);  
  2013.             $rootScope.$emit('$translateChangeEnd', {language: key});  
  2014.             return $q.reject(key);  
  2015.           });  
  2016.           langPromises[key]['finally'](function () {  
  2017.             clearNextLangAndPromise(key);  
  2018.           });  
  2019.         } else if ($nextLang === key && langPromises[key]) {  
  2020.           // we are already loading this asynchronously  
  2021.           // resolve our new deferred when the old langPromise is resolved  
  2022.           langPromises[key].then(function (translation) {  
  2023.             deferred.resolve(translation.key);  
  2024.             return translation;  
  2025.           }, function (key) {  
  2026.             deferred.reject(key);  
  2027.             return $q.reject(key);  
  2028.           });  
  2029.         } else {  
  2030.           deferred.resolve(key);  
  2031.           useLanguage(key);  
  2032.         }  
  2033.   
  2034.         return deferred.promise;  
  2035.       };  
  2036.   
  2037.       /** 
  2038.        * @ngdoc function 
  2039.        * @name translate.$translate#storageKey 
  2040.        * @methodOf translate.$translate 
  2041.        * 
  2042.        * @description 
  2043.        * Returns the key for the storage. 
  2044.        * 
  2045.        * @return {string} storage key 
  2046.        */  
  2047.       $translate.storageKey = function () {  
  2048.         return storageKey();  
  2049.       };  
  2050.   
  2051.       /** 
  2052.        * @ngdoc function 
  2053.        * @name translate.$translate#isPostCompilingEnabled 
  2054.        * @methodOf translate.$translate 
  2055.        * 
  2056.        * @description 
  2057.        * Returns whether post compiling is enabled or not 
  2058.        * 
  2059.        * @return {bool} storage key 
  2060.        */  
  2061.       $translate.isPostCompilingEnabled = function () {  
  2062.         return $postCompilingEnabled;  
  2063.       };  
  2064.   
  2065.       /** 
  2066.        * @ngdoc function 
  2067.        * @name translate.$translate#isForceAsyncReloadEnabled 
  2068.        * @methodOf translate.$translate 
  2069.        * 
  2070.        * @description 
  2071.        * Returns whether force async reload is enabled or not 
  2072.        * 
  2073.        * @return {boolean} forceAsyncReload value 
  2074.        */  
  2075.       $translate.isForceAsyncReloadEnabled = function () {  
  2076.         return $forceAsyncReloadEnabled;  
  2077.       };  
  2078.   
  2079.       /** 
  2080.        * @ngdoc function 
  2081.        * @name translate.$translate#refresh 
  2082.        * @methodOf translate.$translate 
  2083.        * 
  2084.        * @description 
  2085.        * Refreshes a translation table pointed by the given langKey. If langKey is not specified, 
  2086.        * the module will drop all existent translation tables and load new version of those which 
  2087.        * are currently in use. 
  2088.        * 
  2089.        * Refresh means that the module will drop target translation table and try to load it again. 
  2090.        * 
  2091.        * In case there are no loaders registered the refresh() method will throw an Error. 
  2092.        * 
  2093.        * If the module is able to refresh translation tables refresh() method will broadcast 
  2094.        * $translateRefreshStart and $translateRefreshEnd events. 
  2095.        * 
  2096.        * @example 
  2097.        * // this will drop all currently existent translation tables and reload those which are 
  2098.        * // currently in use 
  2099.        * $translate.refresh(); 
  2100.        * // this will refresh a translation table for the en_US language 
  2101.        * $translate.refresh('en_US'); 
  2102.        * 
  2103.        * @param {string} langKey A language key of the table, which has to be refreshed 
  2104.        * 
  2105.        * @return {promise} Promise, which will be resolved in case a translation tables refreshing 
  2106.        * process is finished successfully, and reject if not. 
  2107.        */  
  2108.       $translate.refresh = function (langKey) {  
  2109.         if (!$loaderFactory) {  
  2110.           throw new Error('Couldn\'t refresh translation table, no loader registered!');  
  2111.         }  
  2112.   
  2113.         var deferred = $q.defer();  
  2114.   
  2115.         function resolve() {  
  2116.           deferred.resolve();  
  2117.           $rootScope.$emit('$translateRefreshEnd', {language: langKey});  
  2118.         }  
  2119.   
  2120.         function reject() {  
  2121.           deferred.reject();  
  2122.           $rootScope.$emit('$translateRefreshEnd', {language: langKey});  
  2123.         }  
  2124.   
  2125.         $rootScope.$emit('$translateRefreshStart', {language: langKey});  
  2126.   
  2127.         if (!langKey) {  
  2128.           // if there's no language key specified we refresh ALL THE THINGS!  
  2129.           var tables = [], loadingKeys = {};  
  2130.   
  2131.           // reload registered fallback languages  
  2132.           if ($fallbackLanguage && $fallbackLanguage.length) {  
  2133.             for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {  
  2134.               tables.push(loadAsync($fallbackLanguage[i]));  
  2135.               loadingKeys[$fallbackLanguage[i]] = true;  
  2136.             }  
  2137.           }  
  2138.   
  2139.           // reload currently used language  
  2140.           if ($uses && !loadingKeys[$uses]) {  
  2141.             tables.push(loadAsync($uses));  
  2142.           }  
  2143.   
  2144.           var allTranslationsLoaded = function (tableData) {  
  2145.             $translationTable = {};  
  2146.             angular.forEach(tableData, function (data) {  
  2147.               translations(data.key, data.table);  
  2148.             });  
  2149.             if ($uses) {  
  2150.               useLanguage($uses);  
  2151.             }  
  2152.             resolve();  
  2153.           };  
  2154.           allTranslationsLoaded.displayName = 'refreshPostProcessor';  
  2155.   
  2156.           $q.all(tables).then(allTranslationsLoaded, reject);  
  2157.   
  2158.         } else if ($translationTable[langKey]) {  
  2159.   
  2160.           var oneTranslationsLoaded = function (data) {  
  2161.             translations(data.key, data.table);  
  2162.             if (langKey === $uses) {  
  2163.               useLanguage($uses);  
  2164.             }  
  2165.             resolve();  
  2166.           };  
  2167.           oneTranslationsLoaded.displayName = 'refreshPostProcessor';  
  2168.   
  2169.           loadAsync(langKey).then(oneTranslationsLoaded, reject);  
  2170.   
  2171.         } else {  
  2172.           reject();  
  2173.         }  
  2174.         return deferred.promise;  
  2175.       };  
  2176.   
  2177.       /** 
  2178.        * @ngdoc function 
  2179.        * @name translate.$translate#instant 
  2180.        * @methodOf translate.$translate 
  2181.        * 
  2182.        * @description 
  2183.        * Returns a translation instantly from the internal state of loaded translation. All rules 
  2184.        * regarding the current language, the preferred language of even fallback languages will be 
  2185.        * used except any promise handling. If a language was not found, an asynchronous loading 
  2186.        * will be invoked in the background. 
  2187.        * 
  2188.        * @param {string|array} translationId A token which represents a translation id 
  2189.        *                                     This can be optionally an array of translation ids which 
  2190.        *                                     results that the function's promise returns an object where 
  2191.        *                                     each key is the translation id and the value the translation. 
  2192.        * @param {object} interpolateParams Params 
  2193.        * @param {string} interpolationId The id of the interpolation to use 
  2194.        * 
  2195.        * @return {string|object} translation 
  2196.        */  
  2197.       $translate.instant = function (translationId, interpolateParams, interpolationId) {  
  2198.   
  2199.         // Detect undefined and null values to shorten the execution and prevent exceptions  
  2200.         if (translationId === null || angular.isUndefined(translationId)) {  
  2201.           return translationId;  
  2202.         }  
  2203.   
  2204.         // Duck detection: If the first argument is an array, a bunch of translations was requested.  
  2205.         // The result is an object.  
  2206.         if (angular.isArray(translationId)) {  
  2207.           var results = {};  
  2208.           for (var i = 0, c = translationId.length; i < c; i++) {  
  2209.             results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId);  
  2210.           }  
  2211.           return results;  
  2212.         }  
  2213.   
  2214.         // We discarded unacceptable values. So we just need to verify if translationId is empty String  
  2215.         if (angular.isString(translationId) && translationId.length < 1) {  
  2216.           return translationId;  
  2217.         }  
  2218.   
  2219.         // trim off any whitespace  
  2220.         if (translationId) {  
  2221.           translationId = trim.apply(translationId);  
  2222.         }  
  2223.   
  2224.         var result, possibleLangKeys = [];  
  2225.         if ($preferredLanguage) {  
  2226.           possibleLangKeys.push($preferredLanguage);  
  2227.         }  
  2228.         if ($uses) {  
  2229.           possibleLangKeys.push($uses);  
  2230.         }  
  2231.         if ($fallbackLanguage && $fallbackLanguage.length) {  
  2232.           possibleLangKeys = possibleLangKeys.concat($fallbackLanguage);  
  2233.         }  
  2234.         for (var j = 0, d = possibleLangKeys.length; j < d; j++) {  
  2235.           var possibleLangKey = possibleLangKeys[j];  
  2236.           if ($translationTable[possibleLangKey]) {  
  2237.             if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') {  
  2238.               result = determineTranslationInstant(translationId, interpolateParams, interpolationId);  
  2239.             } else if ($notFoundIndicatorLeft || $notFoundIndicatorRight) {  
  2240.               result = applyNotFoundIndicators(translationId);  
  2241.             }  
  2242.           }  
  2243.           if (typeof result !== 'undefined') {  
  2244.             break;  
  2245.           }  
  2246.         }  
  2247.   
  2248.         if (!result && result !== '') {  
  2249.           // Return translation of default interpolator if not found anything.  
  2250.           result = defaultInterpolator.interpolate(translationId, interpolateParams);  
  2251.           if ($missingTranslationHandlerFactory && !pendingLoader) {  
  2252.             result = translateByHandler(translationId, interpolateParams);  
  2253.           }  
  2254.         }  
  2255.   
  2256.         return result;  
  2257.       };  
  2258.   
  2259.       /** 
  2260.        * @ngdoc function 
  2261.        * @name translate.$translate#versionInfo 
  2262.        * @methodOf translate.$translate 
  2263.        * 
  2264.        * @description 
  2265.        * Returns the current version information for the angular-translate library 
  2266.        * 
  2267.        * @return {string} angular-translate version 
  2268.        */  
  2269.       $translate.versionInfo = function () {  
  2270.         return version;  
  2271.       };  
  2272.   
  2273.       /** 
  2274.        * @ngdoc function 
  2275.        * @name translate.$translate#loaderCache 
  2276.        * @methodOf translate.$translate 
  2277.        * 
  2278.        * @description 
  2279.        * Returns the defined loaderCache. 
  2280.        * 
  2281.        * @return {boolean|string|object} current value of loaderCache 
  2282.        */  
  2283.       $translate.loaderCache = function () {  
  2284.         return loaderCache;  
  2285.       };  
  2286.   
  2287.       // internal purpose only  
  2288.       $translate.directivePriority = function () {  
  2289.         return directivePriority;  
  2290.       };  
  2291.   
  2292.       // internal purpose only  
  2293.       $translate.statefulFilter = function () {  
  2294.         return statefulFilter;  
  2295.       };  
  2296.   
  2297.       if ($loaderFactory) {  
  2298.   
  2299.         // If at least one async loader is defined and there are no  
  2300.         // (default) translations available we should try to load them.  
  2301.         if (angular.equals($translationTable, {})) {  
  2302.           $translate.use($translate.use());  
  2303.         }  
  2304.   
  2305.         // Also, if there are any fallback language registered, we start  
  2306.         // loading them asynchronously as soon as we can.  
  2307.         if ($fallbackLanguage && $fallbackLanguage.length) {  
  2308.           var processAsyncResult = function (translation) {  
  2309.             translations(translation.key, translation.table);  
  2310.             $rootScope.$emit('$translateChangeEnd', { language: translation.key });  
  2311.             return translation;  
  2312.           };  
  2313.           for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {  
  2314.             var fallbackLanguageId = $fallbackLanguage[i];  
  2315.             if ($forceAsyncReloadEnabled || !$translationTable[fallbackLanguageId]) {  
  2316.               langPromises[fallbackLanguageId] = loadAsync(fallbackLanguageId).then(processAsyncResult);  
  2317.             }  
  2318.           }  
  2319.         }  
  2320.       }  
  2321.   
  2322.       return $translate;  
  2323.     }  
  2324.   ];  
  2325. }  
  2326. $translate.$inject = ['$STORAGE_KEY''$windowProvider''$translateSanitizationProvider''pascalprechtTranslateOverrider'];  
  2327.   
  2328. $translate.displayName = 'displayName';  
  2329.   
  2330. /** 
  2331.  * @ngdoc object 
  2332.  * @name translate.$translateDefaultInterpolation 
  2333.  * @requires $interpolate 
  2334.  * 
  2335.  * @description 
  2336.  * Uses angular's `$interpolate` services to interpolate strings against some values. 
  2337.  * 
  2338.  * Be aware to configure a proper sanitization strategy. 
  2339.  * 
  2340.  * See also: 
  2341.  * * {@link translate.$translateSanitization} 
  2342.  * 
  2343.  * @return {object} $translateDefaultInterpolation Interpolator service 
  2344.  */  
  2345. angular.module('translate').factory('$translateDefaultInterpolation', $translateDefaultInterpolation);  
  2346.   
  2347. function $translateDefaultInterpolation ($interpolate, $translateSanitization) {  
  2348.   
  2349.   'use strict';  
  2350.   
  2351.   var $translateInterpolator = {},  
  2352.       $locale,  
  2353.       $identifier = 'default';  
  2354.   
  2355.   /** 
  2356.    * @ngdoc function 
  2357.    * @name translate.$translateDefaultInterpolation#setLocale 
  2358.    * @methodOf translate.$translateDefaultInterpolation 
  2359.    * 
  2360.    * @description 
  2361.    * Sets current locale (this is currently not use in this interpolation). 
  2362.    * 
  2363.    * @param {string} locale Language key or locale. 
  2364.    */  
  2365.   $translateInterpolator.setLocale = function (locale) {  
  2366.     $locale = locale;  
  2367.   };  
  2368.   
  2369.   /** 
  2370.    * @ngdoc function 
  2371.    * @name translate.$translateDefaultInterpolation#getInterpolationIdentifier 
  2372.    * @methodOf translate.$translateDefaultInterpolation 
  2373.    * 
  2374.    * @description 
  2375.    * Returns an identifier for this interpolation service. 
  2376.    * 
  2377.    * @returns {string} $identifier 
  2378.    */  
  2379.   $translateInterpolator.getInterpolationIdentifier = function () {  
  2380.     return $identifier;  
  2381.   };  
  2382.   
  2383.   /** 
  2384.    * @deprecated will be removed in 3.0 
  2385.    * @see {@link translate.$translateSanitization} 
  2386.    */  
  2387.   $translateInterpolator.useSanitizeValueStrategy = function (value) {  
  2388.     $translateSanitization.useStrategy(value);  
  2389.     return this;  
  2390.   };  
  2391.   
  2392.   /** 
  2393.    * @ngdoc function 
  2394.    * @name translate.$translateDefaultInterpolation#interpolate 
  2395.    * @methodOf translate.$translateDefaultInterpolation 
  2396.    * 
  2397.    * @description 
  2398.    * Interpolates given string agains given interpolate params using angulars 
  2399.    * `$interpolate` service. 
  2400.    * 
  2401.    * @returns {string} interpolated string. 
  2402.    */  
  2403.   $translateInterpolator.interpolate = function (string, interpolationParams) {  
  2404.     interpolationParams = interpolationParams || {};  
  2405.     interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params');  
  2406.   
  2407.     var interpolatedText = $interpolate(string)(interpolationParams);  
  2408.     interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text');  
  2409.   
  2410.     return interpolatedText;  
  2411.   };  
  2412.   
  2413.   return $translateInterpolator;  
  2414. }  
  2415. $translateDefaultInterpolation.$inject = ['$interpolate''$translateSanitization'];  
  2416.   
  2417. $translateDefaultInterpolation.displayName = '$translateDefaultInterpolation';  
  2418.   
  2419. angular.module('translate').constant('$STORAGE_KEY''NG_TRANSLATE_LANG_KEY');  
  2420.   
  2421. angular.module('translate')  
  2422. /** 
  2423.  * @ngdoc directive 
  2424.  * @name translate.directive:translate 
  2425.  * @requires $compile 
  2426.  * @requires $filter 
  2427.  * @requires $interpolate 
  2428.  * @restrict A 
  2429.  * 
  2430.  * @description 
  2431.  * Translates given translation id either through attribute or DOM content. 
  2432.  * Internally it uses `translate` filter to translate translation id. It possible to 
  2433.  *  an optional `translate-values` object literal as string into translation id. 
  2434.  * 
  2435.  * @param {string=} translate Translation id which could be either string or interpolated string. 
  2436.  * @param {string=} translate-values Values to  into translation id. Can be ed as object literal string or interpolated object. 
  2437.  * @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute. 
  2438.  * @param {string=} translate-default will be used unless translation was successful 
  2439.  * @param {boolean=} translate-compile (default true if present) defines locally activation of {@link translate.$translateProvider#methods_usePostCompiling} 
  2440.  * 
  2441.  * @example 
  2442.    <example module="ngView"> 
  2443.     <file name="index.html"> 
  2444.       <div ng-controller="TranslateCtrl"> 
  2445.         <pre translate="TRANSLATION_ID"></pre> 
  2446.         <pre translate>TRANSLATION_ID</pre> 
  2447.         <pre translate translate-attr-title="TRANSLATION_ID"></pre> 
  2448.         <pre translate="{{translationId}}"></pre> 
  2449.         <pre translate>{{translationId}}</pre> 
  2450.         <pre translate="WITH_VALUES" translate-values="{value: 5}"></pre> 
  2451.         <pre translate translate-values="{value: 5}">WITH_VALUES</pre> 
  2452.         <pre translate="WITH_VALUES" translate-values="{{values}}"></pre> 
  2453.         <pre translate translate-values="{{values}}">WITH_VALUES</pre> 
  2454.         <pre translate translate-attr-title="WITH_VALUES" translate-values="{{values}}"></pre> 
  2455.       </div> 
  2456.     </file> 
  2457.     <file name="script.js"> 
  2458.       angular.module('ngView', ['translate']) 
  2459.       .config(function ($translateProvider) { 
  2460.         $translateProvider.translations('en',{ 
  2461.           'TRANSLATION_ID': 'Hello there!', 
  2462.           'WITH_VALUES': 'The following value is dynamic: {{value}}' 
  2463.         }).preferredLanguage('en'); 
  2464.       }); 
  2465.       angular.module('ngView').controller('TranslateCtrl', function ($scope) { 
  2466.         $scope.translationId = 'TRANSLATION_ID'; 
  2467.         $scope.values = { 
  2468.           value: 78 
  2469.         }; 
  2470.       }); 
  2471.     </file> 
  2472.     <file name="scenario.js"> 
  2473.       it('should translate', function () { 
  2474.         inject(function ($rootScope, $compile) { 
  2475.           $rootScope.translationId = 'TRANSLATION_ID'; 
  2476.           element = $compile('<p translate="TRANSLATION_ID"></p>')($rootScope); 
  2477.           $rootScope.$digest(); 
  2478.           expect(element.text()).toBe('Hello there!'); 
  2479.           element = $compile('<p translate="{{translationId}}"></p>')($rootScope); 
  2480.           $rootScope.$digest(); 
  2481.           expect(element.text()).toBe('Hello there!'); 
  2482.           element = $compile('<p translate>TRANSLATION_ID</p>')($rootScope); 
  2483.           $rootScope.$digest(); 
  2484.           expect(element.text()).toBe('Hello there!'); 
  2485.           element = $compile('<p translate>{{translationId}}</p>')($rootScope); 
  2486.           $rootScope.$digest(); 
  2487.           expect(element.text()).toBe('Hello there!'); 
  2488.           element = $compile('<p translate translate-attr-title="TRANSLATION_ID"></p>')($rootScope); 
  2489.           $rootScope.$digest(); 
  2490.           expect(element.attr('title')).toBe('Hello there!'); 
  2491.         }); 
  2492.       }); 
  2493.     </file> 
  2494.    </example> 
  2495.  */  
  2496. .directive('translate', translateDirective);  
  2497. function translateDirective($translate, $q, $interpolate, $compile, $parse, $rootScope) {  
  2498.   
  2499.   'use strict';  
  2500.   
  2501.   /** 
  2502.    * @name trim 
  2503.    * @private 
  2504.    * 
  2505.    * @description 
  2506.    * trim polyfill 
  2507.    * 
  2508.    * @returns {string} The string stripped of whitespace from both ends 
  2509.    */  
  2510.   var trim = function() {  
  2511.     return this.toString().replace(/^\s+|\s+$/g, '');  
  2512.   };  
  2513.   
  2514.   return {  
  2515.     restrict: 'AE',  
  2516.     scope: true,  
  2517.     priority: $translate.directivePriority(),  
  2518.     compile: function (tElement, tAttr) {  
  2519.   
  2520.       var translateValuesExist = (tAttr.translateValues) ?  
  2521.         tAttr.translateValues : undefined;  
  2522.   
  2523.       var translateInterpolation = (tAttr.translateInterpolation) ?  
  2524.         tAttr.translateInterpolation : undefined;  
  2525.   
  2526.       var translateValueExist = tElement[0].outerHTML.match(/translate-value-+/i);  
  2527.   
  2528.       var interpolateRegExp = '^(.*)(' + $interpolate.startSymbol() + '.*' + $interpolate.endSymbol() + ')(.*)',  
  2529.           watcherRegExp = '^(.*)' + $interpolate.startSymbol() + '(.*)' + $interpolate.endSymbol() + '(.*)';  
  2530.   
  2531.       return function linkFn(scope, iElement, iAttr) {  
  2532.   
  2533.         scope.interpolateParams = {};  
  2534.         scope.preText = '';  
  2535.         scope.postText = '';  
  2536.         var translationIds = {};  
  2537.   
  2538.         var initInterpolationParams = function (interpolateParams, iAttr, tAttr) {  
  2539.           // initial setup  
  2540.           if (iAttr.translateValues) {  
  2541.             angular.extend(interpolateParams, $parse(iAttr.translateValues)(scope.$parent));  
  2542.           }  
  2543.           // initially fetch all attributes if existing and fill the params  
  2544.           if (translateValueExist) {  
  2545.             for (var attr in tAttr) {  
  2546.               if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') {  
  2547.                 var attributeName = angular.lowercase(attr.substr(14, 1)) + attr.substr(15);  
  2548.                 interpolateParams[attributeName] = tAttr[attr];  
  2549.               }  
  2550.             }  
  2551.           }  
  2552.         };  
  2553.   
  2554.         // Ensures any change of the attribute "translate" containing the id will  
  2555.         // be re-stored to the scope's "translationId".  
  2556.         // If the attribute has no content, the element's text value (white spaces trimmed off) will be used.  
  2557.         var observeElementTranslation = function (translationId) {  
  2558.   
  2559.           // Remove any old watcher  
  2560.           if (angular.isFunction(observeElementTranslation._unwatchOld)) {  
  2561.             observeElementTranslation._unwatchOld();  
  2562.             observeElementTranslation._unwatchOld = undefined;  
  2563.           }  
  2564.   
  2565.           if (angular.equals(translationId , '') || !angular.isDefined(translationId)) {  
  2566.             // Resolve translation id by inner html if required  
  2567.             var interpolateMatches = trim.apply(iElement.text()).match(interpolateRegExp);  
  2568.             // Interpolate translation id if required  
  2569.             if (angular.isArray(interpolateMatches)) {  
  2570.               scope.preText = interpolateMatches[1];  
  2571.               scope.postText = interpolateMatches[3];  
  2572.               translationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent);  
  2573.               var watcherMatches = iElement.text().match(watcherRegExp);  
  2574.               if (angular.isArray(watcherMatches) && watcherMatches[2] && watcherMatches[2].length) {  
  2575.                 observeElementTranslation._unwatchOld = scope.$watch(watcherMatches[2], function (newValue) {  
  2576.                   translationIds.translate = newValue;  
  2577.                   updateTranslations();  
  2578.                 });  
  2579.               }  
  2580.             } else {  
  2581.               translationIds.translate = iElement.text().replace(/^\s+|\s+$/g,'');  
  2582.             }  
  2583.           } else {  
  2584.             translationIds.translate = translationId;  
  2585.           }  
  2586.           updateTranslations();  
  2587.         };  
  2588.   
  2589.         var observeAttributeTranslation = function (translateAttr) {  
  2590.           iAttr.$observe(translateAttr, function (translationId) {  
  2591.             translationIds[translateAttr] = translationId;  
  2592.             updateTranslations();  
  2593.           });  
  2594.         };  
  2595.   
  2596.         // initial setup with values  
  2597.         initInterpolationParams(scope.interpolateParams, iAttr, tAttr);  
  2598.   
  2599.         var firstAttributeChangedEvent = true;  
  2600.         iAttr.$observe('translate'function (translationId) {  
  2601.           if (typeof translationId === 'undefined') {  
  2602.             // case of element "<translate>xyz</translate>"  
  2603.             observeElementTranslation('');  
  2604.           } else {  
  2605.             // case of regular attribute  
  2606.             if (translationId !== '' || !firstAttributeChangedEvent) {  
  2607.               translationIds.translate = translationId;  
  2608.               updateTranslations();  
  2609.             }  
  2610.           }  
  2611.           firstAttributeChangedEvent = false;  
  2612.         });  
  2613.   
  2614.         for (var translateAttr in iAttr) {  
  2615.           if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr') {  
  2616.             observeAttributeTranslation(translateAttr);  
  2617.           }  
  2618.         }  
  2619.   
  2620.         iAttr.$observe('translateDefault'function (value) {  
  2621.           scope.defaultText = value;  
  2622.         });  
  2623.   
  2624.         if (translateValuesExist) {  
  2625.           iAttr.$observe('translateValues'function (interpolateParams) {  
  2626.             if (interpolateParams) {  
  2627.               scope.$parent.$watch(function () {  
  2628.                 angular.extend(scope.interpolateParams, $parse(interpolateParams)(scope.$parent));  
  2629.               });  
  2630.             }  
  2631.           });  
  2632.         }  
  2633.   
  2634.         if (translateValueExist) {  
  2635.           var observeValueAttribute = function (attrName) {  
  2636.             iAttr.$observe(attrName, function (value) {  
  2637.               var attributeName = angular.lowercase(attrName.substr(14, 1)) + attrName.substr(15);  
  2638.               scope.interpolateParams[attributeName] = value;  
  2639.             });  
  2640.           };  
  2641.           for (var attr in iAttr) {  
  2642.             if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') {  
  2643.               observeValueAttribute(attr);  
  2644.             }  
  2645.           }  
  2646.         }  
  2647.   
  2648.         // Master update function  
  2649.         var updateTranslations = function () {  
  2650.           for (var key in translationIds) {  
  2651.   
  2652.             if (translationIds.hasOwnProperty(key) && translationIds[key] !== undefined) {  
  2653.               updateTranslation(key, translationIds[key], scope, scope.interpolateParams, scope.defaultText);  
  2654.             }  
  2655.           }  
  2656.         };  
  2657.   
  2658.         // Put translation processing function outside loop  
  2659.         var updateTranslation = function(translateAttr, translationId, scope, interpolateParams, defaultTranslationText) {  
  2660.           if (translationId) {  
  2661.             $translate(translationId, interpolateParams, translateInterpolation, defaultTranslationText)  
  2662.               .then(function (translation) {  
  2663.                 applyTranslation(translation, scope, true, translateAttr);  
  2664.               }, function (translationId) {  
  2665.                 applyTranslation(translationId, scope, false, translateAttr);  
  2666.               });  
  2667.           } else {  
  2668.             // as an empty string cannot be translated, we can solve this using successful=false  
  2669.             applyTranslation(translationId, scope, false, translateAttr);  
  2670.           }  
  2671.         };  
  2672.   
  2673.         var applyTranslation = function (value, scope, successful, translateAttr) {  
  2674.           if (translateAttr === 'translate') {  
  2675.             // default translate into innerHTML  
  2676.             if (!successful && typeof scope.defaultText !== 'undefined') {  
  2677.               value = scope.defaultText;  
  2678.             }  
  2679.             iElement.html(scope.preText + value + scope.postText);  
  2680.             var globallyEnabled = $translate.isPostCompilingEnabled();  
  2681.             var locallyDefined = typeof tAttr.translateCompile !== 'undefined';  
  2682.             var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false';  
  2683.             if ((globallyEnabled && !locallyDefined) || locallyEnabled) {  
  2684.               $compile(iElement.contents())(scope);  
  2685.             }  
  2686.           } else {  
  2687.             // translate attribute  
  2688.             if (!successful && typeof scope.defaultText !== 'undefined') {  
  2689.               value = scope.defaultText;  
  2690.             }  
  2691.             var attributeName = iAttr.$attr[translateAttr];  
  2692.             if (attributeName.substr(0, 5) === 'data-') {  
  2693.               // ensure html5 data prefix is stripped  
  2694.               attributeName = attributeName.substr(5);  
  2695.             }  
  2696.             attributeName = attributeName.substr(15);  
  2697.             iElement.attr(attributeName, value);  
  2698.           }  
  2699.         };  
  2700.   
  2701.         if (translateValuesExist || translateValueExist || iAttr.translateDefault) {  
  2702.           scope.$watch('interpolateParams', updateTranslations, true);  
  2703.         }  
  2704.   
  2705.         // Ensures the text will be refreshed after the current language was changed  
  2706.         // w/ $translate.use(...)  
  2707.         var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations);  
  2708.   
  2709.         // ensure translation will be looked up at least one  
  2710.         if (iElement.text().length) {  
  2711.           if (iAttr.translate) {  
  2712.             observeElementTranslation(iAttr.translate);  
  2713.           } else {  
  2714.             observeElementTranslation('');  
  2715.           }  
  2716.         } else if (iAttr.translate) {  
  2717.           // ensure attribute will be not skipped  
  2718.           observeElementTranslation(iAttr.translate);  
  2719.         }  
  2720.         updateTranslations();  
  2721.         scope.$on('$destroy', unbind);  
  2722.       };  
  2723.     }  
  2724.   };  
  2725. }  
  2726. translateDirective.$inject = ['$translate''$q''$interpolate''$compile''$parse''$rootScope'];  
  2727.   
  2728. translateDirective.displayName = 'translateDirective';  
  2729.   
  2730. angular.module('translate')  
  2731. /** 
  2732.  * @ngdoc directive 
  2733.  * @name translate.directive:translateCloak 
  2734.  * @requires $rootScope 
  2735.  * @requires $translate 
  2736.  * @restrict A 
  2737.  * 
  2738.  * $description 
  2739.  * Adds a `translate-cloak` class name to the given element where this directive 
  2740.  * is applied initially and removes it, once a loader has finished loading. 
  2741.  * 
  2742.  * This directive can be used to prevent initial flickering when loading translation 
  2743.  * data asynchronously. 
  2744.  * 
  2745.  * The class name is defined in 
  2746.  * {@link translate.$translateProvider#cloakClassName $translate.cloakClassName()}. 
  2747.  * 
  2748.  * @param {string=} translate-cloak If a translationId is provided, it will be used for showing 
  2749.  *                                  or hiding the cloak. Basically it relies on the translation 
  2750.  *                                  resolve. 
  2751.  */  
  2752. .directive('translateCloak', translateCloakDirective);  
  2753.   
  2754. function translateCloakDirective($rootScope, $translate) {  
  2755.   
  2756.   'use strict';  
  2757.   
  2758.   return {  
  2759.     compile: function (tElement) {  
  2760.       var applyCloak = function () {  
  2761.         tElement.addClass($translate.cloakClassName());  
  2762.       },  
  2763.       removeCloak = function () {  
  2764.         tElement.removeClass($translate.cloakClassName());  
  2765.       },  
  2766.       removeListener = $rootScope.$on('$translateChangeEnd'function () {  
  2767.         removeCloak();  
  2768.         removeListener();  
  2769.         removeListener = null;  
  2770.       });  
  2771.       applyCloak();  
  2772.   
  2773.       return function linkFn(scope, iElement, iAttr) {  
  2774.         // Register a watcher for the defined translation allowing a fine tuned cloak  
  2775.         if (iAttr.translateCloak && iAttr.translateCloak.length) {  
  2776.           iAttr.$observe('translateCloak'function (translationId) {  
  2777.             $translate(translationId).then(removeCloak, applyCloak);  
  2778.           });  
  2779.         }  
  2780.       };  
  2781.     }  
  2782.   };  
  2783. }  
  2784. translateCloakDirective.$inject = ['$rootScope''$translate'];  
  2785.   
  2786. translateCloakDirective.displayName = 'translateCloakDirective';  
  2787.   
  2788. angular.module('translate')  
  2789. /** 
  2790.  * @ngdoc filter 
  2791.  * @name translate.filter:translate 
  2792.  * @requires $parse 
  2793.  * @requires translate.$translate 
  2794.  * @function 
  2795.  * 
  2796.  * @description 
  2797.  * Uses `$translate` service to translate contents. Accepts interpolate parameters 
  2798.  * to  dynamized values though translation. 
  2799.  * 
  2800.  * @param {string} translationId A translation id to be translated. 
  2801.  * @param {*=} interpolateParams Optional object literal (as hash or string) to  values into translation. 
  2802.  * 
  2803.  * @returns {string} Translated text. 
  2804.  * 
  2805.  * @example 
  2806.    <example module="ngView"> 
  2807.     <file name="index.html"> 
  2808.       <div ng-controller="TranslateCtrl"> 
  2809.         <pre>{{ 'TRANSLATION_ID' | translate }}</pre> 
  2810.         <pre>{{ translationId | translate }}</pre> 
  2811.         <pre>{{ 'WITH_VALUES' | translate:'{value: 5}' }}</pre> 
  2812.         <pre>{{ 'WITH_VALUES' | translate:values }}</pre> 
  2813.       </div> 
  2814.     </file> 
  2815.     <file name="script.js"> 
  2816.       angular.module('ngView', ['translate']) 
  2817.       .config(function ($translateProvider) { 
  2818.         $translateProvider.translations('en', { 
  2819.           'TRANSLATION_ID': 'Hello there!', 
  2820.           'WITH_VALUES': 'The following value is dynamic: {{value}}' 
  2821.         }); 
  2822.         $translateProvider.preferredLanguage('en'); 
  2823.       }); 
  2824.       angular.module('ngView').controller('TranslateCtrl', function ($scope) { 
  2825.         $scope.translationId = 'TRANSLATION_ID'; 
  2826.         $scope.values = { 
  2827.           value: 78 
  2828.         }; 
  2829.       }); 
  2830.     </file> 
  2831.    </example> 
  2832.  */  
  2833. .filter('translate', translateFilterFactory);  
  2834.   
  2835. function translateFilterFactory($parse, $translate) {  
  2836.   
  2837.   'use strict';  
  2838.   
  2839.   var translateFilter = function (translationId, interpolateParams, interpolation) {  
  2840.   
  2841.     if (!angular.isObject(interpolateParams)) {  
  2842.       interpolateParams = $parse(interpolateParams)(this);  
  2843.     }  
  2844.   
  2845.     return $translate.instant(translationId, interpolateParams, interpolation);  
  2846.   };  
  2847.   
  2848.   if ($translate.statefulFilter()) {  
  2849.     translateFilter.$stateful = true;  
  2850.   }  
  2851.   
  2852.   return translateFilter;  
  2853. }  
  2854. translateFilterFactory.$inject = ['$parse''$translate'];  
  2855.   
  2856. translateFilterFactory.displayName = 'translateFilterFactory';  
  2857.   
  2858. angular.module('translate')  
  2859.   
  2860. /** 
  2861.  * @ngdoc object 
  2862.  * @name translate.$translationCache 
  2863.  * @requires $cacheFactory 
  2864.  * 
  2865.  * @description 
  2866.  * The first time a translation table is used, it is loaded in the translation cache for quick retrieval. You 
  2867.  * can load translation tables directly into the cache by consuming the 
  2868.  * `$translationCache` service directly. 
  2869.  * 
  2870.  * @return {object} $cacheFactory object. 
  2871.  */  
  2872.   .factory('$translationCache', $translationCache);  
  2873.   
  2874. function $translationCache($cacheFactory) {  
  2875.   
  2876.   'use strict';  
  2877.   
  2878.   return $cacheFactory('translations');  
  2879. }  
  2880. $translationCache.$inject = ['$cacheFactory'];  
  2881.   
  2882. $translationCache.displayName = '$translationCache';  
  2883. return 'translate';  
  2884.   
  2885. }));  
Now the remaining two files, named Index.js and MyApp.Js, are used for defining the Angular app and controller for the page. 
 
The following is the code for MyApp.JS:
  1. var MyApp = angular.module('MyApp', ['translate']);  
  2.   
  3. MyApp.config(function ($translateProvider) {  
  4.     $translateProvider.translations('en', {  
  5.         HEADLINE: 'Hello there, This is my awesome app!',  
  6.         INTRO_TEXT: 'And it has i18n support!',  
  7.         BUTTON_TEXT_EN: 'english',  
  8.         BUTTON_TEXT_DE: 'german',  
  9.         BUTTON_TEXT_AE: 'Arabic'  
  10.     })  
  11.     .translations('en-de', {  
  12.         HEADLINE: 'Hey, das ist meine großartige App!',  
  13.         INTRO_TEXT: 'Und sie untersützt mehrere Sprachen!',  
  14.         BUTTON_TEXT_EN: 'englisch',  
  15.         BUTTON_TEXT_DE: 'deutsch',  
  16.         BUTTON_TEXT_AE: 'Arabisch'  
  17.     })  
  18.     .translations('en-ar', {  
  19.         HEADLINE: 'هذا هو اختبار التدويل!',  
  20.         INTRO_TEXT: 'إضفاء الطابع المحلي على القيام به!',  
  21.         BUTTON_TEXT_EN: 'الإنجليزية',  
  22.         BUTTON_TEXT_DE: 'ألماني',  
  23.         BUTTON_TEXT_AE: 'العربية'  
  24.     },  
  25.     { rtl: true });  
  26.     $translateProvider.useSanitizeValueStrategy('escaped');  
  27.     $translateProvider.preferredLanguage('en');  
  28. });  
The following is the code of the Index.js file:
  1. MyApp.controller('TranslateController'function ($translate, $scope) 
  2. {  
  3.     $scope.changeLanguage = function (langKey) 
  4.     {  
  5.         $translate.use(langKey);  
  6.     };  
  7. });  
In the App.Config part, we use the translate provider to translate the language depending on the selected languages. 
 
Now if we run the project then the output looks as in the following: 
  
But the problem is that when we click on the Arabic Button, it translates the text as Arabic, but never change the document alignment to rtl format since Arabic always uses the rtl (right to left) format. For doing this we need to make a change in the MyApp.js file and add the following new code to the files:
  1. MyApp.run(function ($rootScope, Language) 
  2. {  
  3.     $rootScope.Language = Language;  
  4. })  
  5.   
  6. // Service definition  
  7. MyApp.factory('Language'function ($translate) 
  8. {  
  9.     var rtlLanguages = ['en-ar'];  
  10.   
  11.     var isRtl = function () 
  12.     {  
  13.         var languageKey = $translate.proposedLanguage() || $translate.use();  
  14.         for (var i = 0; i < rtlLanguages.length; i += 1) 
  15.         {  
  16.             if (languageKey.indexOf(rtlLanguages[i]) > -1)  
  17.                 return true;  
  18.         }  
  19.         return false;  
  20.     };  
  21.     return 
  22.     {  
  23.         isRtl: isRtl  
  24.     };  
  25. });  
Actually, we create a factory service to define the rtl format is on or off depending on the selected language.
 
Also, we need to change the controller script in the index.js file. 
  1. MyApp.controller('TranslateController'function ($translate, $scope, $window) 
  2. {  
  3.     if (sessionStorage.getItem("Locale") == null || sessionStorage.getItem("Locale") == undefined) 
  4.     {  
  5.         $scope.Lang = 'en';  
  6.         sessionStorage.setItem("Locale", JSON.stringify($scope.Lang));  
  7.     }  
  8.     else 
  9.     {  
  10.         $scope.Lang = JSON.parse(sessionStorage.getItem('Locale'));  
  11.     }  
  12.     $scope.selectedLanguage = $translate.use($scope.Lang); //default  
  13.   
  14.     $scope.changeLanguage = function (langKey) {  
  15.         $translate.use(langKey);  
  16.         sessionStorage.setItem("Locale", JSON.stringify(langKey));  
  17.         $window.location.reload();  
  18.     };  
  19. });  
Here during the load of the Controller (for the first time of the page load) we set the page language to English (en) and store this information in the browser session variables so that we can access this value in the HTML file to load the dynamic Angular locale script file.

Now after doing this we need to make some changes in the HTML file also. 
  1. <!DOCTYPE html>  
  2. <html ng-app="MyApp" ng-controller="TranslateController" ng-class="{'rtl':Language.isRtl()}" dir="{{(Language.isRtl())?'rtl':'ltr'}}" lang={{Lang}}>  
  3. <head>  
  4.     <title></title>  
  5.     <script src="../Scripts/angular.min.js"></script>  
  6.     <script src="../Scripts/angular-route.js"></script>  
  7.     <script src="../Scripts/angular-translate.js"></script>  
  8.     <script src="../UserScript/MyApp.js"></script>  
  9.     <script src="../UserScript/Index.js"></script>  
  10.     <script>  
  11.         var locale = JSON.parse(sessionStorage.getItem('Locale'));  
  12.         if (locale) {  
  13.             document.write('<script src="../scripts/i18n/angular-locale_' + locale + '.js"><\/script>');  
  14.         }  
  15.     </script>  
  16.   
  17. </head>  
  18. <body>  
  19.     <h1>Localization</h1>  
  20.     <div>  
  21.         <button ng-click="changeLanguage('en-de')" translate="BUTTON_TEXT_DE"></button>  
  22.         <button ng-click="changeLanguage('en')" translate="BUTTON_TEXT_EN"></button>  
  23.         <button ng-click="changeLanguage('en-ar')" translate="BUTTON_TEXT_AE"></button>  
  24.     </div>  
  25.     <div>  
  26.         <h2>{{ 'HEADLINE' | translate }}</h2>  
  27.         <p>{{ 'INTRO_TEXT' | translate }}</p>  
  28.     </div>  
  29.     <div>  
  30.         <input type="date" />  
  31.     </div>  
  32. </body>  
  33. </html>  
Execute the project and click on the Arabic button and see the following output.

localization

Up Next
    Ebook Download
    View all
    Learn
    View all