angular-translate-rails-tf 2.6.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48f0076777fbcf6d9711fd95687b7665653e1835
4
+ data.tar.gz: e7b576ef00e88fbf21bc09a0832e76e5973718e7
5
+ SHA512:
6
+ metadata.gz: b34fb0e7b879c19743f5ca9ede714049c1ee971a70af6879c15b6f9050dccc24eac18834b622fc853437ae5c2b215b2c6d712fc72da275d553b65a68676692af
7
+ data.tar.gz: 6bef5dbae974734992ade06be419e8d7c24b8e551685165e8c01ce1d1d35e521a86f545f3d3af927947bce50ed20c79facb3fa351846adeee54c02d6a025ccd2
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 2.6.0 (2015-02-26)
2
+
3
+ Initial commit matched with angular-translate version, not really released yet.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Kanwaldeep Singh Arneja
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # angular-translate-rails
2
+
3
+ angular-translate-rails wraps the [angular-translate.js](http://angular-translate.github.io) library in a rails engine for simple
4
+ use with the asset pipeline provided by Rails 3.1 and higher. The gem includes the development (non-minified)
5
+ source for ease of exploration. The asset pipeline will minify in production.
6
+
7
+ angular-translate is an AngularJS module that makes your life much easier when it comes
8
+ to i18n and l10n including lazy loading and pluralization Please see the
9
+ [documentation](http://angular-translate.github.io/docs/#/api) for details.
10
+
11
+ ## Usage
12
+
13
+ Add the following to your gemfile:
14
+
15
+ gem 'angular-translate-rails'
16
+
17
+ Add the following directive to your Javascript manifest file (application.js):
18
+
19
+ //= require angular-translate
20
+
21
+ ## Versioning
22
+
23
+ angular-translate-rails 2.6.0 == angular-translate.js 2.6.0
24
+
25
+ Every attempt is made to mirror the currently shipping angular-translate.js version number wherever possible.
26
+ The major, minor, and patch version numbers will always represent the angular-translate.js version. Should a gem
27
+ bug be discovered, a 4th version identifier will be added and incremented.
@@ -0,0 +1,6 @@
1
+ module AngularTranslate
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class AngularTranslateRailsTest < ActiveSupport::TestCase
4
+ test "truth" do
5
+ assert_kind_of Module, AngularTranslate::Rails
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ require File.expand_path('../config/application', __FILE__)
2
+
3
+ Dummy::Application.load_tasks
@@ -0,0 +1,22 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require "action_mailer/railtie"
4
+ require "sprockets/railtie"
5
+
6
+ Bundler.require
7
+ require "angular-translate-rails"
8
+
9
+ module Dummy
10
+ class Application < Rails::Application
11
+
12
+ config.encoding = "utf-8"
13
+
14
+ config.filter_parameters += [:password]
15
+
16
+ config.active_support.escape_html_entities_in_json = true
17
+
18
+ config.assets.enabled = true
19
+
20
+ config.assets.version = '1.0'
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ gemfile = File.expand_path('../../../../Gemfile', __FILE__)
3
+
4
+ if File.exist?(gemfile)
5
+ ENV['BUNDLE_GEMFILE'] = gemfile
6
+ require 'bundler'
7
+ Bundler.setup
8
+ end
9
+
10
+ $:.unshift File.expand_path('../../../../lib', __FILE__)
@@ -0,0 +1,3 @@
1
+ require File.expand_path('../application', __FILE__)
2
+
3
+ Dummy::Application.initialize!
@@ -0,0 +1,20 @@
1
+ Dummy::Application.configure do
2
+
3
+ config.cache_classes = false
4
+
5
+ config.whiny_nils = true
6
+
7
+ config.consider_all_requests_local = true
8
+
9
+ config.action_controller.perform_caching = false
10
+
11
+ config.action_mailer.raise_delivery_errors = false
12
+
13
+ config.active_support.deprecation = :log
14
+
15
+ config.action_dispatch.best_standards_support = :builtin
16
+
17
+ config.assets.compress = false
18
+
19
+ config.assets.debug = true
20
+ end
@@ -0,0 +1,20 @@
1
+ Dummy::Application.configure do
2
+
3
+ config.cache_classes = true
4
+
5
+ config.consider_all_requests_local = false
6
+
7
+ config.action_controller.perform_caching = true
8
+
9
+ config.serve_static_assets = false
10
+
11
+ config.assets.compress = true
12
+
13
+ config.assets.compile = false
14
+
15
+ config.assets.digest = true
16
+
17
+ config.i18n.fallbacks = true
18
+
19
+ config.active_support.deprecation = :notify
20
+ end
@@ -0,0 +1,22 @@
1
+ Dummy::Application.configure do
2
+
3
+ config.cache_classes = true
4
+
5
+ config.serve_static_assets = true
6
+
7
+ config.static_cache_control = "public, max-age=3600"
8
+
9
+ config.whiny_nils = true
10
+
11
+ config.consider_all_requests_local = true
12
+
13
+ config.action_controller.perform_caching = false
14
+
15
+ config.action_dispatch.show_exceptions = false
16
+
17
+ config.action_controller.allow_forgery_protection = false
18
+
19
+ config.action_mailer.delivery_method = :test
20
+
21
+ config.active_support.deprecation = :stderr
22
+ end
@@ -0,0 +1 @@
1
+ # Be sure to restart your server when you modify this file.
@@ -0,0 +1 @@
1
+ # Be sure to restart your server when you modify this file.
@@ -0,0 +1 @@
1
+ # Be sure to restart your server when you modify this file.
@@ -0,0 +1,3 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ Dummy::Application.config.secret_token = 'a50ecf55a3b3f1ad2e345253fac2d11d774a26018ca9f74577d9a5e688cdddd39e6b1bff1a9188e6cd982f7dfc7ef5d2608ddd31894472d5ef092dc4b0288309'
@@ -0,0 +1 @@
1
+ Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
@@ -0,0 +1,4 @@
1
+ ActiveSupport.on_load(:action_controller) do
2
+ wrap_parameters format: [:json]
3
+ end
4
+
@@ -0,0 +1,2 @@
1
+ en:
2
+ hello: "Hello world"
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Dummy::Application
@@ -0,0 +1,13 @@
1
+ require 'test_helper'
2
+
3
+ class ResourceTest < ActionDispatch::IntegrationTest
4
+ test 'can access angular-translate' do
5
+ get '/assets/angular-translate.js'
6
+ assert_response :success
7
+ end
8
+
9
+ test 'angular-translate response is for the expected version' do
10
+ get '/assets/angular-translate.js'
11
+ assert_match(/VERSION = '2\.6\.0'/, @response.body)
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+
3
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
4
+ require "rails/test_help"
5
+
6
+ Rails.backtrace_cleaner.remove_silencers!
7
+
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
9
+
10
+ if ActiveSupport::TestCase.method_defined?(:fixture_path=)
11
+ ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
12
+ end
@@ -0,0 +1,2359 @@
1
+ /*!
2
+ * angular-translate - v2.6.1 - 2015-03-01
3
+ * http://github.com/angular-translate/angular-translate
4
+ * Copyright (c) 2015 ; Licensed MIT
5
+ */
6
+ /**
7
+ * @ngdoc overview
8
+ * @name pascalprecht.translate
9
+ *
10
+ * @description
11
+ * The main module which holds everything together.
12
+ */
13
+ angular.module('pascalprecht.translate', ['ng'])
14
+
15
+ .run(['$translate', function ($translate) {
16
+
17
+ var key = $translate.storageKey(),
18
+ storage = $translate.storage();
19
+
20
+ var fallbackFromIncorrectStorageValue = function() {
21
+ var preferred = $translate.preferredLanguage();
22
+ if (angular.isString(preferred)) {
23
+ $translate.use(preferred);
24
+ // $translate.use() will also remember the language.
25
+ // So, we don't need to call storage.put() here.
26
+ } else {
27
+ storage.put(key, $translate.use());
28
+ }
29
+ };
30
+
31
+ if (storage) {
32
+ if (!storage.get(key)) {
33
+ fallbackFromIncorrectStorageValue();
34
+ } else {
35
+ $translate.use(storage.get(key))['catch'](fallbackFromIncorrectStorageValue);
36
+ }
37
+ } else if (angular.isString($translate.preferredLanguage())) {
38
+ $translate.use($translate.preferredLanguage());
39
+ }
40
+ }]);
41
+
42
+ /**
43
+ * @ngdoc object
44
+ * @name pascalprecht.translate.$translateProvider
45
+ * @description
46
+ *
47
+ * $translateProvider allows developers to register translation-tables, asynchronous loaders
48
+ * and similar to configure translation behavior directly inside of a module.
49
+ *
50
+ */
51
+ angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', '$windowProvider', function ($STORAGE_KEY, $windowProvider) {
52
+
53
+ var $translationTable = {},
54
+ $preferredLanguage,
55
+ $availableLanguageKeys = [],
56
+ $languageKeyAliases,
57
+ $fallbackLanguage,
58
+ $fallbackWasString,
59
+ $uses,
60
+ $nextLang,
61
+ $storageFactory,
62
+ $storageKey = $STORAGE_KEY,
63
+ $storagePrefix,
64
+ $missingTranslationHandlerFactory,
65
+ $interpolationFactory,
66
+ $interpolatorFactories = [],
67
+ $interpolationSanitizationStrategy = false,
68
+ $loaderFactory,
69
+ $cloakClassName = 'translate-cloak',
70
+ $loaderOptions,
71
+ $notFoundIndicatorLeft,
72
+ $notFoundIndicatorRight,
73
+ $postCompilingEnabled = false,
74
+ NESTED_OBJECT_DELIMITER = '.',
75
+ loaderCache,
76
+ directivePriority = 0;
77
+
78
+ var version = '2.6.1';
79
+
80
+ // tries to determine the browsers language
81
+ var getFirstBrowserLanguage = function () {
82
+ var nav = $windowProvider.$get().navigator,
83
+ browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'],
84
+ i,
85
+ language;
86
+
87
+ // support for HTML 5.1 "navigator.languages"
88
+ if (angular.isArray(nav.languages)) {
89
+ for (i = 0; i < nav.languages.length; i++) {
90
+ language = nav.languages[i];
91
+ if (language && language.length) {
92
+ return language;
93
+ }
94
+ }
95
+ }
96
+
97
+ // support for other well known properties in browsers
98
+ for (i = 0; i < browserLanguagePropertyKeys.length; i++) {
99
+ language = nav[browserLanguagePropertyKeys[i]];
100
+ if (language && language.length) {
101
+ return language;
102
+ }
103
+ }
104
+
105
+ return null;
106
+ };
107
+ getFirstBrowserLanguage.displayName = 'angular-translate/service: getFirstBrowserLanguage';
108
+
109
+ // tries to determine the browsers locale
110
+ var getLocale = function () {
111
+ return (getFirstBrowserLanguage() || '').split('-').join('_');
112
+ };
113
+ getLocale.displayName = 'angular-translate/service: getLocale';
114
+
115
+ /**
116
+ * @name indexOf
117
+ * @private
118
+ *
119
+ * @description
120
+ * indexOf polyfill. Kinda sorta.
121
+ *
122
+ * @param {array} array Array to search in.
123
+ * @param {string} searchElement Element to search for.
124
+ *
125
+ * @returns {int} Index of search element.
126
+ */
127
+ var indexOf = function(array, searchElement) {
128
+ for (var i = 0, len = array.length; i < len; i++) {
129
+ if (array[i] === searchElement) {
130
+ return i;
131
+ }
132
+ }
133
+ return -1;
134
+ };
135
+
136
+ /**
137
+ * @name trim
138
+ * @private
139
+ *
140
+ * @description
141
+ * trim polyfill
142
+ *
143
+ * @returns {string} The string stripped of whitespace from both ends
144
+ */
145
+ var trim = function() {
146
+ return this.toString().replace(/^\s+|\s+$/g, '');
147
+ };
148
+
149
+ var negotiateLocale = function (preferred) {
150
+
151
+ var avail = [],
152
+ locale = angular.lowercase(preferred),
153
+ i = 0,
154
+ n = $availableLanguageKeys.length;
155
+
156
+ for (; i < n; i++) {
157
+ avail.push(angular.lowercase($availableLanguageKeys[i]));
158
+ }
159
+
160
+ if (indexOf(avail, locale) > -1) {
161
+ return preferred;
162
+ }
163
+
164
+ if ($languageKeyAliases) {
165
+ var alias;
166
+ for (var langKeyAlias in $languageKeyAliases) {
167
+ var hasWildcardKey = false;
168
+ var hasExactKey = Object.prototype.hasOwnProperty.call($languageKeyAliases, langKeyAlias) &&
169
+ angular.lowercase(langKeyAlias) === angular.lowercase(preferred);
170
+
171
+ if (langKeyAlias.slice(-1) === '*') {
172
+ hasWildcardKey = langKeyAlias.slice(0, -1) === preferred.slice(0, langKeyAlias.length-1);
173
+ }
174
+ if (hasExactKey || hasWildcardKey) {
175
+ alias = $languageKeyAliases[langKeyAlias];
176
+ if (indexOf(avail, angular.lowercase(alias)) > -1) {
177
+ return alias;
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ var parts = preferred.split('_');
184
+
185
+ if (parts.length > 1 && indexOf(avail, angular.lowercase(parts[0])) > -1) {
186
+ return parts[0];
187
+ }
188
+
189
+ // If everything fails, just return the preferred, unchanged.
190
+ return preferred;
191
+ };
192
+
193
+ /**
194
+ * @ngdoc function
195
+ * @name pascalprecht.translate.$translateProvider#translations
196
+ * @methodOf pascalprecht.translate.$translateProvider
197
+ *
198
+ * @description
199
+ * Registers a new translation table for specific language key.
200
+ *
201
+ * To register a translation table for specific language, pass a defined language
202
+ * key as first parameter.
203
+ *
204
+ * <pre>
205
+ * // register translation table for language: 'de_DE'
206
+ * $translateProvider.translations('de_DE', {
207
+ * 'GREETING': 'Hallo Welt!'
208
+ * });
209
+ *
210
+ * // register another one
211
+ * $translateProvider.translations('en_US', {
212
+ * 'GREETING': 'Hello world!'
213
+ * });
214
+ * </pre>
215
+ *
216
+ * When registering multiple translation tables for for the same language key,
217
+ * the actual translation table gets extended. This allows you to define module
218
+ * specific translation which only get added, once a specific module is loaded in
219
+ * your app.
220
+ *
221
+ * Invoking this method with no arguments returns the translation table which was
222
+ * registered with no language key. Invoking it with a language key returns the
223
+ * related translation table.
224
+ *
225
+ * @param {string} key A language key.
226
+ * @param {object} translationTable A plain old JavaScript object that represents a translation table.
227
+ *
228
+ */
229
+ var translations = function (langKey, translationTable) {
230
+
231
+ if (!langKey && !translationTable) {
232
+ return $translationTable;
233
+ }
234
+
235
+ if (langKey && !translationTable) {
236
+ if (angular.isString(langKey)) {
237
+ return $translationTable[langKey];
238
+ }
239
+ } else {
240
+ if (!angular.isObject($translationTable[langKey])) {
241
+ $translationTable[langKey] = {};
242
+ }
243
+ angular.extend($translationTable[langKey], flatObject(translationTable));
244
+ }
245
+ return this;
246
+ };
247
+
248
+ this.translations = translations;
249
+
250
+ /**
251
+ * @ngdoc function
252
+ * @name pascalprecht.translate.$translateProvider#cloakClassName
253
+ * @methodOf pascalprecht.translate.$translateProvider
254
+ *
255
+ * @description
256
+ *
257
+ * Let's you change the class name for `translate-cloak` directive.
258
+ * Default class name is `translate-cloak`.
259
+ *
260
+ * @param {string} name translate-cloak class name
261
+ */
262
+ this.cloakClassName = function (name) {
263
+ if (!name) {
264
+ return $cloakClassName;
265
+ }
266
+ $cloakClassName = name;
267
+ return this;
268
+ };
269
+
270
+ /**
271
+ * @name flatObject
272
+ * @private
273
+ *
274
+ * @description
275
+ * Flats an object. This function is used to flatten given translation data with
276
+ * namespaces, so they are later accessible via dot notation.
277
+ */
278
+ var flatObject = function (data, path, result, prevKey) {
279
+ var key, keyWithPath, keyWithShortPath, val;
280
+
281
+ if (!path) {
282
+ path = [];
283
+ }
284
+ if (!result) {
285
+ result = {};
286
+ }
287
+ for (key in data) {
288
+ if (!Object.prototype.hasOwnProperty.call(data, key)) {
289
+ continue;
290
+ }
291
+ val = data[key];
292
+ if (angular.isObject(val)) {
293
+ flatObject(val, path.concat(key), result, key);
294
+ } else {
295
+ keyWithPath = path.length ? ('' + path.join(NESTED_OBJECT_DELIMITER) + NESTED_OBJECT_DELIMITER + key) : key;
296
+ if(path.length && key === prevKey){
297
+ // Create shortcut path (foo.bar == foo.bar.bar)
298
+ keyWithShortPath = '' + path.join(NESTED_OBJECT_DELIMITER);
299
+ // Link it to original path
300
+ result[keyWithShortPath] = '@:' + keyWithPath;
301
+ }
302
+ result[keyWithPath] = val;
303
+ }
304
+ }
305
+ return result;
306
+ };
307
+
308
+ /**
309
+ * @ngdoc function
310
+ * @name pascalprecht.translate.$translateProvider#addInterpolation
311
+ * @methodOf pascalprecht.translate.$translateProvider
312
+ *
313
+ * @description
314
+ * Adds interpolation services to angular-translate, so it can manage them.
315
+ *
316
+ * @param {object} factory Interpolation service factory
317
+ */
318
+ this.addInterpolation = function (factory) {
319
+ $interpolatorFactories.push(factory);
320
+ return this;
321
+ };
322
+
323
+ /**
324
+ * @ngdoc function
325
+ * @name pascalprecht.translate.$translateProvider#useMessageFormatInterpolation
326
+ * @methodOf pascalprecht.translate.$translateProvider
327
+ *
328
+ * @description
329
+ * Tells angular-translate to use interpolation functionality of messageformat.js.
330
+ * This is useful when having high level pluralization and gender selection.
331
+ */
332
+ this.useMessageFormatInterpolation = function () {
333
+ return this.useInterpolation('$translateMessageFormatInterpolation');
334
+ };
335
+
336
+ /**
337
+ * @ngdoc function
338
+ * @name pascalprecht.translate.$translateProvider#useInterpolation
339
+ * @methodOf pascalprecht.translate.$translateProvider
340
+ *
341
+ * @description
342
+ * Tells angular-translate which interpolation style to use as default, application-wide.
343
+ * Simply pass a factory/service name. The interpolation service has to implement
344
+ * the correct interface.
345
+ *
346
+ * @param {string} factory Interpolation service name.
347
+ */
348
+ this.useInterpolation = function (factory) {
349
+ $interpolationFactory = factory;
350
+ return this;
351
+ };
352
+
353
+ /**
354
+ * @ngdoc function
355
+ * @name pascalprecht.translate.$translateProvider#useSanitizeStrategy
356
+ * @methodOf pascalprecht.translate.$translateProvider
357
+ *
358
+ * @description
359
+ * Simply sets a sanitation strategy type.
360
+ *
361
+ * @param {string} value Strategy type.
362
+ */
363
+ this.useSanitizeValueStrategy = function (value) {
364
+ $interpolationSanitizationStrategy = value;
365
+ return this;
366
+ };
367
+
368
+ /**
369
+ * @ngdoc function
370
+ * @name pascalprecht.translate.$translateProvider#preferredLanguage
371
+ * @methodOf pascalprecht.translate.$translateProvider
372
+ *
373
+ * @description
374
+ * Tells the module which of the registered translation tables to use for translation
375
+ * at initial startup by passing a language key. Similar to `$translateProvider#use`
376
+ * only that it says which language to **prefer**.
377
+ *
378
+ * @param {string} langKey A language key.
379
+ *
380
+ */
381
+ this.preferredLanguage = function(langKey) {
382
+ setupPreferredLanguage(langKey);
383
+ return this;
384
+
385
+ };
386
+ var setupPreferredLanguage = function (langKey) {
387
+ if (langKey) {
388
+ $preferredLanguage = langKey;
389
+ }
390
+ return $preferredLanguage;
391
+ };
392
+ /**
393
+ * @ngdoc function
394
+ * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicator
395
+ * @methodOf pascalprecht.translate.$translateProvider
396
+ *
397
+ * @description
398
+ * Sets an indicator which is used when a translation isn't found. E.g. when
399
+ * setting the indicator as 'X' and one tries to translate a translation id
400
+ * called `NOT_FOUND`, this will result in `X NOT_FOUND X`.
401
+ *
402
+ * Internally this methods sets a left indicator and a right indicator using
403
+ * `$translateProvider.translationNotFoundIndicatorLeft()` and
404
+ * `$translateProvider.translationNotFoundIndicatorRight()`.
405
+ *
406
+ * **Note**: These methods automatically add a whitespace between the indicators
407
+ * and the translation id.
408
+ *
409
+ * @param {string} indicator An indicator, could be any string.
410
+ */
411
+ this.translationNotFoundIndicator = function (indicator) {
412
+ this.translationNotFoundIndicatorLeft(indicator);
413
+ this.translationNotFoundIndicatorRight(indicator);
414
+ return this;
415
+ };
416
+
417
+ /**
418
+ * ngdoc function
419
+ * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft
420
+ * @methodOf pascalprecht.translate.$translateProvider
421
+ *
422
+ * @description
423
+ * Sets an indicator which is used when a translation isn't found left to the
424
+ * translation id.
425
+ *
426
+ * @param {string} indicator An indicator.
427
+ */
428
+ this.translationNotFoundIndicatorLeft = function (indicator) {
429
+ if (!indicator) {
430
+ return $notFoundIndicatorLeft;
431
+ }
432
+ $notFoundIndicatorLeft = indicator;
433
+ return this;
434
+ };
435
+
436
+ /**
437
+ * ngdoc function
438
+ * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft
439
+ * @methodOf pascalprecht.translate.$translateProvider
440
+ *
441
+ * @description
442
+ * Sets an indicator which is used when a translation isn't found right to the
443
+ * translation id.
444
+ *
445
+ * @param {string} indicator An indicator.
446
+ */
447
+ this.translationNotFoundIndicatorRight = function (indicator) {
448
+ if (!indicator) {
449
+ return $notFoundIndicatorRight;
450
+ }
451
+ $notFoundIndicatorRight = indicator;
452
+ return this;
453
+ };
454
+
455
+ /**
456
+ * @ngdoc function
457
+ * @name pascalprecht.translate.$translateProvider#fallbackLanguage
458
+ * @methodOf pascalprecht.translate.$translateProvider
459
+ *
460
+ * @description
461
+ * Tells the module which of the registered translation tables to use when missing translations
462
+ * at initial startup by passing a language key. Similar to `$translateProvider#use`
463
+ * only that it says which language to **fallback**.
464
+ *
465
+ * @param {string||array} langKey A language key.
466
+ *
467
+ */
468
+ this.fallbackLanguage = function (langKey) {
469
+ fallbackStack(langKey);
470
+ return this;
471
+ };
472
+
473
+ var fallbackStack = function (langKey) {
474
+ if (langKey) {
475
+ if (angular.isString(langKey)) {
476
+ $fallbackWasString = true;
477
+ $fallbackLanguage = [ langKey ];
478
+ } else if (angular.isArray(langKey)) {
479
+ $fallbackWasString = false;
480
+ $fallbackLanguage = langKey;
481
+ }
482
+ if (angular.isString($preferredLanguage) && indexOf($fallbackLanguage, $preferredLanguage) < 0) {
483
+ $fallbackLanguage.push($preferredLanguage);
484
+ }
485
+
486
+ return this;
487
+ } else {
488
+ if ($fallbackWasString) {
489
+ return $fallbackLanguage[0];
490
+ } else {
491
+ return $fallbackLanguage;
492
+ }
493
+ }
494
+ };
495
+
496
+ /**
497
+ * @ngdoc function
498
+ * @name pascalprecht.translate.$translateProvider#use
499
+ * @methodOf pascalprecht.translate.$translateProvider
500
+ *
501
+ * @description
502
+ * Set which translation table to use for translation by given language key. When
503
+ * trying to 'use' a language which isn't provided, it'll throw an error.
504
+ *
505
+ * You actually don't have to use this method since `$translateProvider#preferredLanguage`
506
+ * does the job too.
507
+ *
508
+ * @param {string} langKey A language key.
509
+ */
510
+ this.use = function (langKey) {
511
+ if (langKey) {
512
+ if (!$translationTable[langKey] && (!$loaderFactory)) {
513
+ // only throw an error, when not loading translation data asynchronously
514
+ throw new Error("$translateProvider couldn't find translationTable for langKey: '" + langKey + "'");
515
+ }
516
+ $uses = langKey;
517
+ return this;
518
+ }
519
+ return $uses;
520
+ };
521
+
522
+ /**
523
+ * @ngdoc function
524
+ * @name pascalprecht.translate.$translateProvider#storageKey
525
+ * @methodOf pascalprecht.translate.$translateProvider
526
+ *
527
+ * @description
528
+ * Tells the module which key must represent the choosed language by a user in the storage.
529
+ *
530
+ * @param {string} key A key for the storage.
531
+ */
532
+ var storageKey = function(key) {
533
+ if (!key) {
534
+ if ($storagePrefix) {
535
+ return $storagePrefix + $storageKey;
536
+ }
537
+ return $storageKey;
538
+ }
539
+ $storageKey = key;
540
+ };
541
+
542
+ this.storageKey = storageKey;
543
+
544
+ /**
545
+ * @ngdoc function
546
+ * @name pascalprecht.translate.$translateProvider#useUrlLoader
547
+ * @methodOf pascalprecht.translate.$translateProvider
548
+ *
549
+ * @description
550
+ * Tells angular-translate to use `$translateUrlLoader` extension service as loader.
551
+ *
552
+ * @param {string} url Url
553
+ * @param {Object=} options Optional configuration object
554
+ */
555
+ this.useUrlLoader = function (url, options) {
556
+ return this.useLoader('$translateUrlLoader', angular.extend({ url: url }, options));
557
+ };
558
+
559
+ /**
560
+ * @ngdoc function
561
+ * @name pascalprecht.translate.$translateProvider#useStaticFilesLoader
562
+ * @methodOf pascalprecht.translate.$translateProvider
563
+ *
564
+ * @description
565
+ * Tells angular-translate to use `$translateStaticFilesLoader` extension service as loader.
566
+ *
567
+ * @param {Object=} options Optional configuration object
568
+ */
569
+ this.useStaticFilesLoader = function (options) {
570
+ return this.useLoader('$translateStaticFilesLoader', options);
571
+ };
572
+
573
+ /**
574
+ * @ngdoc function
575
+ * @name pascalprecht.translate.$translateProvider#useLoader
576
+ * @methodOf pascalprecht.translate.$translateProvider
577
+ *
578
+ * @description
579
+ * Tells angular-translate to use any other service as loader.
580
+ *
581
+ * @param {string} loaderFactory Factory name to use
582
+ * @param {Object=} options Optional configuration object
583
+ */
584
+ this.useLoader = function (loaderFactory, options) {
585
+ $loaderFactory = loaderFactory;
586
+ $loaderOptions = options || {};
587
+ return this;
588
+ };
589
+
590
+ /**
591
+ * @ngdoc function
592
+ * @name pascalprecht.translate.$translateProvider#useLocalStorage
593
+ * @methodOf pascalprecht.translate.$translateProvider
594
+ *
595
+ * @description
596
+ * Tells angular-translate to use `$translateLocalStorage` service as storage layer.
597
+ *
598
+ */
599
+ this.useLocalStorage = function () {
600
+ return this.useStorage('$translateLocalStorage');
601
+ };
602
+
603
+ /**
604
+ * @ngdoc function
605
+ * @name pascalprecht.translate.$translateProvider#useCookieStorage
606
+ * @methodOf pascalprecht.translate.$translateProvider
607
+ *
608
+ * @description
609
+ * Tells angular-translate to use `$translateCookieStorage` service as storage layer.
610
+ */
611
+ this.useCookieStorage = function () {
612
+ return this.useStorage('$translateCookieStorage');
613
+ };
614
+
615
+ /**
616
+ * @ngdoc function
617
+ * @name pascalprecht.translate.$translateProvider#useStorage
618
+ * @methodOf pascalprecht.translate.$translateProvider
619
+ *
620
+ * @description
621
+ * Tells angular-translate to use custom service as storage layer.
622
+ */
623
+ this.useStorage = function (storageFactory) {
624
+ $storageFactory = storageFactory;
625
+ return this;
626
+ };
627
+
628
+ /**
629
+ * @ngdoc function
630
+ * @name pascalprecht.translate.$translateProvider#storagePrefix
631
+ * @methodOf pascalprecht.translate.$translateProvider
632
+ *
633
+ * @description
634
+ * Sets prefix for storage key.
635
+ *
636
+ * @param {string} prefix Storage key prefix
637
+ */
638
+ this.storagePrefix = function (prefix) {
639
+ if (!prefix) {
640
+ return prefix;
641
+ }
642
+ $storagePrefix = prefix;
643
+ return this;
644
+ };
645
+
646
+ /**
647
+ * @ngdoc function
648
+ * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandlerLog
649
+ * @methodOf pascalprecht.translate.$translateProvider
650
+ *
651
+ * @description
652
+ * Tells angular-translate to use built-in log handler when trying to translate
653
+ * a translation Id which doesn't exist.
654
+ *
655
+ * This is actually a shortcut method for `useMissingTranslationHandler()`.
656
+ *
657
+ */
658
+ this.useMissingTranslationHandlerLog = function () {
659
+ return this.useMissingTranslationHandler('$translateMissingTranslationHandlerLog');
660
+ };
661
+
662
+ /**
663
+ * @ngdoc function
664
+ * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandler
665
+ * @methodOf pascalprecht.translate.$translateProvider
666
+ *
667
+ * @description
668
+ * Expects a factory name which later gets instantiated with `$injector`.
669
+ * This method can be used to tell angular-translate to use a custom
670
+ * missingTranslationHandler. Just build a factory which returns a function
671
+ * and expects a translation id as argument.
672
+ *
673
+ * Example:
674
+ * <pre>
675
+ * app.config(function ($translateProvider) {
676
+ * $translateProvider.useMissingTranslationHandler('customHandler');
677
+ * });
678
+ *
679
+ * app.factory('customHandler', function (dep1, dep2) {
680
+ * return function (translationId) {
681
+ * // something with translationId and dep1 and dep2
682
+ * };
683
+ * });
684
+ * </pre>
685
+ *
686
+ * @param {string} factory Factory name
687
+ */
688
+ this.useMissingTranslationHandler = function (factory) {
689
+ $missingTranslationHandlerFactory = factory;
690
+ return this;
691
+ };
692
+
693
+ /**
694
+ * @ngdoc function
695
+ * @name pascalprecht.translate.$translateProvider#usePostCompiling
696
+ * @methodOf pascalprecht.translate.$translateProvider
697
+ *
698
+ * @description
699
+ * If post compiling is enabled, all translated values will be processed
700
+ * again with AngularJS' $compile.
701
+ *
702
+ * Example:
703
+ * <pre>
704
+ * app.config(function ($translateProvider) {
705
+ * $translateProvider.usePostCompiling(true);
706
+ * });
707
+ * </pre>
708
+ *
709
+ * @param {string} factory Factory name
710
+ */
711
+ this.usePostCompiling = function (value) {
712
+ $postCompilingEnabled = !(!value);
713
+ return this;
714
+ };
715
+
716
+ /**
717
+ * @ngdoc function
718
+ * @name pascalprecht.translate.$translateProvider#determinePreferredLanguage
719
+ * @methodOf pascalprecht.translate.$translateProvider
720
+ *
721
+ * @description
722
+ * Tells angular-translate to try to determine on its own which language key
723
+ * to set as preferred language. When `fn` is given, angular-translate uses it
724
+ * to determine a language key, otherwise it uses the built-in `getLocale()`
725
+ * method.
726
+ *
727
+ * The `getLocale()` returns a language key in the format `[lang]_[country]` or
728
+ * `[lang]` depending on what the browser provides.
729
+ *
730
+ * Use this method at your own risk, since not all browsers return a valid
731
+ * locale.
732
+ *
733
+ * @param {object=} fn Function to determine a browser's locale
734
+ */
735
+ this.determinePreferredLanguage = function (fn) {
736
+
737
+ var locale = (fn && angular.isFunction(fn)) ? fn() : getLocale();
738
+
739
+ if (!$availableLanguageKeys.length) {
740
+ $preferredLanguage = locale;
741
+ } else {
742
+ $preferredLanguage = negotiateLocale(locale);
743
+ }
744
+
745
+ return this;
746
+ };
747
+
748
+ /**
749
+ * @ngdoc function
750
+ * @name pascalprecht.translate.$translateProvider#registerAvailableLanguageKeys
751
+ * @methodOf pascalprecht.translate.$translateProvider
752
+ *
753
+ * @description
754
+ * Registers a set of language keys the app will work with. Use this method in
755
+ * combination with
756
+ * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}.
757
+ * When available languages keys are registered, angular-translate
758
+ * tries to find the best fitting language key depending on the browsers locale,
759
+ * considering your language key convention.
760
+ *
761
+ * @param {object} languageKeys Array of language keys the your app will use
762
+ * @param {object=} aliases Alias map.
763
+ */
764
+ this.registerAvailableLanguageKeys = function (languageKeys, aliases) {
765
+ if (languageKeys) {
766
+ $availableLanguageKeys = languageKeys;
767
+ if (aliases) {
768
+ $languageKeyAliases = aliases;
769
+ }
770
+ return this;
771
+ }
772
+ return $availableLanguageKeys;
773
+ };
774
+
775
+ /**
776
+ * @ngdoc function
777
+ * @name pascalprecht.translate.$translateProvider#useLoaderCache
778
+ * @methodOf pascalprecht.translate.$translateProvider
779
+ *
780
+ * @description
781
+ * Registers a cache for internal $http based loaders.
782
+ * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}.
783
+ * When false the cache will be disabled (default). When true or undefined
784
+ * the cache will be a default (see $cacheFactory). When an object it will
785
+ * be treat as a cache object itself: the usage is $http({cache: cache})
786
+ *
787
+ * @param {object} cache boolean, string or cache-object
788
+ */
789
+ this.useLoaderCache = function (cache) {
790
+ if (cache === false) {
791
+ // disable cache
792
+ loaderCache = undefined;
793
+ } else if (cache === true) {
794
+ // enable cache using AJS defaults
795
+ loaderCache = true;
796
+ } else if (typeof(cache) === 'undefined') {
797
+ // enable cache using default
798
+ loaderCache = '$translationCache';
799
+ } else if (cache) {
800
+ // enable cache using given one (see $cacheFactory)
801
+ loaderCache = cache;
802
+ }
803
+ return this;
804
+ };
805
+
806
+ /**
807
+ * @ngdoc function
808
+ * @name pascalprecht.translate.$translateProvider#directivePriority
809
+ * @methodOf pascalprecht.translate.$translateProvider
810
+ *
811
+ * @description
812
+ * Sets the default priority of the translate directive. The standard value is `0`.
813
+ * Calling this function without an argument will return the current value.
814
+ *
815
+ * @param {number} priority for the translate-directive
816
+ */
817
+ this.directivePriority = function (priority) {
818
+ if (priority === undefined) {
819
+ // getter
820
+ return directivePriority;
821
+ } else {
822
+ // setter with chaining
823
+ directivePriority = priority;
824
+ return this;
825
+ }
826
+ };
827
+
828
+ /**
829
+ * @ngdoc object
830
+ * @name pascalprecht.translate.$translate
831
+ * @requires $interpolate
832
+ * @requires $log
833
+ * @requires $rootScope
834
+ * @requires $q
835
+ *
836
+ * @description
837
+ * The `$translate` service is the actual core of angular-translate. It expects a translation id
838
+ * and optional interpolate parameters to translate contents.
839
+ *
840
+ * <pre>
841
+ * $translate('HEADLINE_TEXT').then(function (translation) {
842
+ * $scope.translatedText = translation;
843
+ * });
844
+ * </pre>
845
+ *
846
+ * @param {string|array} translationId A token which represents a translation id
847
+ * This can be optionally an array of translation ids which
848
+ * results that the function returns an object where each key
849
+ * is the translation id and the value the translation.
850
+ * @param {object=} interpolateParams An object hash for dynamic values
851
+ * @param {string} interpolationId The id of the interpolation to use
852
+ * @returns {object} promise
853
+ */
854
+ this.$get = [
855
+ '$log',
856
+ '$injector',
857
+ '$rootScope',
858
+ '$q',
859
+ function ($log, $injector, $rootScope, $q) {
860
+
861
+ var Storage,
862
+ defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'),
863
+ pendingLoader = false,
864
+ interpolatorHashMap = {},
865
+ langPromises = {},
866
+ fallbackIndex,
867
+ startFallbackIteration;
868
+
869
+ var $translate = function (translationId, interpolateParams, interpolationId, defaultTranslationText) {
870
+
871
+ // Duck detection: If the first argument is an array, a bunch of translations was requested.
872
+ // The result is an object.
873
+ if (angular.isArray(translationId)) {
874
+ // Inspired by Q.allSettled by Kris Kowal
875
+ // https://github.com/kriskowal/q/blob/b0fa72980717dc202ffc3cbf03b936e10ebbb9d7/q.js#L1553-1563
876
+ // This transforms all promises regardless resolved or rejected
877
+ var translateAll = function (translationIds) {
878
+ var results = {}; // storing the actual results
879
+ var promises = []; // promises to wait for
880
+ // Wraps the promise a) being always resolved and b) storing the link id->value
881
+ var translate = function (translationId) {
882
+ var deferred = $q.defer();
883
+ var regardless = function (value) {
884
+ results[translationId] = value;
885
+ deferred.resolve([translationId, value]);
886
+ };
887
+ // we don't care whether the promise was resolved or rejected; just store the values
888
+ $translate(translationId, interpolateParams, interpolationId, defaultTranslationText).then(regardless, regardless);
889
+ return deferred.promise;
890
+ };
891
+ for (var i = 0, c = translationIds.length; i < c; i++) {
892
+ promises.push(translate(translationIds[i]));
893
+ }
894
+ // wait for all (including storing to results)
895
+ return $q.all(promises).then(function () {
896
+ // return the results
897
+ return results;
898
+ });
899
+ };
900
+ return translateAll(translationId);
901
+ }
902
+
903
+ var deferred = $q.defer();
904
+
905
+ // trim off any whitespace
906
+ if (translationId) {
907
+ translationId = trim.apply(translationId);
908
+ }
909
+
910
+ var promiseToWaitFor = (function () {
911
+ var promise = $preferredLanguage ?
912
+ langPromises[$preferredLanguage] :
913
+ langPromises[$uses];
914
+
915
+ fallbackIndex = 0;
916
+
917
+ if ($storageFactory && !promise) {
918
+ // looks like there's no pending promise for $preferredLanguage or
919
+ // $uses. Maybe there's one pending for a language that comes from
920
+ // storage.
921
+ var langKey = Storage.get($storageKey);
922
+ promise = langPromises[langKey];
923
+
924
+ if ($fallbackLanguage && $fallbackLanguage.length) {
925
+ var index = indexOf($fallbackLanguage, langKey);
926
+ // maybe the language from storage is also defined as fallback language
927
+ // we increase the fallback language index to not search in that language
928
+ // as fallback, since it's probably the first used language
929
+ // in that case the index starts after the first element
930
+ fallbackIndex = (index === 0) ? 1 : 0;
931
+
932
+ // but we can make sure to ALWAYS fallback to preferred language at least
933
+ if (indexOf($fallbackLanguage, $preferredLanguage) < 0) {
934
+ $fallbackLanguage.push($preferredLanguage);
935
+ }
936
+ }
937
+ }
938
+ return promise;
939
+ }());
940
+
941
+ if (!promiseToWaitFor) {
942
+ // no promise to wait for? okay. Then there's no loader registered
943
+ // nor is a one pending for language that comes from storage.
944
+ // We can just translate.
945
+ determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText).then(deferred.resolve, deferred.reject);
946
+ } else {
947
+ promiseToWaitFor.then(function () {
948
+ determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText).then(deferred.resolve, deferred.reject);
949
+ }, deferred.reject);
950
+ }
951
+ return deferred.promise;
952
+ };
953
+
954
+ /**
955
+ * @name applyNotFoundIndicators
956
+ * @private
957
+ *
958
+ * @description
959
+ * Applies not fount indicators to given translation id, if needed.
960
+ * This function gets only executed, if a translation id doesn't exist,
961
+ * which is why a translation id is expected as argument.
962
+ *
963
+ * @param {string} translationId Translation id.
964
+ * @returns {string} Same as given translation id but applied with not found
965
+ * indicators.
966
+ */
967
+ var applyNotFoundIndicators = function (translationId) {
968
+ // applying notFoundIndicators
969
+ if ($notFoundIndicatorLeft) {
970
+ translationId = [$notFoundIndicatorLeft, translationId].join(' ');
971
+ }
972
+ if ($notFoundIndicatorRight) {
973
+ translationId = [translationId, $notFoundIndicatorRight].join(' ');
974
+ }
975
+ return translationId;
976
+ };
977
+
978
+ /**
979
+ * @name useLanguage
980
+ * @private
981
+ *
982
+ * @description
983
+ * Makes actual use of a language by setting a given language key as used
984
+ * language and informs registered interpolators to also use the given
985
+ * key as locale.
986
+ *
987
+ * @param {key} Locale key.
988
+ */
989
+ var useLanguage = function (key) {
990
+ $uses = key;
991
+ $rootScope.$emit('$translateChangeSuccess', {language: key});
992
+
993
+ if ($storageFactory) {
994
+ Storage.put($translate.storageKey(), $uses);
995
+ }
996
+ // inform default interpolator
997
+ defaultInterpolator.setLocale($uses);
998
+ // inform all others too!
999
+ angular.forEach(interpolatorHashMap, function (interpolator, id) {
1000
+ interpolatorHashMap[id].setLocale($uses);
1001
+ });
1002
+ $rootScope.$emit('$translateChangeEnd', {language: key});
1003
+ };
1004
+
1005
+ /**
1006
+ * @name loadAsync
1007
+ * @private
1008
+ *
1009
+ * @description
1010
+ * Kicks of registered async loader using `$injector` and applies existing
1011
+ * loader options. When resolved, it updates translation tables accordingly
1012
+ * or rejects with given language key.
1013
+ *
1014
+ * @param {string} key Language key.
1015
+ * @return {Promise} A promise.
1016
+ */
1017
+ var loadAsync = function (key) {
1018
+ if (!key) {
1019
+ throw 'No language key specified for loading.';
1020
+ }
1021
+
1022
+ var deferred = $q.defer();
1023
+
1024
+ $rootScope.$emit('$translateLoadingStart', {language: key});
1025
+ pendingLoader = true;
1026
+
1027
+ var cache = loaderCache;
1028
+ if (typeof(cache) === 'string') {
1029
+ // getting on-demand instance of loader
1030
+ cache = $injector.get(cache);
1031
+ }
1032
+
1033
+ var loaderOptions = angular.extend({}, $loaderOptions, {
1034
+ key: key,
1035
+ $http: angular.extend({}, {
1036
+ cache: cache
1037
+ }, $loaderOptions.$http)
1038
+ });
1039
+
1040
+ $injector.get($loaderFactory)(loaderOptions).then(function (data) {
1041
+ var translationTable = {};
1042
+ $rootScope.$emit('$translateLoadingSuccess', {language: key});
1043
+
1044
+ if (angular.isArray(data)) {
1045
+ angular.forEach(data, function (table) {
1046
+ angular.extend(translationTable, flatObject(table));
1047
+ });
1048
+ } else {
1049
+ angular.extend(translationTable, flatObject(data));
1050
+ }
1051
+ pendingLoader = false;
1052
+ deferred.resolve({
1053
+ key: key,
1054
+ table: translationTable
1055
+ });
1056
+ $rootScope.$emit('$translateLoadingEnd', {language: key});
1057
+ }, function (key) {
1058
+ $rootScope.$emit('$translateLoadingError', {language: key});
1059
+ deferred.reject(key);
1060
+ $rootScope.$emit('$translateLoadingEnd', {language: key});
1061
+ });
1062
+ return deferred.promise;
1063
+ };
1064
+
1065
+ if ($storageFactory) {
1066
+ Storage = $injector.get($storageFactory);
1067
+
1068
+ if (!Storage.get || !Storage.put) {
1069
+ throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or put() method!');
1070
+ }
1071
+ }
1072
+
1073
+ // apply additional settings
1074
+ if (angular.isFunction(defaultInterpolator.useSanitizeValueStrategy)) {
1075
+ defaultInterpolator.useSanitizeValueStrategy($interpolationSanitizationStrategy);
1076
+ }
1077
+
1078
+ // if we have additional interpolations that were added via
1079
+ // $translateProvider.addInterpolation(), we have to map'em
1080
+ if ($interpolatorFactories.length) {
1081
+ angular.forEach($interpolatorFactories, function (interpolatorFactory) {
1082
+ var interpolator = $injector.get(interpolatorFactory);
1083
+ // setting initial locale for each interpolation service
1084
+ interpolator.setLocale($preferredLanguage || $uses);
1085
+ // apply additional settings
1086
+ if (angular.isFunction(interpolator.useSanitizeValueStrategy)) {
1087
+ interpolator.useSanitizeValueStrategy($interpolationSanitizationStrategy);
1088
+ }
1089
+ // make'em recognizable through id
1090
+ interpolatorHashMap[interpolator.getInterpolationIdentifier()] = interpolator;
1091
+ });
1092
+ }
1093
+
1094
+ /**
1095
+ * @name getTranslationTable
1096
+ * @private
1097
+ *
1098
+ * @description
1099
+ * Returns a promise that resolves to the translation table
1100
+ * or is rejected if an error occurred.
1101
+ *
1102
+ * @param langKey
1103
+ * @returns {Q.promise}
1104
+ */
1105
+ var getTranslationTable = function (langKey) {
1106
+ var deferred = $q.defer();
1107
+ if (Object.prototype.hasOwnProperty.call($translationTable, langKey)) {
1108
+ deferred.resolve($translationTable[langKey]);
1109
+ } else if (langPromises[langKey]) {
1110
+ langPromises[langKey].then(function (data) {
1111
+ translations(data.key, data.table);
1112
+ deferred.resolve(data.table);
1113
+ }, deferred.reject);
1114
+ } else {
1115
+ deferred.reject();
1116
+ }
1117
+ return deferred.promise;
1118
+ };
1119
+
1120
+ /**
1121
+ * @name getFallbackTranslation
1122
+ * @private
1123
+ *
1124
+ * @description
1125
+ * Returns a promise that will resolve to the translation
1126
+ * or be rejected if no translation was found for the language.
1127
+ * This function is currently only used for fallback language translation.
1128
+ *
1129
+ * @param langKey The language to translate to.
1130
+ * @param translationId
1131
+ * @param interpolateParams
1132
+ * @param Interpolator
1133
+ * @returns {Q.promise}
1134
+ */
1135
+ var getFallbackTranslation = function (langKey, translationId, interpolateParams, Interpolator) {
1136
+ var deferred = $q.defer();
1137
+
1138
+ getTranslationTable(langKey).then(function (translationTable) {
1139
+ if (Object.prototype.hasOwnProperty.call(translationTable, translationId)) {
1140
+ Interpolator.setLocale(langKey);
1141
+ var translation = translationTable[translationId];
1142
+ if (translation.substr(0, 2) === '@:') {
1143
+ getFallbackTranslation(langKey, translation.substr(2), interpolateParams, Interpolator)
1144
+ .then(deferred.resolve, deferred.reject);
1145
+ } else {
1146
+ deferred.resolve(Interpolator.interpolate(translationTable[translationId], interpolateParams));
1147
+ }
1148
+ Interpolator.setLocale($uses);
1149
+ } else {
1150
+ deferred.reject();
1151
+ }
1152
+ }, deferred.reject);
1153
+
1154
+ return deferred.promise;
1155
+ };
1156
+
1157
+ /**
1158
+ * @name getFallbackTranslationInstant
1159
+ * @private
1160
+ *
1161
+ * @description
1162
+ * Returns a translation
1163
+ * This function is currently only used for fallback language translation.
1164
+ *
1165
+ * @param langKey The language to translate to.
1166
+ * @param translationId
1167
+ * @param interpolateParams
1168
+ * @param Interpolator
1169
+ * @returns {string} translation
1170
+ */
1171
+ var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator) {
1172
+ var result, translationTable = $translationTable[langKey];
1173
+
1174
+ if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId)) {
1175
+ Interpolator.setLocale(langKey);
1176
+ result = Interpolator.interpolate(translationTable[translationId], interpolateParams);
1177
+ if (result.substr(0, 2) === '@:') {
1178
+ return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator);
1179
+ }
1180
+ Interpolator.setLocale($uses);
1181
+ }
1182
+
1183
+ return result;
1184
+ };
1185
+
1186
+
1187
+ /**
1188
+ * @name translateByHandler
1189
+ * @private
1190
+ *
1191
+ * Translate by missing translation handler.
1192
+ *
1193
+ * @param translationId
1194
+ * @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is
1195
+ * absent
1196
+ */
1197
+ var translateByHandler = function (translationId) {
1198
+ // If we have a handler factory - we might also call it here to determine if it provides
1199
+ // a default text for a translationid that can't be found anywhere in our tables
1200
+ if ($missingTranslationHandlerFactory) {
1201
+ var resultString = $injector.get($missingTranslationHandlerFactory)(translationId, $uses);
1202
+ if (resultString !== undefined) {
1203
+ return resultString;
1204
+ } else {
1205
+ return translationId;
1206
+ }
1207
+ } else {
1208
+ return translationId;
1209
+ }
1210
+ };
1211
+
1212
+ /**
1213
+ * @name resolveForFallbackLanguage
1214
+ * @private
1215
+ *
1216
+ * Recursive helper function for fallbackTranslation that will sequentially look
1217
+ * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
1218
+ *
1219
+ * @param fallbackLanguageIndex
1220
+ * @param translationId
1221
+ * @param interpolateParams
1222
+ * @param Interpolator
1223
+ * @returns {Q.promise} Promise that will resolve to the translation.
1224
+ */
1225
+ var resolveForFallbackLanguage = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, defaultTranslationText) {
1226
+ var deferred = $q.defer();
1227
+
1228
+ if (fallbackLanguageIndex < $fallbackLanguage.length) {
1229
+ var langKey = $fallbackLanguage[fallbackLanguageIndex];
1230
+ getFallbackTranslation(langKey, translationId, interpolateParams, Interpolator).then(
1231
+ deferred.resolve,
1232
+ function () {
1233
+ // Look in the next fallback language for a translation.
1234
+ // It delays the resolving by passing another promise to resolve.
1235
+ resolveForFallbackLanguage(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator, defaultTranslationText).then(deferred.resolve);
1236
+ }
1237
+ );
1238
+ } else {
1239
+ // No translation found in any fallback language
1240
+ // if a default translation text is set in the directive, then return this as a result
1241
+ if (defaultTranslationText) {
1242
+ deferred.resolve(defaultTranslationText);
1243
+ } else {
1244
+ // if no default translation is set and an error handler is defined, send it to the handler
1245
+ // and then return the result
1246
+ deferred.resolve(translateByHandler(translationId));
1247
+ }
1248
+ }
1249
+ return deferred.promise;
1250
+ };
1251
+
1252
+ /**
1253
+ * @name resolveForFallbackLanguageInstant
1254
+ * @private
1255
+ *
1256
+ * Recursive helper function for fallbackTranslation that will sequentially look
1257
+ * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
1258
+ *
1259
+ * @param fallbackLanguageIndex
1260
+ * @param translationId
1261
+ * @param interpolateParams
1262
+ * @param Interpolator
1263
+ * @returns {string} translation
1264
+ */
1265
+ var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator) {
1266
+ var result;
1267
+
1268
+ if (fallbackLanguageIndex < $fallbackLanguage.length) {
1269
+ var langKey = $fallbackLanguage[fallbackLanguageIndex];
1270
+ result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator);
1271
+ if (!result) {
1272
+ result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator);
1273
+ }
1274
+ }
1275
+ return result;
1276
+ };
1277
+
1278
+ /**
1279
+ * Translates with the usage of the fallback languages.
1280
+ *
1281
+ * @param translationId
1282
+ * @param interpolateParams
1283
+ * @param Interpolator
1284
+ * @returns {Q.promise} Promise, that resolves to the translation.
1285
+ */
1286
+ var fallbackTranslation = function (translationId, interpolateParams, Interpolator, defaultTranslationText) {
1287
+ // Start with the fallbackLanguage with index 0
1288
+ return resolveForFallbackLanguage((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, defaultTranslationText);
1289
+ };
1290
+
1291
+ /**
1292
+ * Translates with the usage of the fallback languages.
1293
+ *
1294
+ * @param translationId
1295
+ * @param interpolateParams
1296
+ * @param Interpolator
1297
+ * @returns {String} translation
1298
+ */
1299
+ var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator) {
1300
+ // Start with the fallbackLanguage with index 0
1301
+ return resolveForFallbackLanguageInstant((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator);
1302
+ };
1303
+
1304
+ var determineTranslation = function (translationId, interpolateParams, interpolationId, defaultTranslationText) {
1305
+
1306
+ var deferred = $q.defer();
1307
+
1308
+ var table = $uses ? $translationTable[$uses] : $translationTable,
1309
+ Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator;
1310
+
1311
+ // if the translation id exists, we can just interpolate it
1312
+ if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {
1313
+ var translation = table[translationId];
1314
+
1315
+ // If using link, rerun $translate with linked translationId and return it
1316
+ if (translation.substr(0, 2) === '@:') {
1317
+
1318
+ $translate(translation.substr(2), interpolateParams, interpolationId, defaultTranslationText)
1319
+ .then(deferred.resolve, deferred.reject);
1320
+ } else {
1321
+ deferred.resolve(Interpolator.interpolate(translation, interpolateParams));
1322
+ }
1323
+ } else {
1324
+ var missingTranslationHandlerTranslation;
1325
+ // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
1326
+ if ($missingTranslationHandlerFactory && !pendingLoader) {
1327
+ missingTranslationHandlerTranslation = translateByHandler(translationId);
1328
+ }
1329
+
1330
+ // since we couldn't translate the inital requested translation id,
1331
+ // we try it now with one or more fallback languages, if fallback language(s) is
1332
+ // configured.
1333
+ if ($uses && $fallbackLanguage && $fallbackLanguage.length) {
1334
+ fallbackTranslation(translationId, interpolateParams, Interpolator, defaultTranslationText)
1335
+ .then(function (translation) {
1336
+ deferred.resolve(translation);
1337
+ }, function (_translationId) {
1338
+ deferred.reject(applyNotFoundIndicators(_translationId));
1339
+ });
1340
+ } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
1341
+ // looks like the requested translation id doesn't exists.
1342
+ // Now, if there is a registered handler for missing translations and no
1343
+ // asyncLoader is pending, we execute the handler
1344
+ if (defaultTranslationText) {
1345
+ deferred.resolve(defaultTranslationText);
1346
+ } else {
1347
+ deferred.resolve(missingTranslationHandlerTranslation);
1348
+ }
1349
+ } else {
1350
+ if (defaultTranslationText) {
1351
+ deferred.resolve(defaultTranslationText);
1352
+ } else {
1353
+ deferred.reject(applyNotFoundIndicators(translationId));
1354
+ }
1355
+ }
1356
+ }
1357
+ return deferred.promise;
1358
+ };
1359
+
1360
+ var determineTranslationInstant = function (translationId, interpolateParams, interpolationId) {
1361
+
1362
+ var result, table = $uses ? $translationTable[$uses] : $translationTable,
1363
+ Interpolator = defaultInterpolator;
1364
+
1365
+ // if the interpolation id exists use custom interpolator
1366
+ if (interpolatorHashMap && Object.prototype.hasOwnProperty.call(interpolatorHashMap, interpolationId)) {
1367
+ Interpolator = interpolatorHashMap[interpolationId];
1368
+ }
1369
+
1370
+ // if the translation id exists, we can just interpolate it
1371
+ if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {
1372
+ var translation = table[translationId];
1373
+
1374
+ // If using link, rerun $translate with linked translationId and return it
1375
+ if (translation.substr(0, 2) === '@:') {
1376
+ result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId);
1377
+ } else {
1378
+ result = Interpolator.interpolate(translation, interpolateParams);
1379
+ }
1380
+ } else {
1381
+ var missingTranslationHandlerTranslation;
1382
+ // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
1383
+ if ($missingTranslationHandlerFactory && !pendingLoader) {
1384
+ missingTranslationHandlerTranslation = translateByHandler(translationId);
1385
+ }
1386
+
1387
+ // since we couldn't translate the inital requested translation id,
1388
+ // we try it now with one or more fallback languages, if fallback language(s) is
1389
+ // configured.
1390
+ if ($uses && $fallbackLanguage && $fallbackLanguage.length) {
1391
+ fallbackIndex = 0;
1392
+ result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator);
1393
+ } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
1394
+ // looks like the requested translation id doesn't exists.
1395
+ // Now, if there is a registered handler for missing translations and no
1396
+ // asyncLoader is pending, we execute the handler
1397
+ result = missingTranslationHandlerTranslation;
1398
+ } else {
1399
+ result = applyNotFoundIndicators(translationId);
1400
+ }
1401
+ }
1402
+
1403
+ return result;
1404
+ };
1405
+
1406
+ /**
1407
+ * @ngdoc function
1408
+ * @name pascalprecht.translate.$translate#preferredLanguage
1409
+ * @methodOf pascalprecht.translate.$translate
1410
+ *
1411
+ * @description
1412
+ * Returns the language key for the preferred language.
1413
+ *
1414
+ * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime)
1415
+ *
1416
+ * @return {string} preferred language key
1417
+ */
1418
+ $translate.preferredLanguage = function (langKey) {
1419
+ if(langKey) {
1420
+ setupPreferredLanguage(langKey);
1421
+ }
1422
+ return $preferredLanguage;
1423
+ };
1424
+
1425
+ /**
1426
+ * @ngdoc function
1427
+ * @name pascalprecht.translate.$translate#cloakClassName
1428
+ * @methodOf pascalprecht.translate.$translate
1429
+ *
1430
+ * @description
1431
+ * Returns the configured class name for `translate-cloak` directive.
1432
+ *
1433
+ * @return {string} cloakClassName
1434
+ */
1435
+ $translate.cloakClassName = function () {
1436
+ return $cloakClassName;
1437
+ };
1438
+
1439
+ /**
1440
+ * @ngdoc function
1441
+ * @name pascalprecht.translate.$translate#fallbackLanguage
1442
+ * @methodOf pascalprecht.translate.$translate
1443
+ *
1444
+ * @description
1445
+ * Returns the language key for the fallback languages or sets a new fallback stack.
1446
+ *
1447
+ * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime)
1448
+ *
1449
+ * @return {string||array} fallback language key
1450
+ */
1451
+ $translate.fallbackLanguage = function (langKey) {
1452
+ if (langKey !== undefined && langKey !== null) {
1453
+ fallbackStack(langKey);
1454
+
1455
+ // as we might have an async loader initiated and a new translation language might have been defined
1456
+ // we need to add the promise to the stack also. So - iterate.
1457
+ if ($loaderFactory) {
1458
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1459
+ for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
1460
+ if (!langPromises[$fallbackLanguage[i]]) {
1461
+ langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]);
1462
+ }
1463
+ }
1464
+ }
1465
+ }
1466
+ $translate.use($translate.use());
1467
+ }
1468
+ if ($fallbackWasString) {
1469
+ return $fallbackLanguage[0];
1470
+ } else {
1471
+ return $fallbackLanguage;
1472
+ }
1473
+
1474
+ };
1475
+
1476
+ /**
1477
+ * @ngdoc function
1478
+ * @name pascalprecht.translate.$translate#useFallbackLanguage
1479
+ * @methodOf pascalprecht.translate.$translate
1480
+ *
1481
+ * @description
1482
+ * Sets the first key of the fallback language stack to be used for translation.
1483
+ * Therefore all languages in the fallback array BEFORE this key will be skipped!
1484
+ *
1485
+ * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to
1486
+ * get back to the whole stack
1487
+ */
1488
+ $translate.useFallbackLanguage = function (langKey) {
1489
+ if (langKey !== undefined && langKey !== null) {
1490
+ if (!langKey) {
1491
+ startFallbackIteration = 0;
1492
+ } else {
1493
+ var langKeyPosition = indexOf($fallbackLanguage, langKey);
1494
+ if (langKeyPosition > -1) {
1495
+ startFallbackIteration = langKeyPosition;
1496
+ }
1497
+ }
1498
+
1499
+ }
1500
+
1501
+ };
1502
+
1503
+ /**
1504
+ * @ngdoc function
1505
+ * @name pascalprecht.translate.$translate#proposedLanguage
1506
+ * @methodOf pascalprecht.translate.$translate
1507
+ *
1508
+ * @description
1509
+ * Returns the language key of language that is currently loaded asynchronously.
1510
+ *
1511
+ * @return {string} language key
1512
+ */
1513
+ $translate.proposedLanguage = function () {
1514
+ return $nextLang;
1515
+ };
1516
+
1517
+ /**
1518
+ * @ngdoc function
1519
+ * @name pascalprecht.translate.$translate#storage
1520
+ * @methodOf pascalprecht.translate.$translate
1521
+ *
1522
+ * @description
1523
+ * Returns registered storage.
1524
+ *
1525
+ * @return {object} Storage
1526
+ */
1527
+ $translate.storage = function () {
1528
+ return Storage;
1529
+ };
1530
+
1531
+ /**
1532
+ * @ngdoc function
1533
+ * @name pascalprecht.translate.$translate#use
1534
+ * @methodOf pascalprecht.translate.$translate
1535
+ *
1536
+ * @description
1537
+ * Tells angular-translate which language to use by given language key. This method is
1538
+ * used to change language at runtime. It also takes care of storing the language
1539
+ * key in a configured store to let your app remember the choosed language.
1540
+ *
1541
+ * When trying to 'use' a language which isn't available it tries to load it
1542
+ * asynchronously with registered loaders.
1543
+ *
1544
+ * Returns promise object with loaded language file data
1545
+ * @example
1546
+ * $translate.use("en_US").then(function(data){
1547
+ * $scope.text = $translate("HELLO");
1548
+ * });
1549
+ *
1550
+ * @param {string} key Language key
1551
+ * @return {string} Language key
1552
+ */
1553
+ $translate.use = function (key) {
1554
+ if (!key) {
1555
+ return $uses;
1556
+ }
1557
+
1558
+ var deferred = $q.defer();
1559
+
1560
+ $rootScope.$emit('$translateChangeStart', {language: key});
1561
+
1562
+ // Try to get the aliased language key
1563
+ var aliasedKey = negotiateLocale(key);
1564
+ if (aliasedKey) {
1565
+ key = aliasedKey;
1566
+ }
1567
+
1568
+ // if there isn't a translation table for the language we've requested,
1569
+ // we load it asynchronously
1570
+ if (!$translationTable[key] && $loaderFactory && !langPromises[key]) {
1571
+ $nextLang = key;
1572
+ langPromises[key] = loadAsync(key).then(function (translation) {
1573
+ translations(translation.key, translation.table);
1574
+ deferred.resolve(translation.key);
1575
+
1576
+ useLanguage(translation.key);
1577
+ if ($nextLang === key) {
1578
+ $nextLang = undefined;
1579
+ }
1580
+ return translation;
1581
+ }, function (key) {
1582
+ if ($nextLang === key) {
1583
+ $nextLang = undefined;
1584
+ }
1585
+ $rootScope.$emit('$translateChangeError', {language: key});
1586
+ deferred.reject(key);
1587
+ $rootScope.$emit('$translateChangeEnd', {language: key});
1588
+ });
1589
+ } else {
1590
+ deferred.resolve(key);
1591
+ useLanguage(key);
1592
+ }
1593
+
1594
+ return deferred.promise;
1595
+ };
1596
+
1597
+ /**
1598
+ * @ngdoc function
1599
+ * @name pascalprecht.translate.$translate#storageKey
1600
+ * @methodOf pascalprecht.translate.$translate
1601
+ *
1602
+ * @description
1603
+ * Returns the key for the storage.
1604
+ *
1605
+ * @return {string} storage key
1606
+ */
1607
+ $translate.storageKey = function () {
1608
+ return storageKey();
1609
+ };
1610
+
1611
+ /**
1612
+ * @ngdoc function
1613
+ * @name pascalprecht.translate.$translate#isPostCompilingEnabled
1614
+ * @methodOf pascalprecht.translate.$translate
1615
+ *
1616
+ * @description
1617
+ * Returns whether post compiling is enabled or not
1618
+ *
1619
+ * @return {bool} storage key
1620
+ */
1621
+ $translate.isPostCompilingEnabled = function () {
1622
+ return $postCompilingEnabled;
1623
+ };
1624
+
1625
+ /**
1626
+ * @ngdoc function
1627
+ * @name pascalprecht.translate.$translate#refresh
1628
+ * @methodOf pascalprecht.translate.$translate
1629
+ *
1630
+ * @description
1631
+ * Refreshes a translation table pointed by the given langKey. If langKey is not specified,
1632
+ * the module will drop all existent translation tables and load new version of those which
1633
+ * are currently in use.
1634
+ *
1635
+ * Refresh means that the module will drop target translation table and try to load it again.
1636
+ *
1637
+ * In case there are no loaders registered the refresh() method will throw an Error.
1638
+ *
1639
+ * If the module is able to refresh translation tables refresh() method will broadcast
1640
+ * $translateRefreshStart and $translateRefreshEnd events.
1641
+ *
1642
+ * @example
1643
+ * // this will drop all currently existent translation tables and reload those which are
1644
+ * // currently in use
1645
+ * $translate.refresh();
1646
+ * // this will refresh a translation table for the en_US language
1647
+ * $translate.refresh('en_US');
1648
+ *
1649
+ * @param {string} langKey A language key of the table, which has to be refreshed
1650
+ *
1651
+ * @return {promise} Promise, which will be resolved in case a translation tables refreshing
1652
+ * process is finished successfully, and reject if not.
1653
+ */
1654
+ $translate.refresh = function (langKey) {
1655
+ if (!$loaderFactory) {
1656
+ throw new Error('Couldn\'t refresh translation table, no loader registered!');
1657
+ }
1658
+
1659
+ var deferred = $q.defer();
1660
+
1661
+ function resolve() {
1662
+ deferred.resolve();
1663
+ $rootScope.$emit('$translateRefreshEnd', {language: langKey});
1664
+ }
1665
+
1666
+ function reject() {
1667
+ deferred.reject();
1668
+ $rootScope.$emit('$translateRefreshEnd', {language: langKey});
1669
+ }
1670
+
1671
+ $rootScope.$emit('$translateRefreshStart', {language: langKey});
1672
+
1673
+ if (!langKey) {
1674
+ // if there's no language key specified we refresh ALL THE THINGS!
1675
+ var tables = [], loadingKeys = {};
1676
+
1677
+ // reload registered fallback languages
1678
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1679
+ for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
1680
+ tables.push(loadAsync($fallbackLanguage[i]));
1681
+ loadingKeys[$fallbackLanguage[i]] = true;
1682
+ }
1683
+ }
1684
+
1685
+ // reload currently used language
1686
+ if ($uses && !loadingKeys[$uses]) {
1687
+ tables.push(loadAsync($uses));
1688
+ }
1689
+
1690
+ $q.all(tables).then(function (tableData) {
1691
+ angular.forEach(tableData, function (data) {
1692
+ if ($translationTable[data.key]) {
1693
+ delete $translationTable[data.key];
1694
+ }
1695
+ translations(data.key, data.table);
1696
+ });
1697
+ if ($uses) {
1698
+ useLanguage($uses);
1699
+ }
1700
+ resolve();
1701
+ });
1702
+
1703
+ } else if ($translationTable[langKey]) {
1704
+
1705
+ loadAsync(langKey).then(function (data) {
1706
+ translations(data.key, data.table);
1707
+ if (langKey === $uses) {
1708
+ useLanguage($uses);
1709
+ }
1710
+ resolve();
1711
+ }, reject);
1712
+
1713
+ } else {
1714
+ reject();
1715
+ }
1716
+ return deferred.promise;
1717
+ };
1718
+
1719
+ /**
1720
+ * @ngdoc function
1721
+ * @name pascalprecht.translate.$translate#instant
1722
+ * @methodOf pascalprecht.translate.$translate
1723
+ *
1724
+ * @description
1725
+ * Returns a translation instantly from the internal state of loaded translation. All rules
1726
+ * regarding the current language, the preferred language of even fallback languages will be
1727
+ * used except any promise handling. If a language was not found, an asynchronous loading
1728
+ * will be invoked in the background.
1729
+ *
1730
+ * @param {string|array} translationId A token which represents a translation id
1731
+ * This can be optionally an array of translation ids which
1732
+ * results that the function's promise returns an object where
1733
+ * each key is the translation id and the value the translation.
1734
+ * @param {object} interpolateParams Params
1735
+ * @param {string} interpolationId The id of the interpolation to use
1736
+ *
1737
+ * @return {string} translation
1738
+ */
1739
+ $translate.instant = function (translationId, interpolateParams, interpolationId) {
1740
+
1741
+ // Detect undefined and null values to shorten the execution and prevent exceptions
1742
+ if (translationId === null || angular.isUndefined(translationId)) {
1743
+ return translationId;
1744
+ }
1745
+
1746
+ // Duck detection: If the first argument is an array, a bunch of translations was requested.
1747
+ // The result is an object.
1748
+ if (angular.isArray(translationId)) {
1749
+ var results = {};
1750
+ for (var i = 0, c = translationId.length; i < c; i++) {
1751
+ results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId);
1752
+ }
1753
+ return results;
1754
+ }
1755
+
1756
+ // We discarded unacceptable values. So we just need to verify if translationId is empty String
1757
+ if (angular.isString(translationId) && translationId.length < 1) {
1758
+ return translationId;
1759
+ }
1760
+
1761
+ // trim off any whitespace
1762
+ if (translationId) {
1763
+ translationId = trim.apply(translationId);
1764
+ }
1765
+
1766
+ var result, possibleLangKeys = [];
1767
+ if ($preferredLanguage) {
1768
+ possibleLangKeys.push($preferredLanguage);
1769
+ }
1770
+ if ($uses) {
1771
+ possibleLangKeys.push($uses);
1772
+ }
1773
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1774
+ possibleLangKeys = possibleLangKeys.concat($fallbackLanguage);
1775
+ }
1776
+ for (var j = 0, d = possibleLangKeys.length; j < d; j++) {
1777
+ var possibleLangKey = possibleLangKeys[j];
1778
+ if ($translationTable[possibleLangKey]) {
1779
+ if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') {
1780
+ result = determineTranslationInstant(translationId, interpolateParams, interpolationId);
1781
+ } else if ($notFoundIndicatorLeft || $notFoundIndicatorRight) {
1782
+ result = applyNotFoundIndicators(translationId);
1783
+ }
1784
+ }
1785
+ if (typeof result !== 'undefined') {
1786
+ break;
1787
+ }
1788
+ }
1789
+
1790
+ if (!result && result !== '') {
1791
+ // Return translation of default interpolator if not found anything.
1792
+ result = defaultInterpolator.interpolate(translationId, interpolateParams);
1793
+ if ($missingTranslationHandlerFactory && !pendingLoader) {
1794
+ result = translateByHandler(translationId);
1795
+ }
1796
+ }
1797
+
1798
+ return result;
1799
+ };
1800
+
1801
+ /**
1802
+ * @ngdoc function
1803
+ * @name pascalprecht.translate.$translate#versionInfo
1804
+ * @methodOf pascalprecht.translate.$translate
1805
+ *
1806
+ * @description
1807
+ * Returns the current version information for the angular-translate library
1808
+ *
1809
+ * @return {string} angular-translate version
1810
+ */
1811
+ $translate.versionInfo = function () {
1812
+ return version;
1813
+ };
1814
+
1815
+ /**
1816
+ * @ngdoc function
1817
+ * @name pascalprecht.translate.$translate#loaderCache
1818
+ * @methodOf pascalprecht.translate.$translate
1819
+ *
1820
+ * @description
1821
+ * Returns the defined loaderCache.
1822
+ *
1823
+ * @return {boolean|string|object} current value of loaderCache
1824
+ */
1825
+ $translate.loaderCache = function () {
1826
+ return loaderCache;
1827
+ };
1828
+
1829
+ // internal purpose only
1830
+ $translate.directivePriority = function () {
1831
+ return directivePriority;
1832
+ };
1833
+
1834
+ if ($loaderFactory) {
1835
+
1836
+ // If at least one async loader is defined and there are no
1837
+ // (default) translations available we should try to load them.
1838
+ if (angular.equals($translationTable, {})) {
1839
+ $translate.use($translate.use());
1840
+ }
1841
+
1842
+ // Also, if there are any fallback language registered, we start
1843
+ // loading them asynchronously as soon as we can.
1844
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1845
+ var processAsyncResult = function (translation) {
1846
+ translations(translation.key, translation.table);
1847
+ $rootScope.$emit('$translateChangeEnd', { language: translation.key });
1848
+ return translation;
1849
+ };
1850
+ for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
1851
+ langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]).then(processAsyncResult);
1852
+ }
1853
+ }
1854
+ }
1855
+
1856
+ return $translate;
1857
+ }
1858
+ ];
1859
+ }]);
1860
+
1861
+ /**
1862
+ * @ngdoc object
1863
+ * @name pascalprecht.translate.$translateDefaultInterpolation
1864
+ * @requires $interpolate
1865
+ *
1866
+ * @description
1867
+ * Uses angular's `$interpolate` services to interpolate strings against some values.
1868
+ *
1869
+ * @return {object} $translateInterpolator Interpolator service
1870
+ */
1871
+ angular.module('pascalprecht.translate').factory('$translateDefaultInterpolation', ['$interpolate', function ($interpolate) {
1872
+
1873
+ var $translateInterpolator = {},
1874
+ $locale,
1875
+ $identifier = 'default',
1876
+ $sanitizeValueStrategy = null,
1877
+ // map of all sanitize strategies
1878
+ sanitizeValueStrategies = {
1879
+ escaped: function (params) {
1880
+ var result = {};
1881
+ for (var key in params) {
1882
+ if (Object.prototype.hasOwnProperty.call(params, key)) {
1883
+ if (angular.isNumber(params[key])) {
1884
+ result[key] = params[key];
1885
+ } else {
1886
+ result[key] = angular.element('<div></div>').text(params[key]).html();
1887
+ }
1888
+ }
1889
+ }
1890
+ return result;
1891
+ }
1892
+ };
1893
+
1894
+ var sanitizeParams = function (params) {
1895
+ var result;
1896
+ if (angular.isFunction(sanitizeValueStrategies[$sanitizeValueStrategy])) {
1897
+ result = sanitizeValueStrategies[$sanitizeValueStrategy](params);
1898
+ } else {
1899
+ result = params;
1900
+ }
1901
+ return result;
1902
+ };
1903
+
1904
+ /**
1905
+ * @ngdoc function
1906
+ * @name pascalprecht.translate.$translateDefaultInterpolation#setLocale
1907
+ * @methodOf pascalprecht.translate.$translateDefaultInterpolation
1908
+ *
1909
+ * @description
1910
+ * Sets current locale (this is currently not use in this interpolation).
1911
+ *
1912
+ * @param {string} locale Language key or locale.
1913
+ */
1914
+ $translateInterpolator.setLocale = function (locale) {
1915
+ $locale = locale;
1916
+ };
1917
+
1918
+ /**
1919
+ * @ngdoc function
1920
+ * @name pascalprecht.translate.$translateDefaultInterpolation#getInterpolationIdentifier
1921
+ * @methodOf pascalprecht.translate.$translateDefaultInterpolation
1922
+ *
1923
+ * @description
1924
+ * Returns an identifier for this interpolation service.
1925
+ *
1926
+ * @returns {string} $identifier
1927
+ */
1928
+ $translateInterpolator.getInterpolationIdentifier = function () {
1929
+ return $identifier;
1930
+ };
1931
+
1932
+ $translateInterpolator.useSanitizeValueStrategy = function (value) {
1933
+ $sanitizeValueStrategy = value;
1934
+ return this;
1935
+ };
1936
+
1937
+ /**
1938
+ * @ngdoc function
1939
+ * @name pascalprecht.translate.$translateDefaultInterpolation#interpolate
1940
+ * @methodOf pascalprecht.translate.$translateDefaultInterpolation
1941
+ *
1942
+ * @description
1943
+ * Interpolates given string agains given interpolate params using angulars
1944
+ * `$interpolate` service.
1945
+ *
1946
+ * @returns {string} interpolated string.
1947
+ */
1948
+ $translateInterpolator.interpolate = function (string, interpolateParams) {
1949
+ if ($sanitizeValueStrategy) {
1950
+ interpolateParams = sanitizeParams(interpolateParams);
1951
+ }
1952
+ return $interpolate(string)(interpolateParams || {});
1953
+ };
1954
+
1955
+ return $translateInterpolator;
1956
+ }]);
1957
+
1958
+ angular.module('pascalprecht.translate').constant('$STORAGE_KEY', 'NG_TRANSLATE_LANG_KEY');
1959
+
1960
+ angular.module('pascalprecht.translate')
1961
+ /**
1962
+ * @ngdoc directive
1963
+ * @name pascalprecht.translate.directive:translate
1964
+ * @requires $compile
1965
+ * @requires $filter
1966
+ * @requires $interpolate
1967
+ * @restrict A
1968
+ *
1969
+ * @description
1970
+ * Translates given translation id either through attribute or DOM content.
1971
+ * Internally it uses `translate` filter to translate translation id. It possible to
1972
+ * pass an optional `translate-values` object literal as string into translation id.
1973
+ *
1974
+ * @param {string=} translate Translation id which could be either string or interpolated string.
1975
+ * @param {string=} translate-values Values to pass into translation id. Can be passed as object literal string or interpolated object.
1976
+ * @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute.
1977
+ * @param {string=} translate-default will be used unless translation was successful
1978
+ * @param {boolean=} translate-compile (default true if present) defines locally activation of {@link pascalprecht.translate.$translate#usePostCompiling}
1979
+ *
1980
+ * @example
1981
+ <example module="ngView">
1982
+ <file name="index.html">
1983
+ <div ng-controller="TranslateCtrl">
1984
+
1985
+ <pre translate="TRANSLATION_ID"></pre>
1986
+ <pre translate>TRANSLATION_ID</pre>
1987
+ <pre translate translate-attr-title="TRANSLATION_ID"></pre>
1988
+ <pre translate="{{translationId}}"></pre>
1989
+ <pre translate>{{translationId}}</pre>
1990
+ <pre translate="WITH_VALUES" translate-values="{value: 5}"></pre>
1991
+ <pre translate translate-values="{value: 5}">WITH_VALUES</pre>
1992
+ <pre translate="WITH_VALUES" translate-values="{{values}}"></pre>
1993
+ <pre translate translate-values="{{values}}">WITH_VALUES</pre>
1994
+ <pre translate translate-attr-title="WITH_VALUES" translate-values="{{values}}"></pre>
1995
+
1996
+ </div>
1997
+ </file>
1998
+ <file name="script.js">
1999
+ angular.module('ngView', ['pascalprecht.translate'])
2000
+
2001
+ .config(function ($translateProvider) {
2002
+
2003
+ $translateProvider.translations('en',{
2004
+ 'TRANSLATION_ID': 'Hello there!',
2005
+ 'WITH_VALUES': 'The following value is dynamic: {{value}}'
2006
+ }).preferredLanguage('en');
2007
+
2008
+ });
2009
+
2010
+ angular.module('ngView').controller('TranslateCtrl', function ($scope) {
2011
+ $scope.translationId = 'TRANSLATION_ID';
2012
+
2013
+ $scope.values = {
2014
+ value: 78
2015
+ };
2016
+ });
2017
+ </file>
2018
+ <file name="scenario.js">
2019
+ it('should translate', function () {
2020
+ inject(function ($rootScope, $compile) {
2021
+ $rootScope.translationId = 'TRANSLATION_ID';
2022
+
2023
+ element = $compile('<p translate="TRANSLATION_ID"></p>')($rootScope);
2024
+ $rootScope.$digest();
2025
+ expect(element.text()).toBe('Hello there!');
2026
+
2027
+ element = $compile('<p translate="{{translationId}}"></p>')($rootScope);
2028
+ $rootScope.$digest();
2029
+ expect(element.text()).toBe('Hello there!');
2030
+
2031
+ element = $compile('<p translate>TRANSLATION_ID</p>')($rootScope);
2032
+ $rootScope.$digest();
2033
+ expect(element.text()).toBe('Hello there!');
2034
+
2035
+ element = $compile('<p translate>{{translationId}}</p>')($rootScope);
2036
+ $rootScope.$digest();
2037
+ expect(element.text()).toBe('Hello there!');
2038
+
2039
+ element = $compile('<p translate translate-attr-title="TRANSLATION_ID"></p>')($rootScope);
2040
+ $rootScope.$digest();
2041
+ expect(element.attr('title')).toBe('Hello there!');
2042
+ });
2043
+ });
2044
+ </file>
2045
+ </example>
2046
+ */
2047
+ .directive('translate', ['$translate', '$q', '$interpolate', '$compile', '$parse', '$rootScope', function ($translate, $q, $interpolate, $compile, $parse, $rootScope) {
2048
+
2049
+ /**
2050
+ * @name trim
2051
+ * @private
2052
+ *
2053
+ * @description
2054
+ * trim polyfill
2055
+ *
2056
+ * @returns {string} The string stripped of whitespace from both ends
2057
+ */
2058
+ var trim = function() {
2059
+ return this.toString().replace(/^\s+|\s+$/g, '');
2060
+ };
2061
+
2062
+ return {
2063
+ restrict: 'AE',
2064
+ scope: true,
2065
+ priority: $translate.directivePriority(),
2066
+ compile: function (tElement, tAttr) {
2067
+
2068
+ var translateValuesExist = (tAttr.translateValues) ?
2069
+ tAttr.translateValues : undefined;
2070
+
2071
+ var translateInterpolation = (tAttr.translateInterpolation) ?
2072
+ tAttr.translateInterpolation : undefined;
2073
+
2074
+ var translateValueExist = tElement[0].outerHTML.match(/translate-value-+/i);
2075
+
2076
+ var interpolateRegExp = '^(.*)(' + $interpolate.startSymbol() + '.*' + $interpolate.endSymbol() + ')(.*)',
2077
+ watcherRegExp = '^(.*)' + $interpolate.startSymbol() + '(.*)' + $interpolate.endSymbol() + '(.*)';
2078
+
2079
+ return function linkFn(scope, iElement, iAttr) {
2080
+
2081
+ scope.interpolateParams = {};
2082
+ scope.preText = '';
2083
+ scope.postText = '';
2084
+ var translationIds = {};
2085
+
2086
+ // Ensures any change of the attribute "translate" containing the id will
2087
+ // be re-stored to the scope's "translationId".
2088
+ // If the attribute has no content, the element's text value (white spaces trimmed off) will be used.
2089
+ var observeElementTranslation = function (translationId) {
2090
+
2091
+ // Remove any old watcher
2092
+ if (angular.isFunction(observeElementTranslation._unwatchOld)) {
2093
+ observeElementTranslation._unwatchOld();
2094
+ observeElementTranslation._unwatchOld = undefined;
2095
+ }
2096
+
2097
+ if (angular.equals(translationId , '') || !angular.isDefined(translationId)) {
2098
+ // Resolve translation id by inner html if required
2099
+ var interpolateMatches = trim.apply(iElement.text()).match(interpolateRegExp);
2100
+ // Interpolate translation id if required
2101
+ if (angular.isArray(interpolateMatches)) {
2102
+ scope.preText = interpolateMatches[1];
2103
+ scope.postText = interpolateMatches[3];
2104
+ translationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent);
2105
+ var watcherMatches = iElement.text().match(watcherRegExp);
2106
+ if (angular.isArray(watcherMatches) && watcherMatches[2] && watcherMatches[2].length) {
2107
+ observeElementTranslation._unwatchOld = scope.$watch(watcherMatches[2], function (newValue) {
2108
+ translationIds.translate = newValue;
2109
+ updateTranslations();
2110
+ });
2111
+ }
2112
+ } else {
2113
+ translationIds.translate = iElement.text().replace(/^\s+|\s+$/g,'');
2114
+ }
2115
+ } else {
2116
+ translationIds.translate = translationId;
2117
+ }
2118
+ updateTranslations();
2119
+ };
2120
+
2121
+ var observeAttributeTranslation = function (translateAttr) {
2122
+ iAttr.$observe(translateAttr, function (translationId) {
2123
+ translationIds[translateAttr] = translationId;
2124
+ updateTranslations();
2125
+ });
2126
+ };
2127
+
2128
+ var firstAttributeChangedEvent = true;
2129
+ iAttr.$observe('translate', function (translationId) {
2130
+ if (typeof translationId === 'undefined') {
2131
+ // case of element "<translate>xyz</translate>"
2132
+ observeElementTranslation('');
2133
+ } else {
2134
+ // case of regular attribute
2135
+ if (translationId !== '' || !firstAttributeChangedEvent) {
2136
+ translationIds.translate = translationId;
2137
+ updateTranslations();
2138
+ }
2139
+ }
2140
+ firstAttributeChangedEvent = false;
2141
+ });
2142
+
2143
+ for (var translateAttr in iAttr) {
2144
+ if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr') {
2145
+ observeAttributeTranslation(translateAttr);
2146
+ }
2147
+ }
2148
+
2149
+ iAttr.$observe('translateDefault', function (value) {
2150
+ scope.defaultText = value;
2151
+ });
2152
+
2153
+ if (translateValuesExist) {
2154
+ iAttr.$observe('translateValues', function (interpolateParams) {
2155
+ if (interpolateParams) {
2156
+ scope.$parent.$watch(function () {
2157
+ angular.extend(scope.interpolateParams, $parse(interpolateParams)(scope.$parent));
2158
+ });
2159
+ }
2160
+ });
2161
+ }
2162
+
2163
+ if (translateValueExist) {
2164
+ var observeValueAttribute = function (attrName) {
2165
+ iAttr.$observe(attrName, function (value) {
2166
+ var attributeName = angular.lowercase(attrName.substr(14, 1)) + attrName.substr(15);
2167
+ scope.interpolateParams[attributeName] = value;
2168
+ });
2169
+ };
2170
+ for (var attr in iAttr) {
2171
+ if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') {
2172
+ observeValueAttribute(attr);
2173
+ }
2174
+ }
2175
+ }
2176
+
2177
+ // Master update function
2178
+ var updateTranslations = function () {
2179
+ for (var key in translationIds) {
2180
+ if (translationIds.hasOwnProperty(key)) {
2181
+ updateTranslation(key, translationIds[key], scope, scope.interpolateParams, scope.defaultText);
2182
+ }
2183
+ }
2184
+ };
2185
+
2186
+ // Put translation processing function outside loop
2187
+ var updateTranslation = function(translateAttr, translationId, scope, interpolateParams, defaultTranslationText) {
2188
+ if (translationId) {
2189
+ $translate(translationId, interpolateParams, translateInterpolation, defaultTranslationText)
2190
+ .then(function (translation) {
2191
+ applyTranslation(translation, scope, true, translateAttr);
2192
+ }, function (translationId) {
2193
+ applyTranslation(translationId, scope, false, translateAttr);
2194
+ });
2195
+ } else {
2196
+ // as an empty string cannot be translated, we can solve this using successful=false
2197
+ applyTranslation(translationId, scope, false, translateAttr);
2198
+ }
2199
+ };
2200
+
2201
+ var applyTranslation = function (value, scope, successful, translateAttr) {
2202
+ if (translateAttr === 'translate') {
2203
+ // default translate into innerHTML
2204
+ if (!successful && typeof scope.defaultText !== 'undefined') {
2205
+ value = scope.defaultText;
2206
+ }
2207
+ iElement.html(scope.preText + value + scope.postText);
2208
+ var globallyEnabled = $translate.isPostCompilingEnabled();
2209
+ var locallyDefined = typeof tAttr.translateCompile !== 'undefined';
2210
+ var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false';
2211
+ if ((globallyEnabled && !locallyDefined) || locallyEnabled) {
2212
+ $compile(iElement.contents())(scope);
2213
+ }
2214
+ } else {
2215
+ // translate attribute
2216
+ if (!successful && typeof scope.defaultText !== 'undefined') {
2217
+ value = scope.defaultText;
2218
+ }
2219
+ var attributeName = iAttr.$attr[translateAttr].substr(15);
2220
+ iElement.attr(attributeName, value);
2221
+ }
2222
+ };
2223
+
2224
+ scope.$watch('interpolateParams', updateTranslations, true);
2225
+
2226
+ // Ensures the text will be refreshed after the current language was changed
2227
+ // w/ $translate.use(...)
2228
+ var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations);
2229
+
2230
+ // ensure translation will be looked up at least one
2231
+ if (iElement.text().length) {
2232
+ observeElementTranslation('');
2233
+ }
2234
+ updateTranslations();
2235
+ scope.$on('$destroy', unbind);
2236
+ };
2237
+ }
2238
+ };
2239
+ }]);
2240
+
2241
+ angular.module('pascalprecht.translate')
2242
+ /**
2243
+ * @ngdoc directive
2244
+ * @name pascalprecht.translate.directive:translateCloak
2245
+ * @requires $rootScope
2246
+ * @requires $translate
2247
+ * @restrict A
2248
+ *
2249
+ * $description
2250
+ * Adds a `translate-cloak` class name to the given element where this directive
2251
+ * is applied initially and removes it, once a loader has finished loading.
2252
+ *
2253
+ * This directive can be used to prevent initial flickering when loading translation
2254
+ * data asynchronously.
2255
+ *
2256
+ * The class name is defined in
2257
+ * {@link pascalprecht.translate.$translateProvider#cloakClassName $translate.cloakClassName()}.
2258
+ *
2259
+ * @param {string=} translate-cloak If a translationId is provided, it will be used for showing
2260
+ * or hiding the cloak. Basically it relies on the translation
2261
+ * resolve.
2262
+ */
2263
+ .directive('translateCloak', ['$rootScope', '$translate', function ($rootScope, $translate) {
2264
+
2265
+ return {
2266
+ compile: function (tElement) {
2267
+ var applyCloak = function () {
2268
+ tElement.addClass($translate.cloakClassName());
2269
+ },
2270
+ removeCloak = function () {
2271
+ tElement.removeClass($translate.cloakClassName());
2272
+ },
2273
+ removeListener = $rootScope.$on('$translateChangeEnd', function () {
2274
+ removeCloak();
2275
+ removeListener();
2276
+ removeListener = null;
2277
+ });
2278
+ applyCloak();
2279
+
2280
+ return function linkFn(scope, iElement, iAttr) {
2281
+ // Register a watcher for the defined translation allowing a fine tuned cloak
2282
+ if (iAttr.translateCloak && iAttr.translateCloak.length) {
2283
+ iAttr.$observe('translateCloak', function (translationId) {
2284
+ $translate(translationId).then(removeCloak, applyCloak);
2285
+ });
2286
+ }
2287
+ };
2288
+ }
2289
+ };
2290
+ }]);
2291
+
2292
+ angular.module('pascalprecht.translate')
2293
+ /**
2294
+ * @ngdoc filter
2295
+ * @name pascalprecht.translate.filter:translate
2296
+ * @requires $parse
2297
+ * @requires pascalprecht.translate.$translate
2298
+ * @function
2299
+ *
2300
+ * @description
2301
+ * Uses `$translate` service to translate contents. Accepts interpolate parameters
2302
+ * to pass dynamized values though translation.
2303
+ *
2304
+ * @param {string} translationId A translation id to be translated.
2305
+ * @param {*=} interpolateParams Optional object literal (as hash or string) to pass values into translation.
2306
+ *
2307
+ * @returns {string} Translated text.
2308
+ *
2309
+ * @example
2310
+ <example module="ngView">
2311
+ <file name="index.html">
2312
+ <div ng-controller="TranslateCtrl">
2313
+
2314
+ <pre>{{ 'TRANSLATION_ID' | translate }}</pre>
2315
+ <pre>{{ translationId | translate }}</pre>
2316
+ <pre>{{ 'WITH_VALUES' | translate:'{value: 5}' }}</pre>
2317
+ <pre>{{ 'WITH_VALUES' | translate:values }}</pre>
2318
+
2319
+ </div>
2320
+ </file>
2321
+ <file name="script.js">
2322
+ angular.module('ngView', ['pascalprecht.translate'])
2323
+
2324
+ .config(function ($translateProvider) {
2325
+
2326
+ $translateProvider.translations('en', {
2327
+ 'TRANSLATION_ID': 'Hello there!',
2328
+ 'WITH_VALUES': 'The following value is dynamic: {{value}}'
2329
+ });
2330
+ $translateProvider.preferredLanguage('en');
2331
+
2332
+ });
2333
+
2334
+ angular.module('ngView').controller('TranslateCtrl', function ($scope) {
2335
+ $scope.translationId = 'TRANSLATION_ID';
2336
+
2337
+ $scope.values = {
2338
+ value: 78
2339
+ };
2340
+ });
2341
+ </file>
2342
+ </example>
2343
+ */
2344
+ .filter('translate', ['$parse', '$translate', function ($parse, $translate) {
2345
+ var translateFilter = function (translationId, interpolateParams, interpolation) {
2346
+
2347
+ if (!angular.isObject(interpolateParams)) {
2348
+ interpolateParams = $parse(interpolateParams)(this);
2349
+ }
2350
+
2351
+ return $translate.instant(translationId, interpolateParams, interpolation);
2352
+ };
2353
+
2354
+ // Since AngularJS 1.3, filters which are not stateless (depending at the scope)
2355
+ // have to explicit define this behavior.
2356
+ translateFilter.$stateful = true;
2357
+
2358
+ return translateFilter;
2359
+ }]);