You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3656 lines
124 KiB

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