angular-translate-rails-tf 2.7.2 → 2.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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';