angular-translate.js 118 KB

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