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