angular-translate-rails-tf 2.7.2 → 2.15.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 929573be32a74bd2cfa7fadf2d62f16c567d6b4a
4
- data.tar.gz: 20a8c41c0be0ba48985362db226cb2ee6c45eac2
3
+ metadata.gz: 5a8c289c878691e9ef070eb726514e852b9163e9
4
+ data.tar.gz: f53d2649e6fd7410a1d947a9eeb50c7787166a76
5
5
  SHA512:
6
- metadata.gz: 0e3170d3f54cdca476c86e5dd91a3e6982458a93ce4087a00487f07f9247c455886304f0dd9215122459b3ef1dcfa2d947efd1558103f6d2a4126ac0625586b8
7
- data.tar.gz: ca1b828a283907b022dedcf6857b537e3ea1ca2b21fe351410e9c60b295afb217660aaac28c7ae8c30820835ed0fcf7b479dfb543c132422ca4e2309dfc6456f
6
+ metadata.gz: 2a8de09ea84acde418bd28adb4fe5bdfa09800e3fef41f4bda455b00423f9a3db6757c550c2fa5ba174c9eff7c6d95758d6813be105369a6e352fc31f8656059
7
+ data.tar.gz: aa1f105aaf921c2a6211b8a431f6fa58d6715d6f6595d4bc3a18e20f2f345466ee3e54beaa4bff14ffea6a73955cb7a3d24b3899f3d5c86ae0dcb4bd7dded07c
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * angular-translate - v2.7.2 - 2015-06-01
3
- * http://github.com/angular-translate/angular-translate
4
- * Copyright (c) 2015 ; Licensed MIT
2
+ * angular-translate - v2.15.2 - 2017-06-22
3
+ *
4
+ * Copyright (c) 2017 The angular-translate team, Pascal Precht; Licensed MIT
5
5
  */
6
6
  (function (root, factory) {
7
7
  if (typeof define === 'function' && define.amd) {
@@ -9,7 +9,7 @@
9
9
  define([], function () {
10
10
  return (factory());
11
11
  });
12
- } else if (typeof exports === 'object') {
12
+ } else if (typeof module === 'object' && module.exports) {
13
13
  // Node. Does not work with strict CommonJS, but
14
14
  // only CommonJS-like environments that support module.exports,
15
15
  // like Node.
@@ -26,6 +26,14 @@
26
26
  * @description
27
27
  * The main module which holds everything together.
28
28
  */
29
+ runTranslate.$inject = ['$translate'];
30
+ $translate.$inject = ['$STORAGE_KEY', '$windowProvider', '$translateSanitizationProvider', 'pascalprechtTranslateOverrider'];
31
+ $translateDefaultInterpolation.$inject = ['$interpolate', '$translateSanitization'];
32
+ translateDirective.$inject = ['$translate', '$interpolate', '$compile', '$parse', '$rootScope'];
33
+ translateAttrDirective.$inject = ['$translate', '$rootScope'];
34
+ translateCloakDirective.$inject = ['$translate', '$rootScope'];
35
+ translateFilterFactory.$inject = ['$parse', '$translate'];
36
+ $translationCache.$inject = ['$cacheFactory'];
29
37
  angular.module('pascalprecht.translate', ['ng'])
30
38
  .run(runTranslate);
31
39
 
@@ -59,7 +67,6 @@ function runTranslate($translate) {
59
67
  $translate.use($translate.preferredLanguage());
60
68
  }
61
69
  }
62
- runTranslate.$inject = ['$translate'];
63
70
 
64
71
  runTranslate.displayName = 'runTranslate';
65
72
 
@@ -78,6 +85,7 @@ function $translateSanitizationProvider () {
78
85
  'use strict';
79
86
 
80
87
  var $sanitize,
88
+ $sce,
81
89
  currentStrategy = null, // TODO change to either 'sanitize', 'escape' or ['sanitize', 'escapeParameters'] in 3.0.
82
90
  hasConfiguredStrategy = false,
83
91
  hasShownNoStrategyConfiguredWarning = false,
@@ -114,29 +122,46 @@ function $translateSanitizationProvider () {
114
122
  */
115
123
 
116
124
  strategies = {
117
- sanitize: function (value, mode) {
125
+ sanitize: function (value, mode/*, context*/) {
118
126
  if (mode === 'text') {
119
127
  value = htmlSanitizeValue(value);
120
128
  }
121
129
  return value;
122
130
  },
123
- escape: function (value, mode) {
131
+ escape: function (value, mode/*, context*/) {
124
132
  if (mode === 'text') {
125
133
  value = htmlEscapeValue(value);
126
134
  }
127
135
  return value;
128
136
  },
129
- sanitizeParameters: function (value, mode) {
137
+ sanitizeParameters: function (value, mode/*, context*/) {
130
138
  if (mode === 'params') {
131
139
  value = mapInterpolationParameters(value, htmlSanitizeValue);
132
140
  }
133
141
  return value;
134
142
  },
135
- escapeParameters: function (value, mode) {
143
+ escapeParameters: function (value, mode/*, context*/) {
136
144
  if (mode === 'params') {
137
145
  value = mapInterpolationParameters(value, htmlEscapeValue);
138
146
  }
139
147
  return value;
148
+ },
149
+ sce: function (value, mode, context) {
150
+ if (mode === 'text') {
151
+ value = htmlTrustValue(value);
152
+ } else if (mode === 'params') {
153
+ if (context !== 'filter') {
154
+ // do html escape in filter context #1101
155
+ value = mapInterpolationParameters(value, htmlEscapeValue);
156
+ }
157
+ }
158
+ return value;
159
+ },
160
+ sceParameters: function (value, mode/*, context*/) {
161
+ if (mode === 'params') {
162
+ value = mapInterpolationParameters(value, htmlTrustValue);
163
+ }
164
+ return value;
140
165
  }
141
166
  };
142
167
  // Support legacy strategy name 'escaped' for backwards compatibility.
@@ -205,12 +230,24 @@ function $translateSanitizationProvider () {
205
230
  */
206
231
  this.$get = ['$injector', '$log', function ($injector, $log) {
207
232
 
208
- var applyStrategies = function (value, mode, selectedStrategies) {
233
+ var cachedStrategyMap = {};
234
+
235
+ var applyStrategies = function (value, mode, context, selectedStrategies) {
209
236
  angular.forEach(selectedStrategies, function (selectedStrategy) {
210
237
  if (angular.isFunction(selectedStrategy)) {
211
- value = selectedStrategy(value, mode);
238
+ value = selectedStrategy(value, mode, context);
212
239
  } else if (angular.isFunction(strategies[selectedStrategy])) {
213
- value = strategies[selectedStrategy](value, mode);
240
+ value = strategies[selectedStrategy](value, mode, context);
241
+ } else if (angular.isString(strategies[selectedStrategy])) {
242
+ if (!cachedStrategyMap[strategies[selectedStrategy]]) {
243
+ try {
244
+ cachedStrategyMap[strategies[selectedStrategy]] = $injector.get(strategies[selectedStrategy]);
245
+ } catch (e) {
246
+ cachedStrategyMap[strategies[selectedStrategy]] = function() {};
247
+ throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\'');
248
+ }
249
+ }
250
+ value = cachedStrategyMap[strategies[selectedStrategy]](value, mode, context);
214
251
  } else {
215
252
  throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\'');
216
253
  }
@@ -229,6 +266,9 @@ function $translateSanitizationProvider () {
229
266
  if ($injector.has('$sanitize')) {
230
267
  $sanitize = $injector.get('$sanitize');
231
268
  }
269
+ if ($injector.has('$sce')) {
270
+ $sce = $injector.get('$sce');
271
+ }
232
272
 
233
273
  return {
234
274
  /**
@@ -258,14 +298,15 @@ function $translateSanitizationProvider () {
258
298
  * @param {string|object} value The value which should be sanitized.
259
299
  * @param {string} mode The current sanitization mode, either 'params' or 'text'.
260
300
  * @param {string|StrategyFunction|array} [strategy] Optional custom strategy which should be used instead of the currently selected strategy.
301
+ * @param {string} [context] The context of this call: filter, service. Default is service
261
302
  * @returns {string|object} sanitized value
262
303
  */
263
- sanitize: function (value, mode, strategy) {
304
+ sanitize: function (value, mode, strategy, context) {
264
305
  if (!currentStrategy) {
265
306
  showNoStrategyConfiguredWarning();
266
307
  }
267
308
 
268
- if (arguments.length < 3) {
309
+ if (!strategy && strategy !== null) {
269
310
  strategy = currentStrategy;
270
311
  }
271
312
 
@@ -273,8 +314,12 @@ function $translateSanitizationProvider () {
273
314
  return value;
274
315
  }
275
316
 
317
+ if (!context) {
318
+ context = 'service';
319
+ }
320
+
276
321
  var selectedStrategies = angular.isArray(strategy) ? strategy : [strategy];
277
- return applyStrategies(value, mode, selectedStrategies);
322
+ return applyStrategies(value, mode, context, selectedStrategies);
278
323
  }
279
324
  };
280
325
  }];
@@ -292,19 +337,48 @@ function $translateSanitizationProvider () {
292
337
  return $sanitize(value);
293
338
  };
294
339
 
295
- var mapInterpolationParameters = function (value, iteratee) {
296
- if (angular.isObject(value)) {
340
+ var htmlTrustValue = function (value) {
341
+ if (!$sce) {
342
+ throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sce service.');
343
+ }
344
+ return $sce.trustAsHtml(value);
345
+ };
346
+
347
+ var mapInterpolationParameters = function (value, iteratee, stack) {
348
+ if (angular.isDate(value)) {
349
+ return value;
350
+ } else if (angular.isObject(value)) {
297
351
  var result = angular.isArray(value) ? [] : {};
298
352
 
353
+ if (!stack) {
354
+ stack = [];
355
+ } else {
356
+ if (stack.indexOf(value) > -1) {
357
+ throw new Error('pascalprecht.translate.$translateSanitization: Error cannot interpolate parameter due recursive object');
358
+ }
359
+ }
360
+
361
+ stack.push(value);
299
362
  angular.forEach(value, function (propertyValue, propertyKey) {
300
- result[propertyKey] = mapInterpolationParameters(propertyValue, iteratee);
363
+
364
+ /* Skipping function properties. */
365
+ if (angular.isFunction(propertyValue)) {
366
+ return;
367
+ }
368
+
369
+ result[propertyKey] = mapInterpolationParameters(propertyValue, iteratee, stack);
301
370
  });
371
+ stack.splice(-1, 1); // remove last
302
372
 
303
373
  return result;
304
374
  } else if (angular.isNumber(value)) {
305
375
  return value;
306
- } else {
376
+ } else if (value === true || value === false) {
377
+ return value;
378
+ } else if (!angular.isUndefined(value) && value !== null) {
307
379
  return iteratee(value);
380
+ } else {
381
+ return value;
308
382
  }
309
383
  };
310
384
  }
@@ -319,56 +393,64 @@ function $translateSanitizationProvider () {
319
393
  *
320
394
  */
321
395
  angular.module('pascalprecht.translate')
322
- .constant('pascalprechtTranslateOverrider', {})
323
- .provider('$translate', $translate);
396
+ .constant('pascalprechtTranslateOverrider', {})
397
+ .provider('$translate', $translate);
324
398
 
325
399
  function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvider, pascalprechtTranslateOverrider) {
326
400
 
327
401
  'use strict';
328
402
 
329
403
  var $translationTable = {},
330
- $preferredLanguage,
331
- $availableLanguageKeys = [],
332
- $languageKeyAliases,
333
- $fallbackLanguage,
334
- $fallbackWasString,
335
- $uses,
336
- $nextLang,
337
- $storageFactory,
338
- $storageKey = $STORAGE_KEY,
339
- $storagePrefix,
340
- $missingTranslationHandlerFactory,
341
- $interpolationFactory,
342
- $interpolatorFactories = [],
343
- $loaderFactory,
344
- $cloakClassName = 'translate-cloak',
345
- $loaderOptions,
346
- $notFoundIndicatorLeft,
347
- $notFoundIndicatorRight,
348
- $postCompilingEnabled = false,
349
- $forceAsyncReloadEnabled = false,
350
- NESTED_OBJECT_DELIMITER = '.',
351
- loaderCache,
352
- directivePriority = 0,
353
- statefulFilter = true,
354
- uniformLanguageTagResolver = 'default',
355
- languageTagResolver = {
356
- 'default': function (tag) {
357
- return (tag || '').split('-').join('_');
358
- },
359
- java: function (tag) {
360
- var temp = (tag || '').split('-').join('_');
361
- var parts = temp.split('_');
362
- return parts.length > 1 ? (parts[0].toLowerCase() + '_' + parts[1].toUpperCase()) : temp;
363
- },
364
- bcp47: function (tag) {
365
- var temp = (tag || '').split('_').join('-');
366
- var parts = temp.split('-');
367
- return parts.length > 1 ? (parts[0].toLowerCase() + '-' + parts[1].toUpperCase()) : temp;
368
- }
369
- };
404
+ $preferredLanguage,
405
+ $availableLanguageKeys = [],
406
+ $languageKeyAliases,
407
+ $fallbackLanguage,
408
+ $fallbackWasString,
409
+ $uses,
410
+ $nextLang,
411
+ $storageFactory,
412
+ $storageKey = $STORAGE_KEY,
413
+ $storagePrefix,
414
+ $missingTranslationHandlerFactory,
415
+ $interpolationFactory,
416
+ $interpolatorFactories = [],
417
+ $loaderFactory,
418
+ $cloakClassName = 'translate-cloak',
419
+ $loaderOptions,
420
+ $notFoundIndicatorLeft,
421
+ $notFoundIndicatorRight,
422
+ $postCompilingEnabled = false,
423
+ $forceAsyncReloadEnabled = false,
424
+ $nestedObjectDelimeter = '.',
425
+ $isReady = false,
426
+ $keepContent = false,
427
+ loaderCache,
428
+ directivePriority = 0,
429
+ statefulFilter = true,
430
+ postProcessFn,
431
+ uniformLanguageTagResolver = 'default',
432
+ languageTagResolver = {
433
+ 'default' : function (tag) {
434
+ return (tag || '').split('-').join('_');
435
+ },
436
+ java : function (tag) {
437
+ var temp = (tag || '').split('-').join('_');
438
+ var parts = temp.split('_');
439
+ return parts.length > 1 ? (parts[0].toLowerCase() + '_' + parts[1].toUpperCase()) : temp;
440
+ },
441
+ bcp47 : function (tag) {
442
+ var temp = (tag || '').split('_').join('-');
443
+ var parts = temp.split('-');
444
+ return parts.length > 1 ? (parts[0].toLowerCase() + '-' + parts[1].toUpperCase()) : temp;
445
+ },
446
+ 'iso639-1' : function (tag) {
447
+ var temp = (tag || '').split('_').join('-');
448
+ var parts = temp.split('-');
449
+ return parts[0].toLowerCase();
450
+ }
451
+ };
370
452
 
371
- var version = '2.7.2';
453
+ var version = '2.15.2';
372
454
 
373
455
  // tries to determine the browsers language
374
456
  var getFirstBrowserLanguage = function () {
@@ -379,9 +461,9 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
379
461
  }
380
462
 
381
463
  var nav = $windowProvider.$get().navigator,
382
- browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'],
383
- i,
384
- language;
464
+ browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'],
465
+ i,
466
+ language;
385
467
 
386
468
  // support for HTML 5.1 "navigator.languages"
387
469
  if (angular.isArray(nav.languages)) {
@@ -427,7 +509,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
427
509
  *
428
510
  * @returns {int} Index of search element.
429
511
  */
430
- var indexOf = function(array, searchElement) {
512
+ var indexOf = function (array, searchElement) {
431
513
  for (var i = 0, len = array.length; i < len; i++) {
432
514
  if (array[i] === searchElement) {
433
515
  return i;
@@ -445,21 +527,25 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
445
527
  *
446
528
  * @returns {string} The string stripped of whitespace from both ends
447
529
  */
448
- var trim = function() {
530
+ var trim = function () {
449
531
  return this.toString().replace(/^\s+|\s+$/g, '');
450
532
  };
451
533
 
452
534
  var negotiateLocale = function (preferred) {
535
+ if (!preferred) {
536
+ return;
537
+ }
453
538
 
454
539
  var avail = [],
455
- locale = angular.lowercase(preferred),
456
- i = 0,
457
- n = $availableLanguageKeys.length;
540
+ locale = angular.lowercase(preferred),
541
+ i = 0,
542
+ n = $availableLanguageKeys.length;
458
543
 
459
544
  for (; i < n; i++) {
460
545
  avail.push(angular.lowercase($availableLanguageKeys[i]));
461
546
  }
462
547
 
548
+ // Check for an exact match in our list of available keys
463
549
  if (indexOf(avail, locale) > -1) {
464
550
  return preferred;
465
551
  }
@@ -467,32 +553,33 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
467
553
  if ($languageKeyAliases) {
468
554
  var alias;
469
555
  for (var langKeyAlias in $languageKeyAliases) {
470
- var hasWildcardKey = false;
471
- var hasExactKey = Object.prototype.hasOwnProperty.call($languageKeyAliases, langKeyAlias) &&
472
- angular.lowercase(langKeyAlias) === angular.lowercase(preferred);
556
+ if ($languageKeyAliases.hasOwnProperty(langKeyAlias)) {
557
+ var hasWildcardKey = false;
558
+ var hasExactKey = Object.prototype.hasOwnProperty.call($languageKeyAliases, langKeyAlias) &&
559
+ angular.lowercase(langKeyAlias) === angular.lowercase(preferred);
473
560
 
474
- if (langKeyAlias.slice(-1) === '*') {
475
- hasWildcardKey = langKeyAlias.slice(0, -1) === preferred.slice(0, langKeyAlias.length-1);
476
- }
477
- if (hasExactKey || hasWildcardKey) {
478
- alias = $languageKeyAliases[langKeyAlias];
479
- if (indexOf(avail, angular.lowercase(alias)) > -1) {
480
- return alias;
561
+ if (langKeyAlias.slice(-1) === '*') {
562
+ hasWildcardKey = langKeyAlias.slice(0, -1) === preferred.slice(0, langKeyAlias.length - 1);
563
+ }
564
+ if (hasExactKey || hasWildcardKey) {
565
+ alias = $languageKeyAliases[langKeyAlias];
566
+ if (indexOf(avail, angular.lowercase(alias)) > -1) {
567
+ return alias;
568
+ }
481
569
  }
482
570
  }
483
571
  }
484
572
  }
485
573
 
486
- if (preferred) {
487
- var parts = preferred.split('_');
574
+ // Check for a language code without region
575
+ var parts = preferred.split('_');
488
576
 
489
- if (parts.length > 1 && indexOf(avail, angular.lowercase(parts[0])) > -1) {
490
- return parts[0];
491
- }
577
+ if (parts.length > 1 && indexOf(avail, angular.lowercase(parts[0])) > -1) {
578
+ return parts[0];
492
579
  }
493
580
 
494
- // If everything fails, just return the preferred, unchanged.
495
- return preferred;
581
+ // If everything fails, return undefined.
582
+ return;
496
583
  };
497
584
 
498
585
  /**
@@ -527,7 +614,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
527
614
  * registered with no language key. Invoking it with a language key returns the
528
615
  * related translation table.
529
616
  *
530
- * @param {string} key A language key.
617
+ * @param {string} langKey A language key.
531
618
  * @param {object} translationTable A plain old JavaScript object that represents a translation table.
532
619
  *
533
620
  */
@@ -572,6 +659,26 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
572
659
  return this;
573
660
  };
574
661
 
662
+ /**
663
+ * @ngdoc function
664
+ * @name pascalprecht.translate.$translateProvider#nestedObjectDelimeter
665
+ * @methodOf pascalprecht.translate.$translateProvider
666
+ *
667
+ * @description
668
+ *
669
+ * Let's you change the delimiter for namespaced translations.
670
+ * Default delimiter is `.`.
671
+ *
672
+ * @param {string} delimiter namespace separator
673
+ */
674
+ this.nestedObjectDelimeter = function (delimiter) {
675
+ if (!delimiter) {
676
+ return $nestedObjectDelimeter;
677
+ }
678
+ $nestedObjectDelimeter = delimiter;
679
+ return this;
680
+ };
681
+
575
682
  /**
576
683
  * @name flatObject
577
684
  * @private
@@ -597,10 +704,10 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
597
704
  if (angular.isObject(val)) {
598
705
  flatObject(val, path.concat(key), result, key);
599
706
  } else {
600
- keyWithPath = path.length ? ('' + path.join(NESTED_OBJECT_DELIMITER) + NESTED_OBJECT_DELIMITER + key) : key;
601
- if(path.length && key === prevKey){
707
+ keyWithPath = path.length ? ('' + path.join($nestedObjectDelimeter) + $nestedObjectDelimeter + key) : key;
708
+ if (path.length && key === prevKey) {
602
709
  // Create shortcut path (foo.bar == foo.bar.bar)
603
- keyWithShortPath = '' + path.join(NESTED_OBJECT_DELIMITER);
710
+ keyWithShortPath = '' + path.join($nestedObjectDelimeter);
604
711
  // Link it to original path
605
712
  result[keyWithShortPath] = '@:' + keyWithPath;
606
713
  }
@@ -671,7 +778,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
671
778
  return this;
672
779
  };
673
780
 
674
- /**
781
+ /**
675
782
  * @ngdoc function
676
783
  * @name pascalprecht.translate.$translateProvider#preferredLanguage
677
784
  * @methodOf pascalprecht.translate.$translateProvider
@@ -682,12 +789,13 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
682
789
  * only that it says which language to **prefer**.
683
790
  *
684
791
  * @param {string} langKey A language key.
685
- *
686
792
  */
687
- this.preferredLanguage = function(langKey) {
688
- setupPreferredLanguage(langKey);
689
- return this;
690
-
793
+ this.preferredLanguage = function (langKey) {
794
+ if (langKey) {
795
+ setupPreferredLanguage(langKey);
796
+ return this;
797
+ }
798
+ return $preferredLanguage;
691
799
  };
692
800
  var setupPreferredLanguage = function (langKey) {
693
801
  if (langKey) {
@@ -780,12 +888,12 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
780
888
  if (langKey) {
781
889
  if (angular.isString(langKey)) {
782
890
  $fallbackWasString = true;
783
- $fallbackLanguage = [ langKey ];
891
+ $fallbackLanguage = [langKey];
784
892
  } else if (angular.isArray(langKey)) {
785
893
  $fallbackWasString = false;
786
894
  $fallbackLanguage = langKey;
787
895
  }
788
- if (angular.isString($preferredLanguage) && indexOf($fallbackLanguage, $preferredLanguage) < 0) {
896
+ if (angular.isString($preferredLanguage) && indexOf($fallbackLanguage, $preferredLanguage) < 0) {
789
897
  $fallbackLanguage.push($preferredLanguage);
790
898
  }
791
899
 
@@ -825,7 +933,21 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
825
933
  return $uses;
826
934
  };
827
935
 
828
- /**
936
+ /**
937
+ * @ngdoc function
938
+ * @name pascalprecht.translate.$translateProvider#resolveClientLocale
939
+ * @methodOf pascalprecht.translate.$translateProvider
940
+ *
941
+ * @description
942
+ * This returns the current browser/client's language key. The result is processed with the configured uniform tag resolver.
943
+ *
944
+ * @returns {string} the current client/browser language key
945
+ */
946
+ this.resolveClientLocale = function () {
947
+ return getLocale();
948
+ };
949
+
950
+ /**
829
951
  * @ngdoc function
830
952
  * @name pascalprecht.translate.$translateProvider#storageKey
831
953
  * @methodOf pascalprecht.translate.$translateProvider
@@ -835,7 +957,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
835
957
  *
836
958
  * @param {string} key A key for the storage.
837
959
  */
838
- var storageKey = function(key) {
960
+ var storageKey = function (key) {
839
961
  if (!key) {
840
962
  if ($storagePrefix) {
841
963
  return $storagePrefix + $storageKey;
@@ -860,7 +982,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
860
982
  * @param {Object=} options Optional configuration object
861
983
  */
862
984
  this.useUrlLoader = function (url, options) {
863
- return this.useLoader('$translateUrlLoader', angular.extend({ url: url }, options));
985
+ return this.useLoader('$translateUrlLoader', angular.extend({url : url}, options));
864
986
  };
865
987
 
866
988
  /**
@@ -1091,7 +1213,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
1091
1213
  options = {};
1092
1214
  } else if (angular.isString(options)) {
1093
1215
  options = {
1094
- standard: options
1216
+ standard : options
1095
1217
  };
1096
1218
  }
1097
1219
 
@@ -1126,7 +1248,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
1126
1248
  if (!$availableLanguageKeys.length) {
1127
1249
  $preferredLanguage = locale;
1128
1250
  } else {
1129
- $preferredLanguage = negotiateLocale(locale);
1251
+ $preferredLanguage = negotiateLocale(locale) || locale;
1130
1252
  }
1131
1253
 
1132
1254
  return this;
@@ -1166,7 +1288,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
1166
1288
  *
1167
1289
  * @description
1168
1290
  * Registers a cache for internal $http based loaders.
1169
- * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}.
1291
+ * {@link pascalprecht.translate.$translationCache $translationCache}.
1170
1292
  * When false the cache will be disabled (default). When true or undefined
1171
1293
  * the cache will be a default (see $cacheFactory). When an object it will
1172
1294
  * be treat as a cache object itself: the usage is $http({cache: cache})
@@ -1237,6 +1359,48 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
1237
1359
  }
1238
1360
  };
1239
1361
 
1362
+ /**
1363
+ * @ngdoc function
1364
+ * @name pascalprecht.translate.$translateProvider#postProcess
1365
+ * @methodOf pascalprecht.translate.$translateProvider
1366
+ *
1367
+ * @description
1368
+ * The post processor will be intercept right after the translation result. It can modify the result.
1369
+ *
1370
+ * @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
1371
+ */
1372
+ this.postProcess = function (fn) {
1373
+ if (fn) {
1374
+ postProcessFn = fn;
1375
+ } else {
1376
+ postProcessFn = undefined;
1377
+ }
1378
+ return this;
1379
+ };
1380
+
1381
+ /**
1382
+ * @ngdoc function
1383
+ * @name pascalprecht.translate.$translateProvider#keepContent
1384
+ * @methodOf pascalprecht.translate.$translateProvider
1385
+ *
1386
+ * @description
1387
+ * If keepContent is set to true than translate directive will always use innerHTML
1388
+ * as a default translation
1389
+ *
1390
+ * Example:
1391
+ * <pre>
1392
+ * app.config(function ($translateProvider) {
1393
+ * $translateProvider.keepContent(true);
1394
+ * });
1395
+ * </pre>
1396
+ *
1397
+ * @param {boolean} value - valid values are true or false
1398
+ */
1399
+ this.keepContent = function (value) {
1400
+ $keepContent = !(!value);
1401
+ return this;
1402
+ };
1403
+
1240
1404
  /**
1241
1405
  * @ngdoc object
1242
1406
  * @name pascalprecht.translate.$translate
@@ -1261,1069 +1425,1344 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
1261
1425
  * is the translation id and the value the translation.
1262
1426
  * @param {object=} interpolateParams An object hash for dynamic values
1263
1427
  * @param {string} interpolationId The id of the interpolation to use
1428
+ * @param {string} defaultTranslationText the optional default translation text that is written as
1429
+ * as default text in case it is not found in any configured language
1430
+ * @param {string} forceLanguage A language to be used instead of the current language
1264
1431
  * @returns {object} promise
1265
1432
  */
1266
- this.$get = [
1267
- '$log',
1268
- '$injector',
1269
- '$rootScope',
1270
- '$q',
1271
- function ($log, $injector, $rootScope, $q) {
1272
-
1273
- var Storage,
1274
- defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'),
1275
- pendingLoader = false,
1276
- interpolatorHashMap = {},
1277
- langPromises = {},
1278
- fallbackIndex,
1279
- startFallbackIteration;
1280
-
1281
- var $translate = function (translationId, interpolateParams, interpolationId, defaultTranslationText) {
1282
-
1283
- // Duck detection: If the first argument is an array, a bunch of translations was requested.
1284
- // The result is an object.
1285
- if (angular.isArray(translationId)) {
1286
- // Inspired by Q.allSettled by Kris Kowal
1287
- // https://github.com/kriskowal/q/blob/b0fa72980717dc202ffc3cbf03b936e10ebbb9d7/q.js#L1553-1563
1288
- // This transforms all promises regardless resolved or rejected
1289
- var translateAll = function (translationIds) {
1290
- var results = {}; // storing the actual results
1291
- var promises = []; // promises to wait for
1292
- // Wraps the promise a) being always resolved and b) storing the link id->value
1293
- var translate = function (translationId) {
1294
- var deferred = $q.defer();
1295
- var regardless = function (value) {
1296
- results[translationId] = value;
1297
- deferred.resolve([translationId, value]);
1298
- };
1299
- // we don't care whether the promise was resolved or rejected; just store the values
1300
- $translate(translationId, interpolateParams, interpolationId, defaultTranslationText).then(regardless, regardless);
1301
- return deferred.promise;
1433
+ this.$get = ['$log', '$injector', '$rootScope', '$q', function ($log, $injector, $rootScope, $q) {
1434
+
1435
+ var Storage,
1436
+ defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'),
1437
+ pendingLoader = false,
1438
+ interpolatorHashMap = {},
1439
+ langPromises = {},
1440
+ fallbackIndex,
1441
+ startFallbackIteration;
1442
+
1443
+ var $translate = function (translationId, interpolateParams, interpolationId, defaultTranslationText, forceLanguage) {
1444
+ if (!$uses && $preferredLanguage) {
1445
+ $uses = $preferredLanguage;
1446
+ }
1447
+ var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses
1448
+ (negotiateLocale(forceLanguage) || forceLanguage) : $uses;
1449
+
1450
+ // Check forceLanguage is present
1451
+ if (forceLanguage) {
1452
+ loadTranslationsIfMissing(forceLanguage);
1453
+ }
1454
+
1455
+ // Duck detection: If the first argument is an array, a bunch of translations was requested.
1456
+ // The result is an object.
1457
+ if (angular.isArray(translationId)) {
1458
+ // Inspired by Q.allSettled by Kris Kowal
1459
+ // https://github.com/kriskowal/q/blob/b0fa72980717dc202ffc3cbf03b936e10ebbb9d7/q.js#L1553-1563
1460
+ // This transforms all promises regardless resolved or rejected
1461
+ var translateAll = function (translationIds) {
1462
+ var results = {}; // storing the actual results
1463
+ var promises = []; // promises to wait for
1464
+ // Wraps the promise a) being always resolved and b) storing the link id->value
1465
+ var translate = function (translationId) {
1466
+ var deferred = $q.defer();
1467
+ var regardless = function (value) {
1468
+ results[translationId] = value;
1469
+ deferred.resolve([translationId, value]);
1302
1470
  };
1303
- for (var i = 0, c = translationIds.length; i < c; i++) {
1304
- promises.push(translate(translationIds[i]));
1305
- }
1306
- // wait for all (including storing to results)
1307
- return $q.all(promises).then(function () {
1308
- // return the results
1309
- return results;
1310
- });
1471
+ // we don't care whether the promise was resolved or rejected; just store the values
1472
+ $translate(translationId, interpolateParams, interpolationId, defaultTranslationText, forceLanguage).then(regardless, regardless);
1473
+ return deferred.promise;
1311
1474
  };
1312
- return translateAll(translationId);
1313
- }
1475
+ for (var i = 0, c = translationIds.length; i < c; i++) {
1476
+ promises.push(translate(translationIds[i]));
1477
+ }
1478
+ // wait for all (including storing to results)
1479
+ return $q.all(promises).then(function () {
1480
+ // return the results
1481
+ return results;
1482
+ });
1483
+ };
1484
+ return translateAll(translationId);
1485
+ }
1314
1486
 
1315
- var deferred = $q.defer();
1487
+ var deferred = $q.defer();
1316
1488
 
1317
- // trim off any whitespace
1318
- if (translationId) {
1319
- translationId = trim.apply(translationId);
1320
- }
1489
+ // trim off any whitespace
1490
+ if (translationId) {
1491
+ translationId = trim.apply(translationId);
1492
+ }
1321
1493
 
1322
- var promiseToWaitFor = (function () {
1323
- var promise = $preferredLanguage ?
1324
- langPromises[$preferredLanguage] :
1325
- langPromises[$uses];
1494
+ var promiseToWaitFor = (function () {
1495
+ var promise = $preferredLanguage ?
1496
+ langPromises[$preferredLanguage] :
1497
+ langPromises[uses];
1326
1498
 
1327
- fallbackIndex = 0;
1499
+ fallbackIndex = 0;
1328
1500
 
1329
- if ($storageFactory && !promise) {
1330
- // looks like there's no pending promise for $preferredLanguage or
1331
- // $uses. Maybe there's one pending for a language that comes from
1332
- // storage.
1333
- var langKey = Storage.get($storageKey);
1334
- promise = langPromises[langKey];
1335
-
1336
- if ($fallbackLanguage && $fallbackLanguage.length) {
1337
- var index = indexOf($fallbackLanguage, langKey);
1338
- // maybe the language from storage is also defined as fallback language
1339
- // we increase the fallback language index to not search in that language
1340
- // as fallback, since it's probably the first used language
1341
- // in that case the index starts after the first element
1342
- fallbackIndex = (index === 0) ? 1 : 0;
1343
-
1344
- // but we can make sure to ALWAYS fallback to preferred language at least
1345
- if (indexOf($fallbackLanguage, $preferredLanguage) < 0) {
1346
- $fallbackLanguage.push($preferredLanguage);
1347
- }
1501
+ if ($storageFactory && !promise) {
1502
+ // looks like there's no pending promise for $preferredLanguage or
1503
+ // $uses. Maybe there's one pending for a language that comes from
1504
+ // storage.
1505
+ var langKey = Storage.get($storageKey);
1506
+ promise = langPromises[langKey];
1507
+
1508
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1509
+ var index = indexOf($fallbackLanguage, langKey);
1510
+ // maybe the language from storage is also defined as fallback language
1511
+ // we increase the fallback language index to not search in that language
1512
+ // as fallback, since it's probably the first used language
1513
+ // in that case the index starts after the first element
1514
+ fallbackIndex = (index === 0) ? 1 : 0;
1515
+
1516
+ // but we can make sure to ALWAYS fallback to preferred language at least
1517
+ if (indexOf($fallbackLanguage, $preferredLanguage) < 0) {
1518
+ $fallbackLanguage.push($preferredLanguage);
1348
1519
  }
1349
1520
  }
1350
- return promise;
1351
- }());
1352
-
1353
- if (!promiseToWaitFor) {
1354
- // no promise to wait for? okay. Then there's no loader registered
1355
- // nor is a one pending for language that comes from storage.
1356
- // We can just translate.
1357
- determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText).then(deferred.resolve, deferred.reject);
1358
- } else {
1359
- var promiseResolved = function () {
1360
- determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText).then(deferred.resolve, deferred.reject);
1361
- };
1362
- promiseResolved.displayName = 'promiseResolved';
1363
-
1364
- promiseToWaitFor['finally'](promiseResolved, deferred.reject);
1365
1521
  }
1366
- return deferred.promise;
1367
- };
1522
+ return promise;
1523
+ }());
1524
+
1525
+ if (!promiseToWaitFor) {
1526
+ // no promise to wait for? okay. Then there's no loader registered
1527
+ // nor is a one pending for language that comes from storage.
1528
+ // We can just translate.
1529
+ determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText, uses).then(deferred.resolve, deferred.reject);
1530
+ } else {
1531
+ var promiseResolved = function () {
1532
+ // $uses may have changed while waiting
1533
+ if (!forceLanguage) {
1534
+ uses = $uses;
1535
+ }
1536
+ determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText, uses).then(deferred.resolve, deferred.reject);
1537
+ };
1538
+ promiseResolved.displayName = 'promiseResolved';
1368
1539
 
1369
- /**
1370
- * @name applyNotFoundIndicators
1371
- * @private
1372
- *
1373
- * @description
1374
- * Applies not fount indicators to given translation id, if needed.
1375
- * This function gets only executed, if a translation id doesn't exist,
1376
- * which is why a translation id is expected as argument.
1377
- *
1378
- * @param {string} translationId Translation id.
1379
- * @returns {string} Same as given translation id but applied with not found
1380
- * indicators.
1381
- */
1382
- var applyNotFoundIndicators = function (translationId) {
1383
- // applying notFoundIndicators
1384
- if ($notFoundIndicatorLeft) {
1385
- translationId = [$notFoundIndicatorLeft, translationId].join(' ');
1386
- }
1387
- if ($notFoundIndicatorRight) {
1388
- translationId = [translationId, $notFoundIndicatorRight].join(' ');
1389
- }
1390
- return translationId;
1391
- };
1540
+ promiseToWaitFor['finally'](promiseResolved)['catch'](angular.noop); // we don't care about errors here, already handled
1541
+ }
1542
+ return deferred.promise;
1543
+ };
1392
1544
 
1393
- /**
1394
- * @name useLanguage
1395
- * @private
1396
- *
1397
- * @description
1398
- * Makes actual use of a language by setting a given language key as used
1399
- * language and informs registered interpolators to also use the given
1400
- * key as locale.
1401
- *
1402
- * @param {key} Locale key.
1403
- */
1404
- var useLanguage = function (key) {
1405
- $uses = key;
1406
- $rootScope.$emit('$translateChangeSuccess', {language: key});
1545
+ /**
1546
+ * @name applyNotFoundIndicators
1547
+ * @private
1548
+ *
1549
+ * @description
1550
+ * Applies not fount indicators to given translation id, if needed.
1551
+ * This function gets only executed, if a translation id doesn't exist,
1552
+ * which is why a translation id is expected as argument.
1553
+ *
1554
+ * @param {string} translationId Translation id.
1555
+ * @returns {string} Same as given translation id but applied with not found
1556
+ * indicators.
1557
+ */
1558
+ var applyNotFoundIndicators = function (translationId) {
1559
+ // applying notFoundIndicators
1560
+ if ($notFoundIndicatorLeft) {
1561
+ translationId = [$notFoundIndicatorLeft, translationId].join(' ');
1562
+ }
1563
+ if ($notFoundIndicatorRight) {
1564
+ translationId = [translationId, $notFoundIndicatorRight].join(' ');
1565
+ }
1566
+ return translationId;
1567
+ };
1407
1568
 
1408
- if ($storageFactory) {
1409
- Storage.put($translate.storageKey(), $uses);
1410
- }
1411
- // inform default interpolator
1412
- defaultInterpolator.setLocale($uses);
1569
+ /**
1570
+ * @name useLanguage
1571
+ * @private
1572
+ *
1573
+ * @description
1574
+ * Makes actual use of a language by setting a given language key as used
1575
+ * language and informs registered interpolators to also use the given
1576
+ * key as locale.
1577
+ *
1578
+ * @param {string} key Locale key.
1579
+ */
1580
+ var useLanguage = function (key) {
1581
+ $uses = key;
1582
+
1583
+ // make sure to store new language key before triggering success event
1584
+ if ($storageFactory) {
1585
+ Storage.put($translate.storageKey(), $uses);
1586
+ }
1413
1587
 
1414
- var eachInterpolator = function (interpolator, id) {
1415
- interpolatorHashMap[id].setLocale($uses);
1416
- };
1417
- eachInterpolator.displayName = 'eachInterpolatorLocaleSetter';
1588
+ $rootScope.$emit('$translateChangeSuccess', {language : key});
1418
1589
 
1419
- // inform all others too!
1420
- angular.forEach(interpolatorHashMap, eachInterpolator);
1421
- $rootScope.$emit('$translateChangeEnd', {language: key});
1590
+ // inform default interpolator
1591
+ defaultInterpolator.setLocale($uses);
1592
+
1593
+ var eachInterpolator = function (interpolator, id) {
1594
+ interpolatorHashMap[id].setLocale($uses);
1422
1595
  };
1596
+ eachInterpolator.displayName = 'eachInterpolatorLocaleSetter';
1423
1597
 
1424
- /**
1425
- * @name loadAsync
1426
- * @private
1427
- *
1428
- * @description
1429
- * Kicks of registered async loader using `$injector` and applies existing
1430
- * loader options. When resolved, it updates translation tables accordingly
1431
- * or rejects with given language key.
1432
- *
1433
- * @param {string} key Language key.
1434
- * @return {Promise} A promise.
1435
- */
1436
- var loadAsync = function (key) {
1437
- if (!key) {
1438
- throw 'No language key specified for loading.';
1439
- }
1598
+ // inform all others too!
1599
+ angular.forEach(interpolatorHashMap, eachInterpolator);
1600
+ $rootScope.$emit('$translateChangeEnd', {language : key});
1601
+ };
1440
1602
 
1441
- var deferred = $q.defer();
1603
+ /**
1604
+ * @name loadAsync
1605
+ * @private
1606
+ *
1607
+ * @description
1608
+ * Kicks off registered async loader using `$injector` and applies existing
1609
+ * loader options. When resolved, it updates translation tables accordingly
1610
+ * or rejects with given language key.
1611
+ *
1612
+ * @param {string} key Language key.
1613
+ * @return {Promise} A promise.
1614
+ */
1615
+ var loadAsync = function (key) {
1616
+ if (!key) {
1617
+ throw 'No language key specified for loading.';
1618
+ }
1442
1619
 
1443
- $rootScope.$emit('$translateLoadingStart', {language: key});
1444
- pendingLoader = true;
1620
+ var deferred = $q.defer();
1445
1621
 
1446
- var cache = loaderCache;
1447
- if (typeof(cache) === 'string') {
1448
- // getting on-demand instance of loader
1449
- cache = $injector.get(cache);
1450
- }
1622
+ $rootScope.$emit('$translateLoadingStart', {language : key});
1623
+ pendingLoader = true;
1451
1624
 
1452
- var loaderOptions = angular.extend({}, $loaderOptions, {
1453
- key: key,
1454
- $http: angular.extend({}, {
1455
- cache: cache
1456
- }, $loaderOptions.$http)
1457
- });
1625
+ var cache = loaderCache;
1626
+ if (typeof(cache) === 'string') {
1627
+ // getting on-demand instance of loader
1628
+ cache = $injector.get(cache);
1629
+ }
1458
1630
 
1459
- var onLoaderSuccess = function (data) {
1460
- var translationTable = {};
1461
- $rootScope.$emit('$translateLoadingSuccess', {language: key});
1631
+ var loaderOptions = angular.extend({}, $loaderOptions, {
1632
+ key : key,
1633
+ $http : angular.extend({}, {
1634
+ cache : cache
1635
+ }, $loaderOptions.$http)
1636
+ });
1462
1637
 
1463
- if (angular.isArray(data)) {
1464
- angular.forEach(data, function (table) {
1465
- angular.extend(translationTable, flatObject(table));
1466
- });
1467
- } else {
1468
- angular.extend(translationTable, flatObject(data));
1469
- }
1470
- pendingLoader = false;
1471
- deferred.resolve({
1472
- key: key,
1473
- table: translationTable
1638
+ var onLoaderSuccess = function (data) {
1639
+ var translationTable = {};
1640
+ $rootScope.$emit('$translateLoadingSuccess', {language : key});
1641
+
1642
+ if (angular.isArray(data)) {
1643
+ angular.forEach(data, function (table) {
1644
+ angular.extend(translationTable, flatObject(table));
1474
1645
  });
1475
- $rootScope.$emit('$translateLoadingEnd', {language: key});
1476
- };
1477
- onLoaderSuccess.displayName = 'onLoaderSuccess';
1646
+ } else {
1647
+ angular.extend(translationTable, flatObject(data));
1648
+ }
1649
+ pendingLoader = false;
1650
+ deferred.resolve({
1651
+ key : key,
1652
+ table : translationTable
1653
+ });
1654
+ $rootScope.$emit('$translateLoadingEnd', {language : key});
1655
+ };
1656
+ onLoaderSuccess.displayName = 'onLoaderSuccess';
1478
1657
 
1479
- var onLoaderError = function (key) {
1480
- $rootScope.$emit('$translateLoadingError', {language: key});
1481
- deferred.reject(key);
1482
- $rootScope.$emit('$translateLoadingEnd', {language: key});
1483
- };
1484
- onLoaderError.displayName = 'onLoaderError';
1658
+ var onLoaderError = function (key) {
1659
+ $rootScope.$emit('$translateLoadingError', {language : key});
1660
+ deferred.reject(key);
1661
+ $rootScope.$emit('$translateLoadingEnd', {language : key});
1662
+ };
1663
+ onLoaderError.displayName = 'onLoaderError';
1485
1664
 
1486
- $injector.get($loaderFactory)(loaderOptions)
1487
- .then(onLoaderSuccess, onLoaderError);
1665
+ $injector.get($loaderFactory)(loaderOptions)
1666
+ .then(onLoaderSuccess, onLoaderError);
1488
1667
 
1489
- return deferred.promise;
1490
- };
1668
+ return deferred.promise;
1669
+ };
1491
1670
 
1492
- if ($storageFactory) {
1493
- Storage = $injector.get($storageFactory);
1671
+ if ($storageFactory) {
1672
+ Storage = $injector.get($storageFactory);
1494
1673
 
1495
- if (!Storage.get || !Storage.put) {
1496
- throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or put() method!');
1497
- }
1674
+ if (!Storage.get || !Storage.put) {
1675
+ throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or put() method!');
1498
1676
  }
1677
+ }
1499
1678
 
1500
- // if we have additional interpolations that were added via
1501
- // $translateProvider.addInterpolation(), we have to map'em
1502
- if ($interpolatorFactories.length) {
1503
- var eachInterpolationFactory = function (interpolatorFactory) {
1504
- var interpolator = $injector.get(interpolatorFactory);
1505
- // setting initial locale for each interpolation service
1506
- interpolator.setLocale($preferredLanguage || $uses);
1507
- // make'em recognizable through id
1508
- interpolatorHashMap[interpolator.getInterpolationIdentifier()] = interpolator;
1509
- };
1510
- eachInterpolationFactory.displayName = 'interpolationFactoryAdder';
1679
+ // if we have additional interpolations that were added via
1680
+ // $translateProvider.addInterpolation(), we have to map'em
1681
+ if ($interpolatorFactories.length) {
1682
+ var eachInterpolationFactory = function (interpolatorFactory) {
1683
+ var interpolator = $injector.get(interpolatorFactory);
1684
+ // setting initial locale for each interpolation service
1685
+ interpolator.setLocale($preferredLanguage || $uses);
1686
+ // make'em recognizable through id
1687
+ interpolatorHashMap[interpolator.getInterpolationIdentifier()] = interpolator;
1688
+ };
1689
+ eachInterpolationFactory.displayName = 'interpolationFactoryAdder';
1690
+
1691
+ angular.forEach($interpolatorFactories, eachInterpolationFactory);
1692
+ }
1511
1693
 
1512
- angular.forEach($interpolatorFactories, eachInterpolationFactory);
1694
+ /**
1695
+ * @name getTranslationTable
1696
+ * @private
1697
+ *
1698
+ * @description
1699
+ * Returns a promise that resolves to the translation table
1700
+ * or is rejected if an error occurred.
1701
+ *
1702
+ * @param langKey
1703
+ * @returns {Q.promise}
1704
+ */
1705
+ var getTranslationTable = function (langKey) {
1706
+ var deferred = $q.defer();
1707
+ if (Object.prototype.hasOwnProperty.call($translationTable, langKey)) {
1708
+ deferred.resolve($translationTable[langKey]);
1709
+ } else if (langPromises[langKey]) {
1710
+ var onResolve = function (data) {
1711
+ translations(data.key, data.table);
1712
+ deferred.resolve(data.table);
1713
+ };
1714
+ onResolve.displayName = 'translationTableResolver';
1715
+ langPromises[langKey].then(onResolve, deferred.reject);
1716
+ } else {
1717
+ deferred.reject();
1513
1718
  }
1719
+ return deferred.promise;
1720
+ };
1514
1721
 
1515
- /**
1516
- * @name getTranslationTable
1517
- * @private
1518
- *
1519
- * @description
1520
- * Returns a promise that resolves to the translation table
1521
- * or is rejected if an error occurred.
1522
- *
1523
- * @param langKey
1524
- * @returns {Q.promise}
1525
- */
1526
- var getTranslationTable = function (langKey) {
1527
- var deferred = $q.defer();
1528
- if (Object.prototype.hasOwnProperty.call($translationTable, langKey)) {
1529
- deferred.resolve($translationTable[langKey]);
1530
- } else if (langPromises[langKey]) {
1531
- var onResolve = function (data) {
1532
- translations(data.key, data.table);
1533
- deferred.resolve(data.table);
1534
- };
1535
- onResolve.displayName = 'translationTableResolver';
1536
- langPromises[langKey].then(onResolve, deferred.reject);
1722
+ /**
1723
+ * @name getFallbackTranslation
1724
+ * @private
1725
+ *
1726
+ * @description
1727
+ * Returns a promise that will resolve to the translation
1728
+ * or be rejected if no translation was found for the language.
1729
+ * This function is currently only used for fallback language translation.
1730
+ *
1731
+ * @param langKey The language to translate to.
1732
+ * @param translationId
1733
+ * @param interpolateParams
1734
+ * @param Interpolator
1735
+ * @param sanitizeStrategy
1736
+ * @returns {Q.promise}
1737
+ */
1738
+ var getFallbackTranslation = function (langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy) {
1739
+ var deferred = $q.defer();
1740
+
1741
+ var onResolve = function (translationTable) {
1742
+ if (Object.prototype.hasOwnProperty.call(translationTable, translationId) && translationTable[translationId] !== null) {
1743
+ Interpolator.setLocale(langKey);
1744
+ var translation = translationTable[translationId];
1745
+ if (translation.substr(0, 2) === '@:') {
1746
+ getFallbackTranslation(langKey, translation.substr(2), interpolateParams, Interpolator, sanitizeStrategy)
1747
+ .then(deferred.resolve, deferred.reject);
1748
+ } else {
1749
+ var interpolatedValue = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'service', sanitizeStrategy, translationId);
1750
+ interpolatedValue = applyPostProcessing(translationId, translationTable[translationId], interpolatedValue, interpolateParams, langKey);
1751
+
1752
+ deferred.resolve(interpolatedValue);
1753
+
1754
+ }
1755
+ Interpolator.setLocale($uses);
1537
1756
  } else {
1538
1757
  deferred.reject();
1539
1758
  }
1540
- return deferred.promise;
1541
1759
  };
1760
+ onResolve.displayName = 'fallbackTranslationResolver';
1542
1761
 
1543
- /**
1544
- * @name getFallbackTranslation
1545
- * @private
1546
- *
1547
- * @description
1548
- * Returns a promise that will resolve to the translation
1549
- * or be rejected if no translation was found for the language.
1550
- * This function is currently only used for fallback language translation.
1551
- *
1552
- * @param langKey The language to translate to.
1553
- * @param translationId
1554
- * @param interpolateParams
1555
- * @param Interpolator
1556
- * @returns {Q.promise}
1557
- */
1558
- var getFallbackTranslation = function (langKey, translationId, interpolateParams, Interpolator) {
1559
- var deferred = $q.defer();
1560
-
1561
- var onResolve = function (translationTable) {
1562
- if (Object.prototype.hasOwnProperty.call(translationTable, translationId)) {
1563
- Interpolator.setLocale(langKey);
1564
- var translation = translationTable[translationId];
1565
- if (translation.substr(0, 2) === '@:') {
1566
- getFallbackTranslation(langKey, translation.substr(2), interpolateParams, Interpolator)
1567
- .then(deferred.resolve, deferred.reject);
1568
- } else {
1569
- deferred.resolve(Interpolator.interpolate(translationTable[translationId], interpolateParams));
1570
- }
1571
- Interpolator.setLocale($uses);
1572
- } else {
1573
- deferred.reject();
1574
- }
1575
- };
1576
- onResolve.displayName = 'fallbackTranslationResolver';
1577
-
1578
- getTranslationTable(langKey).then(onResolve, deferred.reject);
1762
+ getTranslationTable(langKey).then(onResolve, deferred.reject);
1579
1763
 
1580
- return deferred.promise;
1581
- };
1582
-
1583
- /**
1584
- * @name getFallbackTranslationInstant
1585
- * @private
1586
- *
1587
- * @description
1588
- * Returns a translation
1589
- * This function is currently only used for fallback language translation.
1590
- *
1591
- * @param langKey The language to translate to.
1592
- * @param translationId
1593
- * @param interpolateParams
1594
- * @param Interpolator
1595
- * @returns {string} translation
1596
- */
1597
- var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator) {
1598
- var result, translationTable = $translationTable[langKey];
1764
+ return deferred.promise;
1765
+ };
1599
1766
 
1600
- if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId)) {
1601
- Interpolator.setLocale(langKey);
1602
- result = Interpolator.interpolate(translationTable[translationId], interpolateParams);
1603
- if (result.substr(0, 2) === '@:') {
1604
- return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator);
1767
+ /**
1768
+ * @name getFallbackTranslationInstant
1769
+ * @private
1770
+ *
1771
+ * @description
1772
+ * Returns a translation
1773
+ * This function is currently only used for fallback language translation.
1774
+ *
1775
+ * @param langKey The language to translate to.
1776
+ * @param translationId
1777
+ * @param interpolateParams
1778
+ * @param Interpolator
1779
+ * @param sanitizeStrategy sanitize strategy override
1780
+ *
1781
+ * @returns {string} translation
1782
+ */
1783
+ var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy) {
1784
+ var result, translationTable = $translationTable[langKey];
1785
+
1786
+ if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId) && translationTable[translationId] !== null) {
1787
+ Interpolator.setLocale(langKey);
1788
+ result = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'filter', sanitizeStrategy, translationId);
1789
+ result = applyPostProcessing(translationId, translationTable[translationId], result, interpolateParams, langKey, sanitizeStrategy);
1790
+ // workaround for TrustedValueHolderType
1791
+ if (!angular.isString(result) && angular.isFunction(result.$$unwrapTrustedValue)) {
1792
+ var result2 = result.$$unwrapTrustedValue();
1793
+ if (result2.substr(0, 2) === '@:') {
1794
+ return getFallbackTranslationInstant(langKey, result2.substr(2), interpolateParams, Interpolator, sanitizeStrategy);
1605
1795
  }
1606
- Interpolator.setLocale($uses);
1796
+ } else if (result.substr(0, 2) === '@:') {
1797
+ return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator, sanitizeStrategy);
1607
1798
  }
1799
+ Interpolator.setLocale($uses);
1800
+ }
1608
1801
 
1609
- return result;
1610
- };
1802
+ return result;
1803
+ };
1611
1804
 
1612
1805
 
1613
- /**
1614
- * @name translateByHandler
1615
- * @private
1616
- *
1617
- * Translate by missing translation handler.
1618
- *
1619
- * @param translationId
1620
- * @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is
1621
- * absent
1622
- */
1623
- var translateByHandler = function (translationId, interpolateParams) {
1624
- // If we have a handler factory - we might also call it here to determine if it provides
1625
- // a default text for a translationid that can't be found anywhere in our tables
1626
- if ($missingTranslationHandlerFactory) {
1627
- var resultString = $injector.get($missingTranslationHandlerFactory)(translationId, $uses, interpolateParams);
1628
- if (resultString !== undefined) {
1629
- return resultString;
1630
- } else {
1631
- return translationId;
1806
+ /**
1807
+ * @name translateByHandler
1808
+ * @private
1809
+ *
1810
+ * Translate by missing translation handler.
1811
+ *
1812
+ * @param translationId
1813
+ * @param interpolateParams
1814
+ * @param defaultTranslationText
1815
+ * @param sanitizeStrategy sanitize strategy override
1816
+ *
1817
+ * @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is
1818
+ * absent
1819
+ */
1820
+ var translateByHandler = function (translationId, interpolateParams, defaultTranslationText, sanitizeStrategy) {
1821
+ // If we have a handler factory - we might also call it here to determine if it provides
1822
+ // a default text for a translationid that can't be found anywhere in our tables
1823
+ if ($missingTranslationHandlerFactory) {
1824
+ return $injector.get($missingTranslationHandlerFactory)(translationId, $uses, interpolateParams, defaultTranslationText, sanitizeStrategy);
1825
+ } else {
1826
+ return translationId;
1827
+ }
1828
+ };
1829
+
1830
+ /**
1831
+ * @name resolveForFallbackLanguage
1832
+ * @private
1833
+ *
1834
+ * Recursive helper function for fallbackTranslation that will sequentially look
1835
+ * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
1836
+ *
1837
+ * @param fallbackLanguageIndex
1838
+ * @param translationId
1839
+ * @param interpolateParams
1840
+ * @param Interpolator
1841
+ * @param defaultTranslationText
1842
+ * @param sanitizeStrategy
1843
+ * @returns {Q.promise} Promise that will resolve to the translation.
1844
+ */
1845
+ var resolveForFallbackLanguage = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy) {
1846
+ var deferred = $q.defer();
1847
+
1848
+ if (fallbackLanguageIndex < $fallbackLanguage.length) {
1849
+ var langKey = $fallbackLanguage[fallbackLanguageIndex];
1850
+ getFallbackTranslation(langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy).then(
1851
+ function (data) {
1852
+ deferred.resolve(data);
1853
+ },
1854
+ function () {
1855
+ // Look in the next fallback language for a translation.
1856
+ // It delays the resolving by passing another promise to resolve.
1857
+ return resolveForFallbackLanguage(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy).then(deferred.resolve, deferred.reject);
1632
1858
  }
1859
+ );
1860
+ } else {
1861
+ // No translation found in any fallback language
1862
+ // if a default translation text is set in the directive, then return this as a result
1863
+ if (defaultTranslationText) {
1864
+ deferred.resolve(defaultTranslationText);
1633
1865
  } else {
1634
- return translationId;
1635
- }
1636
- };
1866
+ var missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, defaultTranslationText);
1637
1867
 
1638
- /**
1639
- * @name resolveForFallbackLanguage
1640
- * @private
1641
- *
1642
- * Recursive helper function for fallbackTranslation that will sequentially look
1643
- * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
1644
- *
1645
- * @param fallbackLanguageIndex
1646
- * @param translationId
1647
- * @param interpolateParams
1648
- * @param Interpolator
1649
- * @returns {Q.promise} Promise that will resolve to the translation.
1650
- */
1651
- var resolveForFallbackLanguage = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, defaultTranslationText) {
1652
- var deferred = $q.defer();
1653
-
1654
- if (fallbackLanguageIndex < $fallbackLanguage.length) {
1655
- var langKey = $fallbackLanguage[fallbackLanguageIndex];
1656
- getFallbackTranslation(langKey, translationId, interpolateParams, Interpolator).then(
1657
- deferred.resolve,
1658
- function () {
1659
- // Look in the next fallback language for a translation.
1660
- // It delays the resolving by passing another promise to resolve.
1661
- resolveForFallbackLanguage(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator, defaultTranslationText).then(deferred.resolve);
1662
- }
1663
- );
1664
- } else {
1665
- // No translation found in any fallback language
1666
- // if a default translation text is set in the directive, then return this as a result
1667
- if (defaultTranslationText) {
1668
- deferred.resolve(defaultTranslationText);
1868
+ // if no default translation is set and an error handler is defined, send it to the handler
1869
+ // and then return the result if it isn't undefined
1870
+ if ($missingTranslationHandlerFactory && missingTranslationHandlerTranslation) {
1871
+ deferred.resolve(missingTranslationHandlerTranslation);
1669
1872
  } else {
1670
- // if no default translation is set and an error handler is defined, send it to the handler
1671
- // and then return the result
1672
- deferred.resolve(translateByHandler(translationId, interpolateParams));
1873
+ deferred.reject(applyNotFoundIndicators(translationId));
1673
1874
  }
1674
1875
  }
1675
- return deferred.promise;
1676
- };
1876
+ }
1877
+ return deferred.promise;
1878
+ };
1677
1879
 
1678
- /**
1679
- * @name resolveForFallbackLanguageInstant
1680
- * @private
1681
- *
1682
- * Recursive helper function for fallbackTranslation that will sequentially look
1683
- * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
1684
- *
1685
- * @param fallbackLanguageIndex
1686
- * @param translationId
1687
- * @param interpolateParams
1688
- * @param Interpolator
1689
- * @returns {string} translation
1690
- */
1691
- var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator) {
1692
- var result;
1693
-
1694
- if (fallbackLanguageIndex < $fallbackLanguage.length) {
1695
- var langKey = $fallbackLanguage[fallbackLanguageIndex];
1696
- result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator);
1697
- if (!result) {
1698
- result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator);
1699
- }
1880
+ /**
1881
+ * @name resolveForFallbackLanguageInstant
1882
+ * @private
1883
+ *
1884
+ * Recursive helper function for fallbackTranslation that will sequentially look
1885
+ * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
1886
+ *
1887
+ * @param fallbackLanguageIndex
1888
+ * @param translationId
1889
+ * @param interpolateParams
1890
+ * @param Interpolator
1891
+ * @param sanitizeStrategy
1892
+ * @returns {string} translation
1893
+ */
1894
+ var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, sanitizeStrategy) {
1895
+ var result;
1896
+
1897
+ if (fallbackLanguageIndex < $fallbackLanguage.length) {
1898
+ var langKey = $fallbackLanguage[fallbackLanguageIndex];
1899
+ result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy);
1900
+ if (!result && result !== '') {
1901
+ result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator);
1700
1902
  }
1701
- return result;
1702
- };
1903
+ }
1904
+ return result;
1905
+ };
1703
1906
 
1704
- /**
1705
- * Translates with the usage of the fallback languages.
1706
- *
1707
- * @param translationId
1708
- * @param interpolateParams
1709
- * @param Interpolator
1710
- * @returns {Q.promise} Promise, that resolves to the translation.
1711
- */
1712
- var fallbackTranslation = function (translationId, interpolateParams, Interpolator, defaultTranslationText) {
1713
- // Start with the fallbackLanguage with index 0
1714
- return resolveForFallbackLanguage((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, defaultTranslationText);
1715
- };
1907
+ /**
1908
+ * Translates with the usage of the fallback languages.
1909
+ *
1910
+ * @param translationId
1911
+ * @param interpolateParams
1912
+ * @param Interpolator
1913
+ * @param defaultTranslationText
1914
+ * @param sanitizeStrategy
1915
+ * @returns {Q.promise} Promise, that resolves to the translation.
1916
+ */
1917
+ var fallbackTranslation = function (translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy) {
1918
+ // Start with the fallbackLanguage with index 0
1919
+ return resolveForFallbackLanguage((startFallbackIteration > 0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy);
1920
+ };
1716
1921
 
1717
- /**
1718
- * Translates with the usage of the fallback languages.
1719
- *
1720
- * @param translationId
1721
- * @param interpolateParams
1722
- * @param Interpolator
1723
- * @returns {String} translation
1724
- */
1725
- var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator) {
1726
- // Start with the fallbackLanguage with index 0
1727
- return resolveForFallbackLanguageInstant((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator);
1728
- };
1922
+ /**
1923
+ * Translates with the usage of the fallback languages.
1924
+ *
1925
+ * @param translationId
1926
+ * @param interpolateParams
1927
+ * @param Interpolator
1928
+ * @param sanitizeStrategy
1929
+ * @returns {String} translation
1930
+ */
1931
+ var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator, sanitizeStrategy) {
1932
+ // Start with the fallbackLanguage with index 0
1933
+ return resolveForFallbackLanguageInstant((startFallbackIteration > 0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, sanitizeStrategy);
1934
+ };
1729
1935
 
1730
- var determineTranslation = function (translationId, interpolateParams, interpolationId, defaultTranslationText) {
1936
+ var determineTranslation = function (translationId, interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy) {
1731
1937
 
1732
- var deferred = $q.defer();
1938
+ var deferred = $q.defer();
1733
1939
 
1734
- var table = $uses ? $translationTable[$uses] : $translationTable,
1735
- Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator;
1940
+ var table = uses ? $translationTable[uses] : $translationTable,
1941
+ Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator;
1736
1942
 
1737
- // if the translation id exists, we can just interpolate it
1738
- if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {
1739
- var translation = table[translationId];
1943
+ // if the translation id exists, we can just interpolate it
1944
+ if (table && Object.prototype.hasOwnProperty.call(table, translationId) && table[translationId] !== null) {
1945
+ var translation = table[translationId];
1740
1946
 
1741
- // If using link, rerun $translate with linked translationId and return it
1742
- if (translation.substr(0, 2) === '@:') {
1947
+ // If using link, rerun $translate with linked translationId and return it
1948
+ if (translation.substr(0, 2) === '@:') {
1743
1949
 
1744
- $translate(translation.substr(2), interpolateParams, interpolationId, defaultTranslationText)
1745
- .then(deferred.resolve, deferred.reject);
1950
+ $translate(translation.substr(2), interpolateParams, interpolationId, defaultTranslationText, uses)
1951
+ .then(deferred.resolve, deferred.reject);
1952
+ } else {
1953
+ //
1954
+ var resolvedTranslation = Interpolator.interpolate(translation, interpolateParams, 'service', sanitizeStrategy, translationId);
1955
+ resolvedTranslation = applyPostProcessing(translationId, translation, resolvedTranslation, interpolateParams, uses);
1956
+ deferred.resolve(resolvedTranslation);
1957
+ }
1958
+ } else {
1959
+ var missingTranslationHandlerTranslation;
1960
+ // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
1961
+ if ($missingTranslationHandlerFactory && !pendingLoader) {
1962
+ missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, defaultTranslationText);
1963
+ }
1964
+
1965
+ // since we couldn't translate the inital requested translation id,
1966
+ // we try it now with one or more fallback languages, if fallback language(s) is
1967
+ // configured.
1968
+ if (uses && $fallbackLanguage && $fallbackLanguage.length) {
1969
+ fallbackTranslation(translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy)
1970
+ .then(function (translation) {
1971
+ deferred.resolve(translation);
1972
+ }, function (_translationId) {
1973
+ deferred.reject(applyNotFoundIndicators(_translationId));
1974
+ });
1975
+ } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
1976
+ // looks like the requested translation id doesn't exists.
1977
+ // Now, if there is a registered handler for missing translations and no
1978
+ // asyncLoader is pending, we execute the handler
1979
+ if (defaultTranslationText) {
1980
+ deferred.resolve(defaultTranslationText);
1746
1981
  } else {
1747
- deferred.resolve(Interpolator.interpolate(translation, interpolateParams));
1982
+ deferred.resolve(missingTranslationHandlerTranslation);
1748
1983
  }
1749
1984
  } else {
1750
- var missingTranslationHandlerTranslation;
1751
- // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
1752
- if ($missingTranslationHandlerFactory && !pendingLoader) {
1753
- missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams);
1754
- }
1755
-
1756
- // since we couldn't translate the inital requested translation id,
1757
- // we try it now with one or more fallback languages, if fallback language(s) is
1758
- // configured.
1759
- if ($uses && $fallbackLanguage && $fallbackLanguage.length) {
1760
- fallbackTranslation(translationId, interpolateParams, Interpolator, defaultTranslationText)
1761
- .then(function (translation) {
1762
- deferred.resolve(translation);
1763
- }, function (_translationId) {
1764
- deferred.reject(applyNotFoundIndicators(_translationId));
1765
- });
1766
- } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
1767
- // looks like the requested translation id doesn't exists.
1768
- // Now, if there is a registered handler for missing translations and no
1769
- // asyncLoader is pending, we execute the handler
1770
- if (defaultTranslationText) {
1771
- deferred.resolve(defaultTranslationText);
1772
- } else {
1773
- deferred.resolve(missingTranslationHandlerTranslation);
1774
- }
1985
+ if (defaultTranslationText) {
1986
+ deferred.resolve(defaultTranslationText);
1775
1987
  } else {
1776
- if (defaultTranslationText) {
1777
- deferred.resolve(defaultTranslationText);
1778
- } else {
1779
- deferred.reject(applyNotFoundIndicators(translationId));
1780
- }
1988
+ deferred.reject(applyNotFoundIndicators(translationId));
1781
1989
  }
1782
1990
  }
1783
- return deferred.promise;
1784
- };
1991
+ }
1992
+ return deferred.promise;
1993
+ };
1785
1994
 
1786
- var determineTranslationInstant = function (translationId, interpolateParams, interpolationId) {
1995
+ var determineTranslationInstant = function (translationId, interpolateParams, interpolationId, uses, sanitizeStrategy) {
1787
1996
 
1788
- var result, table = $uses ? $translationTable[$uses] : $translationTable,
1789
- Interpolator = defaultInterpolator;
1997
+ var result, table = uses ? $translationTable[uses] : $translationTable,
1998
+ Interpolator = defaultInterpolator;
1790
1999
 
1791
- // if the interpolation id exists use custom interpolator
1792
- if (interpolatorHashMap && Object.prototype.hasOwnProperty.call(interpolatorHashMap, interpolationId)) {
1793
- Interpolator = interpolatorHashMap[interpolationId];
1794
- }
2000
+ // if the interpolation id exists use custom interpolator
2001
+ if (interpolatorHashMap && Object.prototype.hasOwnProperty.call(interpolatorHashMap, interpolationId)) {
2002
+ Interpolator = interpolatorHashMap[interpolationId];
2003
+ }
1795
2004
 
1796
- // if the translation id exists, we can just interpolate it
1797
- if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {
1798
- var translation = table[translationId];
2005
+ // if the translation id exists, we can just interpolate it
2006
+ if (table && Object.prototype.hasOwnProperty.call(table, translationId) && table[translationId] !== null) {
2007
+ var translation = table[translationId];
1799
2008
 
1800
- // If using link, rerun $translate with linked translationId and return it
1801
- if (translation.substr(0, 2) === '@:') {
1802
- result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId);
1803
- } else {
1804
- result = Interpolator.interpolate(translation, interpolateParams);
1805
- }
2009
+ // If using link, rerun $translate with linked translationId and return it
2010
+ if (translation.substr(0, 2) === '@:') {
2011
+ result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId, uses, sanitizeStrategy);
1806
2012
  } else {
1807
- var missingTranslationHandlerTranslation;
1808
- // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
1809
- if ($missingTranslationHandlerFactory && !pendingLoader) {
1810
- missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams);
1811
- }
2013
+ result = Interpolator.interpolate(translation, interpolateParams, 'filter', sanitizeStrategy, translationId);
2014
+ result = applyPostProcessing(translationId, translation, result, interpolateParams, uses, sanitizeStrategy);
2015
+ }
2016
+ } else {
2017
+ var missingTranslationHandlerTranslation;
2018
+ // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
2019
+ if ($missingTranslationHandlerFactory && !pendingLoader) {
2020
+ missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy);
2021
+ }
1812
2022
 
1813
- // since we couldn't translate the inital requested translation id,
1814
- // we try it now with one or more fallback languages, if fallback language(s) is
1815
- // configured.
1816
- if ($uses && $fallbackLanguage && $fallbackLanguage.length) {
1817
- fallbackIndex = 0;
1818
- result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator);
1819
- } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
1820
- // looks like the requested translation id doesn't exists.
1821
- // Now, if there is a registered handler for missing translations and no
1822
- // asyncLoader is pending, we execute the handler
1823
- result = missingTranslationHandlerTranslation;
1824
- } else {
1825
- result = applyNotFoundIndicators(translationId);
1826
- }
2023
+ // since we couldn't translate the inital requested translation id,
2024
+ // we try it now with one or more fallback languages, if fallback language(s) is
2025
+ // configured.
2026
+ if (uses && $fallbackLanguage && $fallbackLanguage.length) {
2027
+ fallbackIndex = 0;
2028
+ result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator, sanitizeStrategy);
2029
+ } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
2030
+ // looks like the requested translation id doesn't exists.
2031
+ // Now, if there is a registered handler for missing translations and no
2032
+ // asyncLoader is pending, we execute the handler
2033
+ result = missingTranslationHandlerTranslation;
2034
+ } else {
2035
+ result = applyNotFoundIndicators(translationId);
1827
2036
  }
2037
+ }
1828
2038
 
1829
- return result;
1830
- };
2039
+ return result;
2040
+ };
1831
2041
 
1832
- var clearNextLangAndPromise = function(key) {
1833
- if ($nextLang === key) {
1834
- $nextLang = undefined;
1835
- }
1836
- langPromises[key] = undefined;
1837
- };
2042
+ var clearNextLangAndPromise = function (key) {
2043
+ if ($nextLang === key) {
2044
+ $nextLang = undefined;
2045
+ }
2046
+ langPromises[key] = undefined;
2047
+ };
1838
2048
 
1839
- /**
1840
- * @ngdoc function
1841
- * @name pascalprecht.translate.$translate#preferredLanguage
1842
- * @methodOf pascalprecht.translate.$translate
1843
- *
1844
- * @description
1845
- * Returns the language key for the preferred language.
1846
- *
1847
- * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime)
1848
- *
1849
- * @return {string} preferred language key
1850
- */
1851
- $translate.preferredLanguage = function (langKey) {
1852
- if(langKey) {
1853
- setupPreferredLanguage(langKey);
2049
+ var applyPostProcessing = function (translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy) {
2050
+ var fn = postProcessFn;
2051
+
2052
+ if (fn) {
2053
+
2054
+ if (typeof(fn) === 'string') {
2055
+ // getting on-demand instance
2056
+ fn = $injector.get(fn);
1854
2057
  }
1855
- return $preferredLanguage;
1856
- };
2058
+ if (fn) {
2059
+ return fn(translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy);
2060
+ }
2061
+ }
1857
2062
 
1858
- /**
1859
- * @ngdoc function
1860
- * @name pascalprecht.translate.$translate#cloakClassName
1861
- * @methodOf pascalprecht.translate.$translate
1862
- *
1863
- * @description
1864
- * Returns the configured class name for `translate-cloak` directive.
1865
- *
1866
- * @return {string} cloakClassName
1867
- */
1868
- $translate.cloakClassName = function () {
1869
- return $cloakClassName;
1870
- };
2063
+ return resolvedTranslation;
2064
+ };
1871
2065
 
1872
- /**
1873
- * @ngdoc function
1874
- * @name pascalprecht.translate.$translate#fallbackLanguage
1875
- * @methodOf pascalprecht.translate.$translate
1876
- *
1877
- * @description
1878
- * Returns the language key for the fallback languages or sets a new fallback stack.
1879
- *
1880
- * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime)
1881
- *
1882
- * @return {string||array} fallback language key
1883
- */
1884
- $translate.fallbackLanguage = function (langKey) {
1885
- if (langKey !== undefined && langKey !== null) {
1886
- fallbackStack(langKey);
1887
-
1888
- // as we might have an async loader initiated and a new translation language might have been defined
1889
- // we need to add the promise to the stack also. So - iterate.
1890
- if ($loaderFactory) {
1891
- if ($fallbackLanguage && $fallbackLanguage.length) {
1892
- for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
1893
- if (!langPromises[$fallbackLanguage[i]]) {
1894
- langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]);
1895
- }
2066
+ var loadTranslationsIfMissing = function (key) {
2067
+ if (!$translationTable[key] && $loaderFactory && !langPromises[key]) {
2068
+ langPromises[key] = loadAsync(key).then(function (translation) {
2069
+ translations(translation.key, translation.table);
2070
+ return translation;
2071
+ });
2072
+ }
2073
+ };
2074
+
2075
+ /**
2076
+ * @ngdoc function
2077
+ * @name pascalprecht.translate.$translate#preferredLanguage
2078
+ * @methodOf pascalprecht.translate.$translate
2079
+ *
2080
+ * @description
2081
+ * Returns the language key for the preferred language.
2082
+ *
2083
+ * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime)
2084
+ *
2085
+ * @return {string} preferred language key
2086
+ */
2087
+ $translate.preferredLanguage = function (langKey) {
2088
+ if (langKey) {
2089
+ setupPreferredLanguage(langKey);
2090
+ }
2091
+ return $preferredLanguage;
2092
+ };
2093
+
2094
+ /**
2095
+ * @ngdoc function
2096
+ * @name pascalprecht.translate.$translate#cloakClassName
2097
+ * @methodOf pascalprecht.translate.$translate
2098
+ *
2099
+ * @description
2100
+ * Returns the configured class name for `translate-cloak` directive.
2101
+ *
2102
+ * @return {string} cloakClassName
2103
+ */
2104
+ $translate.cloakClassName = function () {
2105
+ return $cloakClassName;
2106
+ };
2107
+
2108
+ /**
2109
+ * @ngdoc function
2110
+ * @name pascalprecht.translate.$translate#nestedObjectDelimeter
2111
+ * @methodOf pascalprecht.translate.$translate
2112
+ *
2113
+ * @description
2114
+ * Returns the configured delimiter for nested namespaces.
2115
+ *
2116
+ * @return {string} nestedObjectDelimeter
2117
+ */
2118
+ $translate.nestedObjectDelimeter = function () {
2119
+ return $nestedObjectDelimeter;
2120
+ };
2121
+
2122
+ /**
2123
+ * @ngdoc function
2124
+ * @name pascalprecht.translate.$translate#fallbackLanguage
2125
+ * @methodOf pascalprecht.translate.$translate
2126
+ *
2127
+ * @description
2128
+ * Returns the language key for the fallback languages or sets a new fallback stack.
2129
+ *
2130
+ * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime)
2131
+ *
2132
+ * @return {string||array} fallback language key
2133
+ */
2134
+ $translate.fallbackLanguage = function (langKey) {
2135
+ if (langKey !== undefined && langKey !== null) {
2136
+ fallbackStack(langKey);
2137
+
2138
+ // as we might have an async loader initiated and a new translation language might have been defined
2139
+ // we need to add the promise to the stack also. So - iterate.
2140
+ if ($loaderFactory) {
2141
+ if ($fallbackLanguage && $fallbackLanguage.length) {
2142
+ for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
2143
+ if (!langPromises[$fallbackLanguage[i]]) {
2144
+ langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]);
1896
2145
  }
1897
2146
  }
1898
2147
  }
1899
- $translate.use($translate.use());
1900
- }
1901
- if ($fallbackWasString) {
1902
- return $fallbackLanguage[0];
1903
- } else {
1904
- return $fallbackLanguage;
1905
2148
  }
2149
+ $translate.use($translate.use());
2150
+ }
2151
+ if ($fallbackWasString) {
2152
+ return $fallbackLanguage[0];
2153
+ } else {
2154
+ return $fallbackLanguage;
2155
+ }
1906
2156
 
1907
- };
2157
+ };
1908
2158
 
1909
- /**
1910
- * @ngdoc function
1911
- * @name pascalprecht.translate.$translate#useFallbackLanguage
1912
- * @methodOf pascalprecht.translate.$translate
1913
- *
1914
- * @description
1915
- * Sets the first key of the fallback language stack to be used for translation.
1916
- * Therefore all languages in the fallback array BEFORE this key will be skipped!
1917
- *
1918
- * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to
1919
- * get back to the whole stack
1920
- */
1921
- $translate.useFallbackLanguage = function (langKey) {
1922
- if (langKey !== undefined && langKey !== null) {
1923
- if (!langKey) {
1924
- startFallbackIteration = 0;
1925
- } else {
1926
- var langKeyPosition = indexOf($fallbackLanguage, langKey);
1927
- if (langKeyPosition > -1) {
1928
- startFallbackIteration = langKeyPosition;
1929
- }
2159
+ /**
2160
+ * @ngdoc function
2161
+ * @name pascalprecht.translate.$translate#useFallbackLanguage
2162
+ * @methodOf pascalprecht.translate.$translate
2163
+ *
2164
+ * @description
2165
+ * Sets the first key of the fallback language stack to be used for translation.
2166
+ * Therefore all languages in the fallback array BEFORE this key will be skipped!
2167
+ *
2168
+ * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to
2169
+ * get back to the whole stack
2170
+ */
2171
+ $translate.useFallbackLanguage = function (langKey) {
2172
+ if (langKey !== undefined && langKey !== null) {
2173
+ if (!langKey) {
2174
+ startFallbackIteration = 0;
2175
+ } else {
2176
+ var langKeyPosition = indexOf($fallbackLanguage, langKey);
2177
+ if (langKeyPosition > -1) {
2178
+ startFallbackIteration = langKeyPosition;
1930
2179
  }
1931
-
1932
2180
  }
1933
2181
 
1934
- };
2182
+ }
1935
2183
 
1936
- /**
1937
- * @ngdoc function
1938
- * @name pascalprecht.translate.$translate#proposedLanguage
1939
- * @methodOf pascalprecht.translate.$translate
1940
- *
1941
- * @description
1942
- * Returns the language key of language that is currently loaded asynchronously.
1943
- *
1944
- * @return {string} language key
1945
- */
1946
- $translate.proposedLanguage = function () {
1947
- return $nextLang;
1948
- };
2184
+ };
1949
2185
 
1950
- /**
1951
- * @ngdoc function
1952
- * @name pascalprecht.translate.$translate#storage
1953
- * @methodOf pascalprecht.translate.$translate
1954
- *
1955
- * @description
1956
- * Returns registered storage.
1957
- *
1958
- * @return {object} Storage
1959
- */
1960
- $translate.storage = function () {
1961
- return Storage;
1962
- };
2186
+ /**
2187
+ * @ngdoc function
2188
+ * @name pascalprecht.translate.$translate#proposedLanguage
2189
+ * @methodOf pascalprecht.translate.$translate
2190
+ *
2191
+ * @description
2192
+ * Returns the language key of language that is currently loaded asynchronously.
2193
+ *
2194
+ * @return {string} language key
2195
+ */
2196
+ $translate.proposedLanguage = function () {
2197
+ return $nextLang;
2198
+ };
1963
2199
 
1964
- /**
1965
- * @ngdoc function
1966
- * @name pascalprecht.translate.$translate#use
1967
- * @methodOf pascalprecht.translate.$translate
1968
- *
1969
- * @description
1970
- * Tells angular-translate which language to use by given language key. This method is
1971
- * used to change language at runtime. It also takes care of storing the language
1972
- * key in a configured store to let your app remember the choosed language.
1973
- *
1974
- * When trying to 'use' a language which isn't available it tries to load it
1975
- * asynchronously with registered loaders.
1976
- *
1977
- * Returns promise object with loaded language file data
1978
- * @example
1979
- * $translate.use("en_US").then(function(data){
2200
+ /**
2201
+ * @ngdoc function
2202
+ * @name pascalprecht.translate.$translate#storage
2203
+ * @methodOf pascalprecht.translate.$translate
2204
+ *
2205
+ * @description
2206
+ * Returns registered storage.
2207
+ *
2208
+ * @return {object} Storage
2209
+ */
2210
+ $translate.storage = function () {
2211
+ return Storage;
2212
+ };
2213
+
2214
+ /**
2215
+ * @ngdoc function
2216
+ * @name pascalprecht.translate.$translate#negotiateLocale
2217
+ * @methodOf pascalprecht.translate.$translate
2218
+ *
2219
+ * @description
2220
+ * Returns a language key based on available languages and language aliases. If a
2221
+ * language key cannot be resolved, returns undefined.
2222
+ *
2223
+ * If no or a falsy key is given, returns undefined.
2224
+ *
2225
+ * @param {string} [key] Language key
2226
+ * @return {string|undefined} Language key or undefined if no language key is found.
2227
+ */
2228
+ $translate.negotiateLocale = negotiateLocale;
2229
+
2230
+ /**
2231
+ * @ngdoc function
2232
+ * @name pascalprecht.translate.$translate#use
2233
+ * @methodOf pascalprecht.translate.$translate
2234
+ *
2235
+ * @description
2236
+ * Tells angular-translate which language to use by given language key. This method is
2237
+ * used to change language at runtime. It also takes care of storing the language
2238
+ * key in a configured store to let your app remember the choosed language.
2239
+ *
2240
+ * When trying to 'use' a language which isn't available it tries to load it
2241
+ * asynchronously with registered loaders.
2242
+ *
2243
+ * Returns promise object with loaded language file data or string of the currently used language.
2244
+ *
2245
+ * If no or a falsy key is given it returns the currently used language key.
2246
+ * The returned string will be ```undefined``` if setting up $translate hasn't finished.
2247
+ * @example
2248
+ * $translate.use("en_US").then(function(data){
1980
2249
  * $scope.text = $translate("HELLO");
1981
2250
  * });
1982
- *
1983
- * @param {string} key Language key
1984
- * @return {string} Language key
1985
- */
1986
- $translate.use = function (key) {
1987
- if (!key) {
1988
- return $uses;
1989
- }
1990
-
1991
- var deferred = $q.defer();
2251
+ *
2252
+ * @param {string} [key] Language key
2253
+ * @return {object|string} Promise with loaded language data or the language key if a falsy param was given.
2254
+ */
2255
+ $translate.use = function (key) {
2256
+ if (!key) {
2257
+ return $uses;
2258
+ }
1992
2259
 
1993
- $rootScope.$emit('$translateChangeStart', {language: key});
2260
+ var deferred = $q.defer();
2261
+ deferred.promise.then(null, angular.noop); // AJS "Possibly unhandled rejection"
1994
2262
 
1995
- // Try to get the aliased language key
1996
- var aliasedKey = negotiateLocale(key);
1997
- if (aliasedKey) {
1998
- key = aliasedKey;
1999
- }
2263
+ $rootScope.$emit('$translateChangeStart', {language : key});
2000
2264
 
2001
- // if there isn't a translation table for the language we've requested,
2002
- // we load it asynchronously
2003
- if (($forceAsyncReloadEnabled || !$translationTable[key]) && $loaderFactory && !langPromises[key]) {
2004
- $nextLang = key;
2005
- langPromises[key] = loadAsync(key).then(function (translation) {
2006
- translations(translation.key, translation.table);
2007
- deferred.resolve(translation.key);
2008
- useLanguage(translation.key);
2009
- return translation;
2010
- }, function (key) {
2011
- $rootScope.$emit('$translateChangeError', {language: key});
2012
- deferred.reject(key);
2013
- $rootScope.$emit('$translateChangeEnd', {language: key});
2014
- return $q.reject(key);
2015
- });
2016
- langPromises[key]['finally'](function () {
2017
- clearNextLangAndPromise(key);
2018
- });
2019
- } else if ($nextLang === key && langPromises[key]) {
2020
- // we are already loading this asynchronously
2021
- // resolve our new deferred when the old langPromise is resolved
2022
- langPromises[key].then(function (translation) {
2023
- deferred.resolve(translation.key);
2024
- return translation;
2025
- }, function (key) {
2026
- deferred.reject(key);
2027
- return $q.reject(key);
2028
- });
2029
- } else {
2030
- deferred.resolve(key);
2031
- useLanguage(key);
2032
- }
2265
+ // Try to get the aliased language key
2266
+ var aliasedKey = negotiateLocale(key);
2267
+ // Ensure only registered language keys will be loaded
2268
+ if ($availableLanguageKeys.length > 0 && !aliasedKey) {
2269
+ return $q.reject(key);
2270
+ }
2033
2271
 
2034
- return deferred.promise;
2035
- };
2272
+ if (aliasedKey) {
2273
+ key = aliasedKey;
2274
+ }
2036
2275
 
2037
- /**
2038
- * @ngdoc function
2039
- * @name pascalprecht.translate.$translate#storageKey
2040
- * @methodOf pascalprecht.translate.$translate
2041
- *
2042
- * @description
2043
- * Returns the key for the storage.
2044
- *
2045
- * @return {string} storage key
2046
- */
2047
- $translate.storageKey = function () {
2048
- return storageKey();
2049
- };
2276
+ // if there isn't a translation table for the language we've requested,
2277
+ // we load it asynchronously
2278
+ $nextLang = key;
2279
+ if (($forceAsyncReloadEnabled || !$translationTable[key]) && $loaderFactory && !langPromises[key]) {
2280
+ langPromises[key] = loadAsync(key).then(function (translation) {
2281
+ translations(translation.key, translation.table);
2282
+ deferred.resolve(translation.key);
2283
+ if ($nextLang === key) {
2284
+ useLanguage(translation.key);
2285
+ }
2286
+ return translation;
2287
+ }, function (key) {
2288
+ $rootScope.$emit('$translateChangeError', {language : key});
2289
+ deferred.reject(key);
2290
+ $rootScope.$emit('$translateChangeEnd', {language : key});
2291
+ return $q.reject(key);
2292
+ });
2293
+ langPromises[key]['finally'](function () {
2294
+ clearNextLangAndPromise(key);
2295
+ })['catch'](angular.noop); // we don't care about errors (clearing)
2296
+ } else if (langPromises[key]) {
2297
+ // we are already loading this asynchronously
2298
+ // resolve our new deferred when the old langPromise is resolved
2299
+ langPromises[key].then(function (translation) {
2300
+ if ($nextLang === translation.key) {
2301
+ useLanguage(translation.key);
2302
+ }
2303
+ deferred.resolve(translation.key);
2304
+ return translation;
2305
+ }, function (key) {
2306
+ // find first available fallback language if that request has failed
2307
+ if (!$uses && $fallbackLanguage && $fallbackLanguage.length > 0 && $fallbackLanguage[0] !== key) {
2308
+ return $translate.use($fallbackLanguage[0]).then(deferred.resolve, deferred.reject);
2309
+ } else {
2310
+ return deferred.reject(key);
2311
+ }
2312
+ });
2313
+ } else {
2314
+ deferred.resolve(key);
2315
+ useLanguage(key);
2316
+ }
2050
2317
 
2051
- /**
2052
- * @ngdoc function
2053
- * @name pascalprecht.translate.$translate#isPostCompilingEnabled
2054
- * @methodOf pascalprecht.translate.$translate
2055
- *
2056
- * @description
2057
- * Returns whether post compiling is enabled or not
2058
- *
2059
- * @return {bool} storage key
2060
- */
2061
- $translate.isPostCompilingEnabled = function () {
2062
- return $postCompilingEnabled;
2063
- };
2318
+ return deferred.promise;
2319
+ };
2064
2320
 
2065
- /**
2066
- * @ngdoc function
2067
- * @name pascalprecht.translate.$translate#isForceAsyncReloadEnabled
2068
- * @methodOf pascalprecht.translate.$translate
2069
- *
2070
- * @description
2071
- * Returns whether force async reload is enabled or not
2072
- *
2073
- * @return {boolean} forceAsyncReload value
2074
- */
2075
- $translate.isForceAsyncReloadEnabled = function () {
2076
- return $forceAsyncReloadEnabled;
2077
- };
2321
+ /**
2322
+ * @ngdoc function
2323
+ * @name pascalprecht.translate.$translate#resolveClientLocale
2324
+ * @methodOf pascalprecht.translate.$translate
2325
+ *
2326
+ * @description
2327
+ * This returns the current browser/client's language key. The result is processed with the configured uniform tag resolver.
2328
+ *
2329
+ * @returns {string} the current client/browser language key
2330
+ */
2331
+ $translate.resolveClientLocale = function () {
2332
+ return getLocale();
2333
+ };
2078
2334
 
2079
- /**
2080
- * @ngdoc function
2081
- * @name pascalprecht.translate.$translate#refresh
2082
- * @methodOf pascalprecht.translate.$translate
2083
- *
2084
- * @description
2085
- * Refreshes a translation table pointed by the given langKey. If langKey is not specified,
2086
- * the module will drop all existent translation tables and load new version of those which
2087
- * are currently in use.
2088
- *
2089
- * Refresh means that the module will drop target translation table and try to load it again.
2090
- *
2091
- * In case there are no loaders registered the refresh() method will throw an Error.
2092
- *
2093
- * If the module is able to refresh translation tables refresh() method will broadcast
2094
- * $translateRefreshStart and $translateRefreshEnd events.
2095
- *
2096
- * @example
2097
- * // this will drop all currently existent translation tables and reload those which are
2098
- * // currently in use
2099
- * $translate.refresh();
2100
- * // this will refresh a translation table for the en_US language
2101
- * $translate.refresh('en_US');
2102
- *
2103
- * @param {string} langKey A language key of the table, which has to be refreshed
2104
- *
2105
- * @return {promise} Promise, which will be resolved in case a translation tables refreshing
2106
- * process is finished successfully, and reject if not.
2107
- */
2108
- $translate.refresh = function (langKey) {
2109
- if (!$loaderFactory) {
2110
- throw new Error('Couldn\'t refresh translation table, no loader registered!');
2111
- }
2335
+ /**
2336
+ * @ngdoc function
2337
+ * @name pascalprecht.translate.$translate#storageKey
2338
+ * @methodOf pascalprecht.translate.$translate
2339
+ *
2340
+ * @description
2341
+ * Returns the key for the storage.
2342
+ *
2343
+ * @return {string} storage key
2344
+ */
2345
+ $translate.storageKey = function () {
2346
+ return storageKey();
2347
+ };
2112
2348
 
2113
- var deferred = $q.defer();
2349
+ /**
2350
+ * @ngdoc function
2351
+ * @name pascalprecht.translate.$translate#isPostCompilingEnabled
2352
+ * @methodOf pascalprecht.translate.$translate
2353
+ *
2354
+ * @description
2355
+ * Returns whether post compiling is enabled or not
2356
+ *
2357
+ * @return {bool} storage key
2358
+ */
2359
+ $translate.isPostCompilingEnabled = function () {
2360
+ return $postCompilingEnabled;
2361
+ };
2114
2362
 
2115
- function resolve() {
2116
- deferred.resolve();
2117
- $rootScope.$emit('$translateRefreshEnd', {language: langKey});
2118
- }
2363
+ /**
2364
+ * @ngdoc function
2365
+ * @name pascalprecht.translate.$translate#isForceAsyncReloadEnabled
2366
+ * @methodOf pascalprecht.translate.$translate
2367
+ *
2368
+ * @description
2369
+ * Returns whether force async reload is enabled or not
2370
+ *
2371
+ * @return {boolean} forceAsyncReload value
2372
+ */
2373
+ $translate.isForceAsyncReloadEnabled = function () {
2374
+ return $forceAsyncReloadEnabled;
2375
+ };
2119
2376
 
2120
- function reject() {
2121
- deferred.reject();
2122
- $rootScope.$emit('$translateRefreshEnd', {language: langKey});
2123
- }
2377
+ /**
2378
+ * @ngdoc function
2379
+ * @name pascalprecht.translate.$translate#isKeepContent
2380
+ * @methodOf pascalprecht.translate.$translate
2381
+ *
2382
+ * @description
2383
+ * Returns whether keepContent or not
2384
+ *
2385
+ * @return {boolean} keepContent value
2386
+ */
2387
+ $translate.isKeepContent = function () {
2388
+ return $keepContent;
2389
+ };
2124
2390
 
2125
- $rootScope.$emit('$translateRefreshStart', {language: langKey});
2391
+ /**
2392
+ * @ngdoc function
2393
+ * @name pascalprecht.translate.$translate#refresh
2394
+ * @methodOf pascalprecht.translate.$translate
2395
+ *
2396
+ * @description
2397
+ * Refreshes a translation table pointed by the given langKey. If langKey is not specified,
2398
+ * the module will drop all existent translation tables and load new version of those which
2399
+ * are currently in use.
2400
+ *
2401
+ * Refresh means that the module will drop target translation table and try to load it again.
2402
+ *
2403
+ * In case there are no loaders registered the refresh() method will throw an Error.
2404
+ *
2405
+ * If the module is able to refresh translation tables refresh() method will broadcast
2406
+ * $translateRefreshStart and $translateRefreshEnd events.
2407
+ *
2408
+ * @example
2409
+ * // this will drop all currently existent translation tables and reload those which are
2410
+ * // currently in use
2411
+ * $translate.refresh();
2412
+ * // this will refresh a translation table for the en_US language
2413
+ * $translate.refresh('en_US');
2414
+ *
2415
+ * @param {string} langKey A language key of the table, which has to be refreshed
2416
+ *
2417
+ * @return {promise} Promise, which will be resolved in case a translation tables refreshing
2418
+ * process is finished successfully, and reject if not.
2419
+ */
2420
+ $translate.refresh = function (langKey) {
2421
+ if (!$loaderFactory) {
2422
+ throw new Error('Couldn\'t refresh translation table, no loader registered!');
2423
+ }
2126
2424
 
2127
- if (!langKey) {
2128
- // if there's no language key specified we refresh ALL THE THINGS!
2129
- var tables = [], loadingKeys = {};
2425
+ $rootScope.$emit('$translateRefreshStart', {language : langKey});
2426
+
2427
+ var deferred = $q.defer(), updatedLanguages = {};
2428
+
2429
+ //private helper
2430
+ function loadNewData(languageKey) {
2431
+ var promise = loadAsync(languageKey);
2432
+ //update the load promise cache for this language
2433
+ langPromises[languageKey] = promise;
2434
+ //register a data handler for the promise
2435
+ promise.then(function (data) {
2436
+ //clear the cache for this language
2437
+ $translationTable[languageKey] = {};
2438
+ //add the new data for this language
2439
+ translations(languageKey, data.table);
2440
+ //track that we updated this language
2441
+ updatedLanguages[languageKey] = true;
2442
+ },
2443
+ //handle rejection to appease the $q validation
2444
+ angular.noop);
2445
+ return promise;
2446
+ }
2130
2447
 
2131
- // reload registered fallback languages
2132
- if ($fallbackLanguage && $fallbackLanguage.length) {
2133
- for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
2134
- tables.push(loadAsync($fallbackLanguage[i]));
2135
- loadingKeys[$fallbackLanguage[i]] = true;
2448
+ //set up post-processing
2449
+ deferred.promise.then(
2450
+ function () {
2451
+ for (var key in $translationTable) {
2452
+ if ($translationTable.hasOwnProperty(key)) {
2453
+ //delete cache entries that were not updated
2454
+ if (!(key in updatedLanguages)) {
2455
+ delete $translationTable[key];
2456
+ }
2136
2457
  }
2137
2458
  }
2138
-
2139
- // reload currently used language
2140
- if ($uses && !loadingKeys[$uses]) {
2141
- tables.push(loadAsync($uses));
2459
+ if ($uses) {
2460
+ useLanguage($uses);
2142
2461
  }
2462
+ },
2463
+ //handle rejection to appease the $q validation
2464
+ angular.noop
2465
+ )['finally'](
2466
+ function () {
2467
+ $rootScope.$emit('$translateRefreshEnd', {language : langKey});
2468
+ }
2469
+ );
2143
2470
 
2144
- var allTranslationsLoaded = function (tableData) {
2145
- $translationTable = {};
2146
- angular.forEach(tableData, function (data) {
2147
- translations(data.key, data.table);
2148
- });
2149
- if ($uses) {
2150
- useLanguage($uses);
2151
- }
2152
- resolve();
2153
- };
2154
- allTranslationsLoaded.displayName = 'refreshPostProcessor';
2471
+ if (!langKey) {
2472
+ // if there's no language key specified we refresh ALL THE THINGS!
2473
+ var languagesToReload = $fallbackLanguage && $fallbackLanguage.slice() || [];
2474
+ if ($uses && languagesToReload.indexOf($uses) === -1) {
2475
+ languagesToReload.push($uses);
2476
+ }
2477
+ $q.all(languagesToReload.map(loadNewData)).then(deferred.resolve, deferred.reject);
2155
2478
 
2156
- $q.all(tables).then(allTranslationsLoaded, reject);
2479
+ } else if ($translationTable[langKey]) {
2480
+ //just refresh the specified language cache
2481
+ loadNewData(langKey).then(deferred.resolve, deferred.reject);
2157
2482
 
2158
- } else if ($translationTable[langKey]) {
2483
+ } else {
2484
+ deferred.reject();
2485
+ }
2159
2486
 
2160
- var oneTranslationsLoaded = function (data) {
2161
- translations(data.key, data.table);
2162
- if (langKey === $uses) {
2163
- useLanguage($uses);
2164
- }
2165
- resolve();
2166
- };
2167
- oneTranslationsLoaded.displayName = 'refreshPostProcessor';
2487
+ return deferred.promise;
2488
+ };
2168
2489
 
2169
- loadAsync(langKey).then(oneTranslationsLoaded, reject);
2490
+ /**
2491
+ * @ngdoc function
2492
+ * @name pascalprecht.translate.$translate#instant
2493
+ * @methodOf pascalprecht.translate.$translate
2494
+ *
2495
+ * @description
2496
+ * Returns a translation instantly from the internal state of loaded translation. All rules
2497
+ * regarding the current language, the preferred language of even fallback languages will be
2498
+ * used except any promise handling. If a language was not found, an asynchronous loading
2499
+ * will be invoked in the background.
2500
+ *
2501
+ * @param {string|array} translationId A token which represents a translation id
2502
+ * This can be optionally an array of translation ids which
2503
+ * results that the function's promise returns an object where
2504
+ * each key is the translation id and the value the translation.
2505
+ * @param {object} interpolateParams Params
2506
+ * @param {string} interpolationId The id of the interpolation to use
2507
+ * @param {string} forceLanguage A language to be used instead of the current language
2508
+ * @param {string} sanitizeStrategy force sanitize strategy for this call instead of using the configured one
2509
+ *
2510
+ * @return {string|object} translation
2511
+ */
2512
+ $translate.instant = function (translationId, interpolateParams, interpolationId, forceLanguage, sanitizeStrategy) {
2513
+
2514
+ // we don't want to re-negotiate $uses
2515
+ var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses
2516
+ (negotiateLocale(forceLanguage) || forceLanguage) : $uses;
2517
+
2518
+ // Detect undefined and null values to shorten the execution and prevent exceptions
2519
+ if (translationId === null || angular.isUndefined(translationId)) {
2520
+ return translationId;
2521
+ }
2170
2522
 
2171
- } else {
2172
- reject();
2523
+ // Check forceLanguage is present
2524
+ if (forceLanguage) {
2525
+ loadTranslationsIfMissing(forceLanguage);
2526
+ }
2527
+
2528
+ // Duck detection: If the first argument is an array, a bunch of translations was requested.
2529
+ // The result is an object.
2530
+ if (angular.isArray(translationId)) {
2531
+ var results = {};
2532
+ for (var i = 0, c = translationId.length; i < c; i++) {
2533
+ results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId, forceLanguage, sanitizeStrategy);
2173
2534
  }
2174
- return deferred.promise;
2175
- };
2535
+ return results;
2536
+ }
2176
2537
 
2177
- /**
2178
- * @ngdoc function
2179
- * @name pascalprecht.translate.$translate#instant
2180
- * @methodOf pascalprecht.translate.$translate
2181
- *
2182
- * @description
2183
- * Returns a translation instantly from the internal state of loaded translation. All rules
2184
- * regarding the current language, the preferred language of even fallback languages will be
2185
- * used except any promise handling. If a language was not found, an asynchronous loading
2186
- * will be invoked in the background.
2187
- *
2188
- * @param {string|array} translationId A token which represents a translation id
2189
- * This can be optionally an array of translation ids which
2190
- * results that the function's promise returns an object where
2191
- * each key is the translation id and the value the translation.
2192
- * @param {object} interpolateParams Params
2193
- * @param {string} interpolationId The id of the interpolation to use
2194
- *
2195
- * @return {string|object} translation
2196
- */
2197
- $translate.instant = function (translationId, interpolateParams, interpolationId) {
2538
+ // We discarded unacceptable values. So we just need to verify if translationId is empty String
2539
+ if (angular.isString(translationId) && translationId.length < 1) {
2540
+ return translationId;
2541
+ }
2198
2542
 
2199
- // Detect undefined and null values to shorten the execution and prevent exceptions
2200
- if (translationId === null || angular.isUndefined(translationId)) {
2201
- return translationId;
2202
- }
2543
+ // trim off any whitespace
2544
+ if (translationId) {
2545
+ translationId = trim.apply(translationId);
2546
+ }
2203
2547
 
2204
- // Duck detection: If the first argument is an array, a bunch of translations was requested.
2205
- // The result is an object.
2206
- if (angular.isArray(translationId)) {
2207
- var results = {};
2208
- for (var i = 0, c = translationId.length; i < c; i++) {
2209
- results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId);
2548
+ var result, possibleLangKeys = [];
2549
+ if ($preferredLanguage) {
2550
+ possibleLangKeys.push($preferredLanguage);
2551
+ }
2552
+ if (uses) {
2553
+ possibleLangKeys.push(uses);
2554
+ }
2555
+ if ($fallbackLanguage && $fallbackLanguage.length) {
2556
+ possibleLangKeys = possibleLangKeys.concat($fallbackLanguage);
2557
+ }
2558
+ for (var j = 0, d = possibleLangKeys.length; j < d; j++) {
2559
+ var possibleLangKey = possibleLangKeys[j];
2560
+ if ($translationTable[possibleLangKey]) {
2561
+ if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') {
2562
+ result = determineTranslationInstant(translationId, interpolateParams, interpolationId, uses, sanitizeStrategy);
2210
2563
  }
2211
- return results;
2212
2564
  }
2213
-
2214
- // We discarded unacceptable values. So we just need to verify if translationId is empty String
2215
- if (angular.isString(translationId) && translationId.length < 1) {
2216
- return translationId;
2565
+ if (typeof result !== 'undefined') {
2566
+ break;
2217
2567
  }
2568
+ }
2218
2569
 
2219
- // trim off any whitespace
2220
- if (translationId) {
2221
- translationId = trim.apply(translationId);
2222
- }
2570
+ if (!result && result !== '') {
2571
+ if ($notFoundIndicatorLeft || $notFoundIndicatorRight) {
2572
+ result = applyNotFoundIndicators(translationId);
2573
+ } else {
2574
+ // Return translation of default interpolator if not found anything.
2575
+ result = defaultInterpolator.interpolate(translationId, interpolateParams, 'filter', sanitizeStrategy);
2223
2576
 
2224
- var result, possibleLangKeys = [];
2225
- if ($preferredLanguage) {
2226
- possibleLangKeys.push($preferredLanguage);
2227
- }
2228
- if ($uses) {
2229
- possibleLangKeys.push($uses);
2230
- }
2231
- if ($fallbackLanguage && $fallbackLanguage.length) {
2232
- possibleLangKeys = possibleLangKeys.concat($fallbackLanguage);
2233
- }
2234
- for (var j = 0, d = possibleLangKeys.length; j < d; j++) {
2235
- var possibleLangKey = possibleLangKeys[j];
2236
- if ($translationTable[possibleLangKey]) {
2237
- if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') {
2238
- result = determineTranslationInstant(translationId, interpolateParams, interpolationId);
2239
- } else if ($notFoundIndicatorLeft || $notFoundIndicatorRight) {
2240
- result = applyNotFoundIndicators(translationId);
2241
- }
2242
- }
2243
- if (typeof result !== 'undefined') {
2244
- break;
2577
+ // looks like the requested translation id doesn't exists.
2578
+ // Now, if there is a registered handler for missing translations and no
2579
+ // asyncLoader is pending, we execute the handler
2580
+ var missingTranslationHandlerTranslation;
2581
+ if ($missingTranslationHandlerFactory && !pendingLoader) {
2582
+ missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy);
2245
2583
  }
2246
- }
2247
2584
 
2248
- if (!result && result !== '') {
2249
- // Return translation of default interpolator if not found anything.
2250
- result = defaultInterpolator.interpolate(translationId, interpolateParams);
2251
- if ($missingTranslationHandlerFactory && !pendingLoader) {
2252
- result = translateByHandler(translationId, interpolateParams);
2585
+ if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
2586
+ result = missingTranslationHandlerTranslation;
2253
2587
  }
2254
2588
  }
2589
+ }
2255
2590
 
2256
- return result;
2257
- };
2591
+ return result;
2592
+ };
2258
2593
 
2259
- /**
2260
- * @ngdoc function
2261
- * @name pascalprecht.translate.$translate#versionInfo
2262
- * @methodOf pascalprecht.translate.$translate
2263
- *
2264
- * @description
2265
- * Returns the current version information for the angular-translate library
2266
- *
2267
- * @return {string} angular-translate version
2268
- */
2269
- $translate.versionInfo = function () {
2270
- return version;
2271
- };
2594
+ /**
2595
+ * @ngdoc function
2596
+ * @name pascalprecht.translate.$translate#versionInfo
2597
+ * @methodOf pascalprecht.translate.$translate
2598
+ *
2599
+ * @description
2600
+ * Returns the current version information for the angular-translate library
2601
+ *
2602
+ * @return {string} angular-translate version
2603
+ */
2604
+ $translate.versionInfo = function () {
2605
+ return version;
2606
+ };
2607
+
2608
+ /**
2609
+ * @ngdoc function
2610
+ * @name pascalprecht.translate.$translate#loaderCache
2611
+ * @methodOf pascalprecht.translate.$translate
2612
+ *
2613
+ * @description
2614
+ * Returns the defined loaderCache.
2615
+ *
2616
+ * @return {boolean|string|object} current value of loaderCache
2617
+ */
2618
+ $translate.loaderCache = function () {
2619
+ return loaderCache;
2620
+ };
2621
+
2622
+ // internal purpose only
2623
+ $translate.directivePriority = function () {
2624
+ return directivePriority;
2625
+ };
2626
+
2627
+ // internal purpose only
2628
+ $translate.statefulFilter = function () {
2629
+ return statefulFilter;
2630
+ };
2272
2631
 
2273
- /**
2274
- * @ngdoc function
2275
- * @name pascalprecht.translate.$translate#loaderCache
2276
- * @methodOf pascalprecht.translate.$translate
2277
- *
2278
- * @description
2279
- * Returns the defined loaderCache.
2280
- *
2281
- * @return {boolean|string|object} current value of loaderCache
2282
- */
2283
- $translate.loaderCache = function () {
2284
- return loaderCache;
2285
- };
2632
+ /**
2633
+ * @ngdoc function
2634
+ * @name pascalprecht.translate.$translate#isReady
2635
+ * @methodOf pascalprecht.translate.$translate
2636
+ *
2637
+ * @description
2638
+ * Returns whether the service is "ready" to translate (i.e. loading 1st language).
2639
+ *
2640
+ * See also {@link pascalprecht.translate.$translate#methods_onReady onReady()}.
2641
+ *
2642
+ * @return {boolean} current value of ready
2643
+ */
2644
+ $translate.isReady = function () {
2645
+ return $isReady;
2646
+ };
2286
2647
 
2287
- // internal purpose only
2288
- $translate.directivePriority = function () {
2289
- return directivePriority;
2290
- };
2648
+ var $onReadyDeferred = $q.defer();
2649
+ $onReadyDeferred.promise.then(function () {
2650
+ $isReady = true;
2651
+ });
2291
2652
 
2292
- // internal purpose only
2293
- $translate.statefulFilter = function () {
2294
- return statefulFilter;
2295
- };
2653
+ /**
2654
+ * @ngdoc function
2655
+ * @name pascalprecht.translate.$translate#onReady
2656
+ * @methodOf pascalprecht.translate.$translate
2657
+ *
2658
+ * @description
2659
+ * Calls the function provided or resolved the returned promise after the service is "ready" to translate (i.e. loading 1st language).
2660
+ *
2661
+ * See also {@link pascalprecht.translate.$translate#methods_isReady isReady()}.
2662
+ *
2663
+ * @param {Function=} fn Function to invoke when service is ready
2664
+ * @return {object} Promise resolved when service is ready
2665
+ */
2666
+ $translate.onReady = function (fn) {
2667
+ var deferred = $q.defer();
2668
+ if (angular.isFunction(fn)) {
2669
+ deferred.promise.then(fn);
2670
+ }
2671
+ if ($isReady) {
2672
+ deferred.resolve();
2673
+ } else {
2674
+ $onReadyDeferred.promise.then(deferred.resolve);
2675
+ }
2676
+ return deferred.promise;
2677
+ };
2678
+
2679
+ /**
2680
+ * @ngdoc function
2681
+ * @name pascalprecht.translate.$translate#getAvailableLanguageKeys
2682
+ * @methodOf pascalprecht.translate.$translate
2683
+ *
2684
+ * @description
2685
+ * This function simply returns the registered language keys being defined before in the config phase
2686
+ * With this, an application can use the array to provide a language selection dropdown or similar
2687
+ * without any additional effort
2688
+ *
2689
+ * @returns {object} returns the list of possibly registered language keys and mapping or null if not defined
2690
+ */
2691
+ $translate.getAvailableLanguageKeys = function () {
2692
+ if ($availableLanguageKeys.length > 0) {
2693
+ return $availableLanguageKeys;
2694
+ }
2695
+ return null;
2696
+ };
2697
+
2698
+ /**
2699
+ * @ngdoc function
2700
+ * @name pascalprecht.translate.$translate#getTranslationTable
2701
+ * @methodOf pascalprecht.translate.$translate
2702
+ *
2703
+ * @description
2704
+ * Returns translation table by the given language key.
2705
+ *
2706
+ * Unless a language is provided it returns a translation table of the current one.
2707
+ * Note: If translation dictionary is currently downloading or in progress
2708
+ * it will return null.
2709
+ *
2710
+ * @param {string} langKey A token which represents a translation id
2711
+ *
2712
+ * @return {object} a copy of angular-translate $translationTable
2713
+ */
2714
+ $translate.getTranslationTable = function (langKey) {
2715
+ langKey = langKey || $translate.use();
2716
+ if (langKey && $translationTable[langKey]) {
2717
+ return angular.copy($translationTable[langKey]);
2718
+ }
2719
+ return null;
2720
+ };
2721
+
2722
+ // Whenever $translateReady is being fired, this will ensure the state of $isReady
2723
+ var globalOnReadyListener = $rootScope.$on('$translateReady', function () {
2724
+ $onReadyDeferred.resolve();
2725
+ globalOnReadyListener(); // one time only
2726
+ globalOnReadyListener = null;
2727
+ });
2728
+ var globalOnChangeListener = $rootScope.$on('$translateChangeEnd', function () {
2729
+ $onReadyDeferred.resolve();
2730
+ globalOnChangeListener(); // one time only
2731
+ globalOnChangeListener = null;
2732
+ });
2296
2733
 
2297
- if ($loaderFactory) {
2734
+ if ($loaderFactory) {
2298
2735
 
2299
- // If at least one async loader is defined and there are no
2300
- // (default) translations available we should try to load them.
2301
- if (angular.equals($translationTable, {})) {
2736
+ // If at least one async loader is defined and there are no
2737
+ // (default) translations available we should try to load them.
2738
+ if (angular.equals($translationTable, {})) {
2739
+ if ($translate.use()) {
2302
2740
  $translate.use($translate.use());
2303
2741
  }
2742
+ }
2304
2743
 
2305
- // Also, if there are any fallback language registered, we start
2306
- // loading them asynchronously as soon as we can.
2307
- if ($fallbackLanguage && $fallbackLanguage.length) {
2308
- var processAsyncResult = function (translation) {
2309
- translations(translation.key, translation.table);
2310
- $rootScope.$emit('$translateChangeEnd', { language: translation.key });
2311
- return translation;
2312
- };
2313
- for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
2314
- var fallbackLanguageId = $fallbackLanguage[i];
2315
- if ($forceAsyncReloadEnabled || !$translationTable[fallbackLanguageId]) {
2316
- langPromises[fallbackLanguageId] = loadAsync(fallbackLanguageId).then(processAsyncResult);
2317
- }
2744
+ // Also, if there are any fallback language registered, we start
2745
+ // loading them asynchronously as soon as we can.
2746
+ if ($fallbackLanguage && $fallbackLanguage.length) {
2747
+ var processAsyncResult = function (translation) {
2748
+ translations(translation.key, translation.table);
2749
+ $rootScope.$emit('$translateChangeEnd', {language : translation.key});
2750
+ return translation;
2751
+ };
2752
+ for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
2753
+ var fallbackLanguageId = $fallbackLanguage[i];
2754
+ if ($forceAsyncReloadEnabled || !$translationTable[fallbackLanguageId]) {
2755
+ langPromises[fallbackLanguageId] = loadAsync(fallbackLanguageId).then(processAsyncResult);
2318
2756
  }
2319
2757
  }
2320
2758
  }
2321
-
2322
- return $translate;
2759
+ } else {
2760
+ $rootScope.$emit('$translateReady', {language : $translate.use()});
2323
2761
  }
2324
- ];
2762
+
2763
+ return $translate;
2764
+ }];
2325
2765
  }
2326
- $translate.$inject = ['$STORAGE_KEY', '$windowProvider', '$translateSanitizationProvider', 'pascalprechtTranslateOverrider'];
2327
2766
 
2328
2767
  $translate.displayName = 'displayName';
2329
2768
 
@@ -2395,24 +2834,41 @@ function $translateDefaultInterpolation ($interpolate, $translateSanitization) {
2395
2834
  * @methodOf pascalprecht.translate.$translateDefaultInterpolation
2396
2835
  *
2397
2836
  * @description
2398
- * Interpolates given string agains given interpolate params using angulars
2837
+ * Interpolates given value agains given interpolate params using angulars
2399
2838
  * `$interpolate` service.
2400
2839
  *
2401
- * @returns {string} interpolated string.
2840
+ * Since AngularJS 1.5, `value` must not be a string but can be anything input.
2841
+ *
2842
+ * @param {string} value translation
2843
+ * @param {object} interpolationParams interpolation params
2844
+ * @param {string} context current context (filter, directive, service)
2845
+ * @param {string} sanitizeStrategy sanitize strategy
2846
+ * @param {string} translationId current translationId
2847
+ *
2848
+ * @returns {string} interpolated string
2402
2849
  */
2403
- $translateInterpolator.interpolate = function (string, interpolationParams) {
2850
+ $translateInterpolator.interpolate = function (value, interpolationParams, context, sanitizeStrategy, translationId) { // jshint ignore:line
2404
2851
  interpolationParams = interpolationParams || {};
2405
- interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params');
2406
-
2407
- var interpolatedText = $interpolate(string)(interpolationParams);
2408
- interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text');
2852
+ interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params', sanitizeStrategy, context);
2853
+
2854
+ var interpolatedText;
2855
+ if (angular.isNumber(value)) {
2856
+ // numbers are safe
2857
+ interpolatedText = '' + value;
2858
+ } else if (angular.isString(value)) {
2859
+ // strings must be interpolated (that's the job here)
2860
+ interpolatedText = $interpolate(value)(interpolationParams);
2861
+ interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text', sanitizeStrategy, context);
2862
+ } else {
2863
+ // neither a number or a string, cant interpolate => empty string
2864
+ interpolatedText = '';
2865
+ }
2409
2866
 
2410
2867
  return interpolatedText;
2411
2868
  };
2412
2869
 
2413
2870
  return $translateInterpolator;
2414
2871
  }
2415
- $translateDefaultInterpolation.$inject = ['$interpolate', '$translateSanitization'];
2416
2872
 
2417
2873
  $translateDefaultInterpolation.displayName = '$translateDefaultInterpolation';
2418
2874
 
@@ -2422,14 +2878,15 @@ angular.module('pascalprecht.translate')
2422
2878
  /**
2423
2879
  * @ngdoc directive
2424
2880
  * @name pascalprecht.translate.directive:translate
2425
- * @requires $compile
2426
- * @requires $filter
2427
- * @requires $interpolate
2428
- * @restrict A
2881
+ * @requires $interpolate,
2882
+ * @requires $compile,
2883
+ * @requires $parse,
2884
+ * @requires $rootScope
2885
+ * @restrict AE
2429
2886
  *
2430
2887
  * @description
2431
2888
  * Translates given translation id either through attribute or DOM content.
2432
- * Internally it uses `translate` filter to translate translation id. It possible to
2889
+ * Internally it uses $translate service to translate the translation id. It possible to
2433
2890
  * pass an optional `translate-values` object literal as string into translation id.
2434
2891
  *
2435
2892
  * @param {string=} translate Translation id which could be either string or interpolated string.
@@ -2437,6 +2894,7 @@ angular.module('pascalprecht.translate')
2437
2894
  * @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute.
2438
2895
  * @param {string=} translate-default will be used unless translation was successful
2439
2896
  * @param {boolean=} translate-compile (default true if present) defines locally activation of {@link pascalprecht.translate.$translateProvider#methods_usePostCompiling}
2897
+ * @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}
2440
2898
  *
2441
2899
  * @example
2442
2900
  <example module="ngView">
@@ -2453,6 +2911,7 @@ angular.module('pascalprecht.translate')
2453
2911
  <pre translate="WITH_VALUES" translate-values="{{values}}"></pre>
2454
2912
  <pre translate translate-values="{{values}}">WITH_VALUES</pre>
2455
2913
  <pre translate translate-attr-title="WITH_VALUES" translate-values="{{values}}"></pre>
2914
+ <pre translate="WITH_CAMEL_CASE_KEY" translate-value-camel-case-key="Hi"></pre>
2456
2915
 
2457
2916
  </div>
2458
2917
  </file>
@@ -2463,7 +2922,8 @@ angular.module('pascalprecht.translate')
2463
2922
 
2464
2923
  $translateProvider.translations('en',{
2465
2924
  'TRANSLATION_ID': 'Hello there!',
2466
- 'WITH_VALUES': 'The following value is dynamic: {{value}}'
2925
+ 'WITH_VALUES': 'The following value is dynamic: {{value}}',
2926
+ 'WITH_CAMEL_CASE_KEY': 'The interpolation key is camel cased: {{camelCaseKey}}'
2467
2927
  }).preferredLanguage('en');
2468
2928
 
2469
2929
  });
@@ -2500,13 +2960,17 @@ angular.module('pascalprecht.translate')
2500
2960
  element = $compile('<p translate translate-attr-title="TRANSLATION_ID"></p>')($rootScope);
2501
2961
  $rootScope.$digest();
2502
2962
  expect(element.attr('title')).toBe('Hello there!');
2963
+
2964
+ element = $compile('<p translate="WITH_CAMEL_CASE_KEY" translate-value-camel-case-key="Hello"></p>')($rootScope);
2965
+ $rootScope.$digest();
2966
+ expect(element.text()).toBe('The interpolation key is camel cased: Hello');
2503
2967
  });
2504
2968
  });
2505
2969
  </file>
2506
2970
  </example>
2507
2971
  */
2508
2972
  .directive('translate', translateDirective);
2509
- function translateDirective($translate, $q, $interpolate, $compile, $parse, $rootScope) {
2973
+ function translateDirective($translate, $interpolate, $compile, $parse, $rootScope) {
2510
2974
 
2511
2975
  'use strict';
2512
2976
 
@@ -2545,6 +3009,7 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2545
3009
  scope.interpolateParams = {};
2546
3010
  scope.preText = '';
2547
3011
  scope.postText = '';
3012
+ scope.translateNamespace = getTranslateNamespace(scope);
2548
3013
  var translationIds = {};
2549
3014
 
2550
3015
  var initInterpolationParams = function (interpolateParams, iAttr, tAttr) {
@@ -2575,14 +3040,16 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2575
3040
  }
2576
3041
 
2577
3042
  if (angular.equals(translationId , '') || !angular.isDefined(translationId)) {
3043
+ var iElementText = trim.apply(iElement.text());
3044
+
2578
3045
  // Resolve translation id by inner html if required
2579
- var interpolateMatches = trim.apply(iElement.text()).match(interpolateRegExp);
3046
+ var interpolateMatches = iElementText.match(interpolateRegExp);
2580
3047
  // Interpolate translation id if required
2581
3048
  if (angular.isArray(interpolateMatches)) {
2582
3049
  scope.preText = interpolateMatches[1];
2583
3050
  scope.postText = interpolateMatches[3];
2584
3051
  translationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent);
2585
- var watcherMatches = iElement.text().match(watcherRegExp);
3052
+ var watcherMatches = iElementText.match(watcherRegExp);
2586
3053
  if (angular.isArray(watcherMatches) && watcherMatches[2] && watcherMatches[2].length) {
2587
3054
  observeElementTranslation._unwatchOld = scope.$watch(watcherMatches[2], function (newValue) {
2588
3055
  translationIds.translate = newValue;
@@ -2590,7 +3057,8 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2590
3057
  });
2591
3058
  }
2592
3059
  } else {
2593
- translationIds.translate = iElement.text().replace(/^\s+|\s+$/g,'');
3060
+ // do not assigne the translation id if it is empty.
3061
+ translationIds.translate = !iElementText ? undefined : iElementText;
2594
3062
  }
2595
3063
  } else {
2596
3064
  translationIds.translate = translationId;
@@ -2624,13 +3092,14 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2624
3092
  });
2625
3093
 
2626
3094
  for (var translateAttr in iAttr) {
2627
- if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr') {
3095
+ if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr' && translateAttr.length > 13) {
2628
3096
  observeAttributeTranslation(translateAttr);
2629
3097
  }
2630
3098
  }
2631
3099
 
2632
3100
  iAttr.$observe('translateDefault', function (value) {
2633
3101
  scope.defaultText = value;
3102
+ updateTranslations();
2634
3103
  });
2635
3104
 
2636
3105
  if (translateValuesExist) {
@@ -2660,17 +3129,21 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2660
3129
  // Master update function
2661
3130
  var updateTranslations = function () {
2662
3131
  for (var key in translationIds) {
2663
-
2664
3132
  if (translationIds.hasOwnProperty(key) && translationIds[key] !== undefined) {
2665
- updateTranslation(key, translationIds[key], scope, scope.interpolateParams, scope.defaultText);
3133
+ updateTranslation(key, translationIds[key], scope, scope.interpolateParams, scope.defaultText, scope.translateNamespace);
2666
3134
  }
2667
3135
  }
2668
3136
  };
2669
3137
 
2670
3138
  // Put translation processing function outside loop
2671
- var updateTranslation = function(translateAttr, translationId, scope, interpolateParams, defaultTranslationText) {
3139
+ var updateTranslation = function(translateAttr, translationId, scope, interpolateParams, defaultTranslationText, translateNamespace) {
2672
3140
  if (translationId) {
2673
- $translate(translationId, interpolateParams, translateInterpolation, defaultTranslationText)
3141
+ // if translation id starts with '.' and translateNamespace given, prepend namespace
3142
+ if (translateNamespace && translationId.charAt(0) === '.') {
3143
+ translationId = translateNamespace + translationId;
3144
+ }
3145
+
3146
+ $translate(translationId, interpolateParams, translateInterpolation, defaultTranslationText, scope.translateLanguage)
2674
3147
  .then(function (translation) {
2675
3148
  applyTranslation(translation, scope, true, translateAttr);
2676
3149
  }, function (translationId) {
@@ -2683,12 +3156,16 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2683
3156
  };
2684
3157
 
2685
3158
  var applyTranslation = function (value, scope, successful, translateAttr) {
3159
+ if (!successful) {
3160
+ if (typeof scope.defaultText !== 'undefined') {
3161
+ value = scope.defaultText;
3162
+ }
3163
+ }
2686
3164
  if (translateAttr === 'translate') {
2687
3165
  // default translate into innerHTML
2688
- if (!successful && typeof scope.defaultText !== 'undefined') {
2689
- value = scope.defaultText;
3166
+ if (successful || (!successful && !$translate.isKeepContent() && typeof iAttr.translateKeepContent === 'undefined')) {
3167
+ iElement.empty().append(scope.preText + value + scope.postText);
2690
3168
  }
2691
- iElement.html(scope.preText + value + scope.postText);
2692
3169
  var globallyEnabled = $translate.isPostCompilingEnabled();
2693
3170
  var locallyDefined = typeof tAttr.translateCompile !== 'undefined';
2694
3171
  var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false';
@@ -2697,9 +3174,6 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2697
3174
  }
2698
3175
  } else {
2699
3176
  // translate attribute
2700
- if (!successful && typeof scope.defaultText !== 'undefined') {
2701
- value = scope.defaultText;
2702
- }
2703
3177
  var attributeName = iAttr.$attr[translateAttr];
2704
3178
  if (attributeName.substr(0, 5) === 'data-') {
2705
3179
  // ensure html5 data prefix is stripped
@@ -2714,6 +3188,9 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2714
3188
  scope.$watch('interpolateParams', updateTranslations, true);
2715
3189
  }
2716
3190
 
3191
+ // Replaced watcher on translateLanguage with event listener
3192
+ scope.$on('translateLanguageChanged', updateTranslations);
3193
+
2717
3194
  // Ensures the text will be refreshed after the current language was changed
2718
3195
  // w/ $translate.use(...)
2719
3196
  var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations);
@@ -2735,15 +3212,179 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
2735
3212
  }
2736
3213
  };
2737
3214
  }
2738
- translateDirective.$inject = ['$translate', '$q', '$interpolate', '$compile', '$parse', '$rootScope'];
3215
+
3216
+ /**
3217
+ * Returns the scope's namespace.
3218
+ * @private
3219
+ * @param scope
3220
+ * @returns {string}
3221
+ */
3222
+ function getTranslateNamespace(scope) {
3223
+ 'use strict';
3224
+ if (scope.translateNamespace) {
3225
+ return scope.translateNamespace;
3226
+ }
3227
+ if (scope.$parent) {
3228
+ return getTranslateNamespace(scope.$parent);
3229
+ }
3230
+ }
2739
3231
 
2740
3232
  translateDirective.displayName = 'translateDirective';
2741
3233
 
3234
+ angular.module('pascalprecht.translate')
3235
+ /**
3236
+ * @ngdoc directive
3237
+ * @name pascalprecht.translate.directive:translate-attr
3238
+ * @restrict A
3239
+ *
3240
+ * @description
3241
+ * Translates attributes like translate-attr-ATTR, but with an object like ng-class.
3242
+ * Internally it uses `translate` service to translate translation id. It possible to
3243
+ * pass an optional `translate-values` object literal as string into translation id.
3244
+ *
3245
+ * @param {string=} translate-attr Object literal mapping attributes to translation ids.
3246
+ * @param {string=} translate-values Values to pass into the translation ids. Can be passed as object literal string.
3247
+ *
3248
+ * @example
3249
+ <example module="ngView">
3250
+ <file name="index.html">
3251
+ <div ng-controller="TranslateCtrl">
3252
+
3253
+ <input translate-attr="{ placeholder: translationId, title: 'WITH_VALUES' }" translate-values="{value: 5}" />
3254
+
3255
+ </div>
3256
+ </file>
3257
+ <file name="script.js">
3258
+ angular.module('ngView', ['pascalprecht.translate'])
3259
+
3260
+ .config(function ($translateProvider) {
3261
+
3262
+ $translateProvider.translations('en',{
3263
+ 'TRANSLATION_ID': 'Hello there!',
3264
+ 'WITH_VALUES': 'The following value is dynamic: {{value}}',
3265
+ }).preferredLanguage('en');
3266
+
3267
+ });
3268
+
3269
+ angular.module('ngView').controller('TranslateCtrl', function ($scope) {
3270
+ $scope.translationId = 'TRANSLATION_ID';
3271
+
3272
+ $scope.values = {
3273
+ value: 78
3274
+ };
3275
+ });
3276
+ </file>
3277
+ <file name="scenario.js">
3278
+ it('should translate', function () {
3279
+ inject(function ($rootScope, $compile) {
3280
+ $rootScope.translationId = 'TRANSLATION_ID';
3281
+
3282
+ element = $compile('<input translate-attr="{ placeholder: translationId, title: 'WITH_VALUES' }" translate-values="{ value: 5 }" />')($rootScope);
3283
+ $rootScope.$digest();
3284
+ expect(element.attr('placeholder)).toBe('Hello there!');
3285
+ expect(element.attr('title)).toBe('The following value is dynamic: 5');
3286
+ });
3287
+ });
3288
+ </file>
3289
+ </example>
3290
+ */
3291
+ .directive('translateAttr', translateAttrDirective);
3292
+ function translateAttrDirective($translate, $rootScope) {
3293
+
3294
+ 'use strict';
3295
+
3296
+ return {
3297
+ restrict: 'A',
3298
+ priority: $translate.directivePriority(),
3299
+ link: function linkFn(scope, element, attr) {
3300
+
3301
+ var translateAttr,
3302
+ translateValues,
3303
+ previousAttributes = {};
3304
+
3305
+ // Main update translations function
3306
+ var updateTranslations = function () {
3307
+ angular.forEach(translateAttr, function (translationId, attributeName) {
3308
+ if (!translationId) {
3309
+ return;
3310
+ }
3311
+ previousAttributes[attributeName] = true;
3312
+
3313
+ // if translation id starts with '.' and translateNamespace given, prepend namespace
3314
+ if (scope.translateNamespace && translationId.charAt(0) === '.') {
3315
+ translationId = scope.translateNamespace + translationId;
3316
+ }
3317
+ $translate(translationId, translateValues, attr.translateInterpolation, undefined, scope.translateLanguage)
3318
+ .then(function (translation) {
3319
+ element.attr(attributeName, translation);
3320
+ }, function (translationId) {
3321
+ element.attr(attributeName, translationId);
3322
+ });
3323
+ });
3324
+
3325
+ // Removing unused attributes that were previously used
3326
+ angular.forEach(previousAttributes, function (flag, attributeName) {
3327
+ if (!translateAttr[attributeName]) {
3328
+ element.removeAttr(attributeName);
3329
+ delete previousAttributes[attributeName];
3330
+ }
3331
+ });
3332
+ };
3333
+
3334
+ // Watch for attribute changes
3335
+ watchAttribute(
3336
+ scope,
3337
+ attr.translateAttr,
3338
+ function (newValue) { translateAttr = newValue; },
3339
+ updateTranslations
3340
+ );
3341
+ // Watch for value changes
3342
+ watchAttribute(
3343
+ scope,
3344
+ attr.translateValues,
3345
+ function (newValue) { translateValues = newValue; },
3346
+ updateTranslations
3347
+ );
3348
+
3349
+ if (attr.translateValues) {
3350
+ scope.$watch(attr.translateValues, updateTranslations, true);
3351
+ }
3352
+
3353
+ // Replaced watcher on translateLanguage with event listener
3354
+ scope.$on('translateLanguageChanged', updateTranslations);
3355
+
3356
+ // Ensures the text will be refreshed after the current language was changed
3357
+ // w/ $translate.use(...)
3358
+ var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations);
3359
+
3360
+ updateTranslations();
3361
+ scope.$on('$destroy', unbind);
3362
+ }
3363
+ };
3364
+ }
3365
+
3366
+ function watchAttribute(scope, attribute, valueCallback, changeCallback) {
3367
+ 'use strict';
3368
+ if (!attribute) {
3369
+ return;
3370
+ }
3371
+ if (attribute.substr(0, 2) === '::') {
3372
+ attribute = attribute.substr(2);
3373
+ } else {
3374
+ scope.$watch(attribute, function(newValue) {
3375
+ valueCallback(newValue);
3376
+ changeCallback();
3377
+ }, true);
3378
+ }
3379
+ valueCallback(scope.$eval(attribute));
3380
+ }
3381
+
3382
+ translateAttrDirective.displayName = 'translateAttrDirective';
3383
+
2742
3384
  angular.module('pascalprecht.translate')
2743
3385
  /**
2744
3386
  * @ngdoc directive
2745
3387
  * @name pascalprecht.translate.directive:translateCloak
2746
- * @requires $rootScope
2747
3388
  * @requires $translate
2748
3389
  * @restrict A
2749
3390
  *
@@ -2763,40 +3404,205 @@ angular.module('pascalprecht.translate')
2763
3404
  */
2764
3405
  .directive('translateCloak', translateCloakDirective);
2765
3406
 
2766
- function translateCloakDirective($rootScope, $translate) {
3407
+ function translateCloakDirective($translate, $rootScope) {
2767
3408
 
2768
3409
  'use strict';
2769
3410
 
2770
3411
  return {
2771
- compile: function (tElement) {
2772
- var applyCloak = function () {
2773
- tElement.addClass($translate.cloakClassName());
2774
- },
2775
- removeCloak = function () {
2776
- tElement.removeClass($translate.cloakClassName());
2777
- },
2778
- removeListener = $rootScope.$on('$translateChangeEnd', function () {
2779
- removeCloak();
2780
- removeListener();
2781
- removeListener = null;
2782
- });
2783
- applyCloak();
3412
+ compile : function (tElement) {
3413
+ var applyCloak = function (element) {
3414
+ element.addClass($translate.cloakClassName());
3415
+ },
3416
+ removeCloak = function (element) {
3417
+ element.removeClass($translate.cloakClassName());
3418
+ };
3419
+ applyCloak(tElement);
2784
3420
 
2785
3421
  return function linkFn(scope, iElement, iAttr) {
2786
- // Register a watcher for the defined translation allowing a fine tuned cloak
3422
+ //Create bound functions that incorporate the active DOM element.
3423
+ var iRemoveCloak = removeCloak.bind(this, iElement), iApplyCloak = applyCloak.bind(this, iElement);
2787
3424
  if (iAttr.translateCloak && iAttr.translateCloak.length) {
3425
+ // Register a watcher for the defined translation allowing a fine tuned cloak
2788
3426
  iAttr.$observe('translateCloak', function (translationId) {
2789
- $translate(translationId).then(removeCloak, applyCloak);
3427
+ $translate(translationId).then(iRemoveCloak, iApplyCloak);
2790
3428
  });
3429
+ $rootScope.$on('$translateChangeSuccess', function () {
3430
+ $translate(iAttr.translateCloak).then(iRemoveCloak, iApplyCloak);
3431
+ });
3432
+ } else {
3433
+ $translate.onReady(iRemoveCloak);
2791
3434
  }
2792
3435
  };
2793
3436
  }
2794
3437
  };
2795
3438
  }
2796
- translateCloakDirective.$inject = ['$rootScope', '$translate'];
2797
3439
 
2798
3440
  translateCloakDirective.displayName = 'translateCloakDirective';
2799
3441
 
3442
+ angular.module('pascalprecht.translate')
3443
+ /**
3444
+ * @ngdoc directive
3445
+ * @name pascalprecht.translate.directive:translateNamespace
3446
+ * @restrict A
3447
+ *
3448
+ * @description
3449
+ * Translates given translation id either through attribute or DOM content.
3450
+ * Internally it uses `translate` filter to translate translation id. It possible to
3451
+ * pass an optional `translate-values` object literal as string into translation id.
3452
+ *
3453
+ * @param {string=} translate namespace name which could be either string or interpolated string.
3454
+ *
3455
+ * @example
3456
+ <example module="ngView">
3457
+ <file name="index.html">
3458
+ <div translate-namespace="CONTENT">
3459
+
3460
+ <div>
3461
+ <h1 translate>.HEADERS.TITLE</h1>
3462
+ <h1 translate>.HEADERS.WELCOME</h1>
3463
+ </div>
3464
+
3465
+ <div translate-namespace=".HEADERS">
3466
+ <h1 translate>.TITLE</h1>
3467
+ <h1 translate>.WELCOME</h1>
3468
+ </div>
3469
+
3470
+ </div>
3471
+ </file>
3472
+ <file name="script.js">
3473
+ angular.module('ngView', ['pascalprecht.translate'])
3474
+
3475
+ .config(function ($translateProvider) {
3476
+
3477
+ $translateProvider.translations('en',{
3478
+ 'TRANSLATION_ID': 'Hello there!',
3479
+ 'CONTENT': {
3480
+ 'HEADERS': {
3481
+ TITLE: 'Title'
3482
+ }
3483
+ },
3484
+ 'CONTENT.HEADERS.WELCOME': 'Welcome'
3485
+ }).preferredLanguage('en');
3486
+
3487
+ });
3488
+
3489
+ </file>
3490
+ </example>
3491
+ */
3492
+ .directive('translateNamespace', translateNamespaceDirective);
3493
+
3494
+ function translateNamespaceDirective() {
3495
+
3496
+ 'use strict';
3497
+
3498
+ return {
3499
+ restrict: 'A',
3500
+ scope: true,
3501
+ compile: function () {
3502
+ return {
3503
+ pre: function (scope, iElement, iAttrs) {
3504
+ scope.translateNamespace = getTranslateNamespace(scope);
3505
+
3506
+ if (scope.translateNamespace && iAttrs.translateNamespace.charAt(0) === '.') {
3507
+ scope.translateNamespace += iAttrs.translateNamespace;
3508
+ } else {
3509
+ scope.translateNamespace = iAttrs.translateNamespace;
3510
+ }
3511
+ }
3512
+ };
3513
+ }
3514
+ };
3515
+ }
3516
+
3517
+ /**
3518
+ * Returns the scope's namespace.
3519
+ * @private
3520
+ * @param scope
3521
+ * @returns {string}
3522
+ */
3523
+ function getTranslateNamespace(scope) {
3524
+ 'use strict';
3525
+ if (scope.translateNamespace) {
3526
+ return scope.translateNamespace;
3527
+ }
3528
+ if (scope.$parent) {
3529
+ return getTranslateNamespace(scope.$parent);
3530
+ }
3531
+ }
3532
+
3533
+ translateNamespaceDirective.displayName = 'translateNamespaceDirective';
3534
+
3535
+ angular.module('pascalprecht.translate')
3536
+ /**
3537
+ * @ngdoc directive
3538
+ * @name pascalprecht.translate.directive:translateLanguage
3539
+ * @restrict A
3540
+ *
3541
+ * @description
3542
+ * Forces the language to the directives in the underlying scope.
3543
+ *
3544
+ * @param {string=} translate language that will be negotiated.
3545
+ *
3546
+ * @example
3547
+ <example module="ngView">
3548
+ <file name="index.html">
3549
+ <div>
3550
+
3551
+ <div>
3552
+ <h1 translate>HELLO</h1>
3553
+ </div>
3554
+
3555
+ <div translate-language="de">
3556
+ <h1 translate>HELLO</h1>
3557
+ </div>
3558
+
3559
+ </div>
3560
+ </file>
3561
+ <file name="script.js">
3562
+ angular.module('ngView', ['pascalprecht.translate'])
3563
+
3564
+ .config(function ($translateProvider) {
3565
+
3566
+ $translateProvider
3567
+ .translations('en',{
3568
+ 'HELLO': 'Hello world!'
3569
+ })
3570
+ .translations('de',{
3571
+ 'HELLO': 'Hallo Welt!'
3572
+ })
3573
+ .preferredLanguage('en');
3574
+
3575
+ });
3576
+
3577
+ </file>
3578
+ </example>
3579
+ */
3580
+ .directive('translateLanguage', translateLanguageDirective);
3581
+
3582
+ function translateLanguageDirective() {
3583
+
3584
+ 'use strict';
3585
+
3586
+ return {
3587
+ restrict: 'A',
3588
+ scope: true,
3589
+ compile: function () {
3590
+ return function linkFn(scope, iElement, iAttrs) {
3591
+
3592
+ iAttrs.$observe('translateLanguage', function (newTranslateLanguage) {
3593
+ scope.translateLanguage = newTranslateLanguage;
3594
+ });
3595
+
3596
+ scope.$watch('translateLanguage', function(){
3597
+ scope.$broadcast('translateLanguageChanged');
3598
+ });
3599
+ };
3600
+ }
3601
+ };
3602
+ }
3603
+
3604
+ translateLanguageDirective.displayName = 'translateLanguageDirective';
3605
+
2800
3606
  angular.module('pascalprecht.translate')
2801
3607
  /**
2802
3608
  * @ngdoc filter
@@ -2855,13 +3661,15 @@ function translateFilterFactory($parse, $translate) {
2855
3661
 
2856
3662
  'use strict';
2857
3663
 
2858
- var translateFilter = function (translationId, interpolateParams, interpolation) {
2859
-
3664
+ var translateFilter = function (translationId, interpolateParams, interpolation, forceLanguage) {
2860
3665
  if (!angular.isObject(interpolateParams)) {
2861
- interpolateParams = $parse(interpolateParams)(this);
3666
+ var ctx = this || {
3667
+ '__SCOPE_IS_NOT_AVAILABLE': 'More info at https://github.com/angular/angular.js/commit/8863b9d04c722b278fa93c5d66ad1e578ad6eb1f'
3668
+ };
3669
+ interpolateParams = $parse(interpolateParams)(ctx);
2862
3670
  }
2863
3671
 
2864
- return $translate.instant(translationId, interpolateParams, interpolation);
3672
+ return $translate.instant(translationId, interpolateParams, interpolation, forceLanguage);
2865
3673
  };
2866
3674
 
2867
3675
  if ($translate.statefulFilter()) {
@@ -2870,7 +3678,6 @@ function translateFilterFactory($parse, $translate) {
2870
3678
 
2871
3679
  return translateFilter;
2872
3680
  }
2873
- translateFilterFactory.$inject = ['$parse', '$translate'];
2874
3681
 
2875
3682
  translateFilterFactory.displayName = 'translateFilterFactory';
2876
3683
 
@@ -2896,7 +3703,6 @@ function $translationCache($cacheFactory) {
2896
3703
 
2897
3704
  return $cacheFactory('translations');
2898
3705
  }
2899
- $translationCache.$inject = ['$cacheFactory'];
2900
3706
 
2901
3707
  $translationCache.displayName = '$translationCache';
2902
3708
  return 'pascalprecht.translate';