angular-translate-rails 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4d961961c3745245acce0ee7b453ee921b4c28b1
4
+ data.tar.gz: e61c1451041121351383578542e1bfffdde34898
5
+ SHA512:
6
+ metadata.gz: 15aa3de3677d50a51db462ba06be62cea4df730458b1222f439c34f75db42e16d62d2533cb54130c5df0b028846a1b46607f182aa11f5e34a2f79eae066b914f
7
+ data.tar.gz: f2c3eaed012b2d63e96ed69d6eefd0f6d5631648470204050d3ba951fc7c5327a9ab1ad451d18da7e596c7ce27a329a0a70deafec7bfd40602f71be6b5d30e58
@@ -0,0 +1,3 @@
1
+ # 2.6.0 (2015-02-26)
2
+
3
+ Initial commit matched with angular-translate version, not really released yet.
@@ -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.
@@ -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,2354 @@
1
+ /*!
2
+ * angular-translate - v2.6.0 - 2015-02-08
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.0';
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.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 = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator;
1364
+
1365
+ // if the translation id exists, we can just interpolate it
1366
+ if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {
1367
+ var translation = table[translationId];
1368
+
1369
+ // If using link, rerun $translate with linked translationId and return it
1370
+ if (translation.substr(0, 2) === '@:') {
1371
+ result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId);
1372
+ } else {
1373
+ result = Interpolator.interpolate(translation, interpolateParams);
1374
+ }
1375
+ } else {
1376
+ var missingTranslationHandlerTranslation;
1377
+ // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
1378
+ if ($missingTranslationHandlerFactory && !pendingLoader) {
1379
+ missingTranslationHandlerTranslation = translateByHandler(translationId);
1380
+ }
1381
+
1382
+ // since we couldn't translate the inital requested translation id,
1383
+ // we try it now with one or more fallback languages, if fallback language(s) is
1384
+ // configured.
1385
+ if ($uses && $fallbackLanguage && $fallbackLanguage.length) {
1386
+ fallbackIndex = 0;
1387
+ result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator);
1388
+ } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
1389
+ // looks like the requested translation id doesn't exists.
1390
+ // Now, if there is a registered handler for missing translations and no
1391
+ // asyncLoader is pending, we execute the handler
1392
+ result = missingTranslationHandlerTranslation;
1393
+ } else {
1394
+ result = applyNotFoundIndicators(translationId);
1395
+ }
1396
+ }
1397
+
1398
+ return result;
1399
+ };
1400
+
1401
+ /**
1402
+ * @ngdoc function
1403
+ * @name pascalprecht.translate.$translate#preferredLanguage
1404
+ * @methodOf pascalprecht.translate.$translate
1405
+ *
1406
+ * @description
1407
+ * Returns the language key for the preferred language.
1408
+ *
1409
+ * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime)
1410
+ *
1411
+ * @return {string} preferred language key
1412
+ */
1413
+ $translate.preferredLanguage = function (langKey) {
1414
+ if(langKey) {
1415
+ setupPreferredLanguage(langKey);
1416
+ }
1417
+ return $preferredLanguage;
1418
+ };
1419
+
1420
+ /**
1421
+ * @ngdoc function
1422
+ * @name pascalprecht.translate.$translate#cloakClassName
1423
+ * @methodOf pascalprecht.translate.$translate
1424
+ *
1425
+ * @description
1426
+ * Returns the configured class name for `translate-cloak` directive.
1427
+ *
1428
+ * @return {string} cloakClassName
1429
+ */
1430
+ $translate.cloakClassName = function () {
1431
+ return $cloakClassName;
1432
+ };
1433
+
1434
+ /**
1435
+ * @ngdoc function
1436
+ * @name pascalprecht.translate.$translate#fallbackLanguage
1437
+ * @methodOf pascalprecht.translate.$translate
1438
+ *
1439
+ * @description
1440
+ * Returns the language key for the fallback languages or sets a new fallback stack.
1441
+ *
1442
+ * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime)
1443
+ *
1444
+ * @return {string||array} fallback language key
1445
+ */
1446
+ $translate.fallbackLanguage = function (langKey) {
1447
+ if (langKey !== undefined && langKey !== null) {
1448
+ fallbackStack(langKey);
1449
+
1450
+ // as we might have an async loader initiated and a new translation language might have been defined
1451
+ // we need to add the promise to the stack also. So - iterate.
1452
+ if ($loaderFactory) {
1453
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1454
+ for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
1455
+ if (!langPromises[$fallbackLanguage[i]]) {
1456
+ langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]);
1457
+ }
1458
+ }
1459
+ }
1460
+ }
1461
+ $translate.use($translate.use());
1462
+ }
1463
+ if ($fallbackWasString) {
1464
+ return $fallbackLanguage[0];
1465
+ } else {
1466
+ return $fallbackLanguage;
1467
+ }
1468
+
1469
+ };
1470
+
1471
+ /**
1472
+ * @ngdoc function
1473
+ * @name pascalprecht.translate.$translate#useFallbackLanguage
1474
+ * @methodOf pascalprecht.translate.$translate
1475
+ *
1476
+ * @description
1477
+ * Sets the first key of the fallback language stack to be used for translation.
1478
+ * Therefore all languages in the fallback array BEFORE this key will be skipped!
1479
+ *
1480
+ * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to
1481
+ * get back to the whole stack
1482
+ */
1483
+ $translate.useFallbackLanguage = function (langKey) {
1484
+ if (langKey !== undefined && langKey !== null) {
1485
+ if (!langKey) {
1486
+ startFallbackIteration = 0;
1487
+ } else {
1488
+ var langKeyPosition = indexOf($fallbackLanguage, langKey);
1489
+ if (langKeyPosition > -1) {
1490
+ startFallbackIteration = langKeyPosition;
1491
+ }
1492
+ }
1493
+
1494
+ }
1495
+
1496
+ };
1497
+
1498
+ /**
1499
+ * @ngdoc function
1500
+ * @name pascalprecht.translate.$translate#proposedLanguage
1501
+ * @methodOf pascalprecht.translate.$translate
1502
+ *
1503
+ * @description
1504
+ * Returns the language key of language that is currently loaded asynchronously.
1505
+ *
1506
+ * @return {string} language key
1507
+ */
1508
+ $translate.proposedLanguage = function () {
1509
+ return $nextLang;
1510
+ };
1511
+
1512
+ /**
1513
+ * @ngdoc function
1514
+ * @name pascalprecht.translate.$translate#storage
1515
+ * @methodOf pascalprecht.translate.$translate
1516
+ *
1517
+ * @description
1518
+ * Returns registered storage.
1519
+ *
1520
+ * @return {object} Storage
1521
+ */
1522
+ $translate.storage = function () {
1523
+ return Storage;
1524
+ };
1525
+
1526
+ /**
1527
+ * @ngdoc function
1528
+ * @name pascalprecht.translate.$translate#use
1529
+ * @methodOf pascalprecht.translate.$translate
1530
+ *
1531
+ * @description
1532
+ * Tells angular-translate which language to use by given language key. This method is
1533
+ * used to change language at runtime. It also takes care of storing the language
1534
+ * key in a configured store to let your app remember the choosed language.
1535
+ *
1536
+ * When trying to 'use' a language which isn't available it tries to load it
1537
+ * asynchronously with registered loaders.
1538
+ *
1539
+ * Returns promise object with loaded language file data
1540
+ * @example
1541
+ * $translate.use("en_US").then(function(data){
1542
+ * $scope.text = $translate("HELLO");
1543
+ * });
1544
+ *
1545
+ * @param {string} key Language key
1546
+ * @return {string} Language key
1547
+ */
1548
+ $translate.use = function (key) {
1549
+ if (!key) {
1550
+ return $uses;
1551
+ }
1552
+
1553
+ var deferred = $q.defer();
1554
+
1555
+ $rootScope.$emit('$translateChangeStart', {language: key});
1556
+
1557
+ // Try to get the aliased language key
1558
+ var aliasedKey = negotiateLocale(key);
1559
+ if (aliasedKey) {
1560
+ key = aliasedKey;
1561
+ }
1562
+
1563
+ // if there isn't a translation table for the language we've requested,
1564
+ // we load it asynchronously
1565
+ if (!$translationTable[key] && $loaderFactory && !langPromises[key]) {
1566
+ $nextLang = key;
1567
+ langPromises[key] = loadAsync(key).then(function (translation) {
1568
+ translations(translation.key, translation.table);
1569
+ deferred.resolve(translation.key);
1570
+
1571
+ useLanguage(translation.key);
1572
+ if ($nextLang === key) {
1573
+ $nextLang = undefined;
1574
+ }
1575
+ return translation;
1576
+ }, function (key) {
1577
+ if ($nextLang === key) {
1578
+ $nextLang = undefined;
1579
+ }
1580
+ $rootScope.$emit('$translateChangeError', {language: key});
1581
+ deferred.reject(key);
1582
+ $rootScope.$emit('$translateChangeEnd', {language: key});
1583
+ });
1584
+ } else {
1585
+ deferred.resolve(key);
1586
+ useLanguage(key);
1587
+ }
1588
+
1589
+ return deferred.promise;
1590
+ };
1591
+
1592
+ /**
1593
+ * @ngdoc function
1594
+ * @name pascalprecht.translate.$translate#storageKey
1595
+ * @methodOf pascalprecht.translate.$translate
1596
+ *
1597
+ * @description
1598
+ * Returns the key for the storage.
1599
+ *
1600
+ * @return {string} storage key
1601
+ */
1602
+ $translate.storageKey = function () {
1603
+ return storageKey();
1604
+ };
1605
+
1606
+ /**
1607
+ * @ngdoc function
1608
+ * @name pascalprecht.translate.$translate#isPostCompilingEnabled
1609
+ * @methodOf pascalprecht.translate.$translate
1610
+ *
1611
+ * @description
1612
+ * Returns whether post compiling is enabled or not
1613
+ *
1614
+ * @return {bool} storage key
1615
+ */
1616
+ $translate.isPostCompilingEnabled = function () {
1617
+ return $postCompilingEnabled;
1618
+ };
1619
+
1620
+ /**
1621
+ * @ngdoc function
1622
+ * @name pascalprecht.translate.$translate#refresh
1623
+ * @methodOf pascalprecht.translate.$translate
1624
+ *
1625
+ * @description
1626
+ * Refreshes a translation table pointed by the given langKey. If langKey is not specified,
1627
+ * the module will drop all existent translation tables and load new version of those which
1628
+ * are currently in use.
1629
+ *
1630
+ * Refresh means that the module will drop target translation table and try to load it again.
1631
+ *
1632
+ * In case there are no loaders registered the refresh() method will throw an Error.
1633
+ *
1634
+ * If the module is able to refresh translation tables refresh() method will broadcast
1635
+ * $translateRefreshStart and $translateRefreshEnd events.
1636
+ *
1637
+ * @example
1638
+ * // this will drop all currently existent translation tables and reload those which are
1639
+ * // currently in use
1640
+ * $translate.refresh();
1641
+ * // this will refresh a translation table for the en_US language
1642
+ * $translate.refresh('en_US');
1643
+ *
1644
+ * @param {string} langKey A language key of the table, which has to be refreshed
1645
+ *
1646
+ * @return {promise} Promise, which will be resolved in case a translation tables refreshing
1647
+ * process is finished successfully, and reject if not.
1648
+ */
1649
+ $translate.refresh = function (langKey) {
1650
+ if (!$loaderFactory) {
1651
+ throw new Error('Couldn\'t refresh translation table, no loader registered!');
1652
+ }
1653
+
1654
+ var deferred = $q.defer();
1655
+
1656
+ function resolve() {
1657
+ deferred.resolve();
1658
+ $rootScope.$emit('$translateRefreshEnd', {language: langKey});
1659
+ }
1660
+
1661
+ function reject() {
1662
+ deferred.reject();
1663
+ $rootScope.$emit('$translateRefreshEnd', {language: langKey});
1664
+ }
1665
+
1666
+ $rootScope.$emit('$translateRefreshStart', {language: langKey});
1667
+
1668
+ if (!langKey) {
1669
+ // if there's no language key specified we refresh ALL THE THINGS!
1670
+ var tables = [], loadingKeys = {};
1671
+
1672
+ // reload registered fallback languages
1673
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1674
+ for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
1675
+ tables.push(loadAsync($fallbackLanguage[i]));
1676
+ loadingKeys[$fallbackLanguage[i]] = true;
1677
+ }
1678
+ }
1679
+
1680
+ // reload currently used language
1681
+ if ($uses && !loadingKeys[$uses]) {
1682
+ tables.push(loadAsync($uses));
1683
+ }
1684
+
1685
+ $q.all(tables).then(function (tableData) {
1686
+ angular.forEach(tableData, function (data) {
1687
+ if ($translationTable[data.key]) {
1688
+ delete $translationTable[data.key];
1689
+ }
1690
+ translations(data.key, data.table);
1691
+ });
1692
+ if ($uses) {
1693
+ useLanguage($uses);
1694
+ }
1695
+ resolve();
1696
+ });
1697
+
1698
+ } else if ($translationTable[langKey]) {
1699
+
1700
+ loadAsync(langKey).then(function (data) {
1701
+ translations(data.key, data.table);
1702
+ if (langKey === $uses) {
1703
+ useLanguage($uses);
1704
+ }
1705
+ resolve();
1706
+ }, reject);
1707
+
1708
+ } else {
1709
+ reject();
1710
+ }
1711
+ return deferred.promise;
1712
+ };
1713
+
1714
+ /**
1715
+ * @ngdoc function
1716
+ * @name pascalprecht.translate.$translate#instant
1717
+ * @methodOf pascalprecht.translate.$translate
1718
+ *
1719
+ * @description
1720
+ * Returns a translation instantly from the internal state of loaded translation. All rules
1721
+ * regarding the current language, the preferred language of even fallback languages will be
1722
+ * used except any promise handling. If a language was not found, an asynchronous loading
1723
+ * will be invoked in the background.
1724
+ *
1725
+ * @param {string|array} translationId A token which represents a translation id
1726
+ * This can be optionally an array of translation ids which
1727
+ * results that the function's promise returns an object where
1728
+ * each key is the translation id and the value the translation.
1729
+ * @param {object} interpolateParams Params
1730
+ * @param {string} interpolationId The id of the interpolation to use
1731
+ *
1732
+ * @return {string} translation
1733
+ */
1734
+ $translate.instant = function (translationId, interpolateParams, interpolationId) {
1735
+
1736
+ // Detect undefined and null values to shorten the execution and prevent exceptions
1737
+ if (translationId === null || angular.isUndefined(translationId)) {
1738
+ return translationId;
1739
+ }
1740
+
1741
+ // Duck detection: If the first argument is an array, a bunch of translations was requested.
1742
+ // The result is an object.
1743
+ if (angular.isArray(translationId)) {
1744
+ var results = {};
1745
+ for (var i = 0, c = translationId.length; i < c; i++) {
1746
+ results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId);
1747
+ }
1748
+ return results;
1749
+ }
1750
+
1751
+ // We discarded unacceptable values. So we just need to verify if translationId is empty String
1752
+ if (angular.isString(translationId) && translationId.length < 1) {
1753
+ return translationId;
1754
+ }
1755
+
1756
+ // trim off any whitespace
1757
+ if (translationId) {
1758
+ translationId = trim.apply(translationId);
1759
+ }
1760
+
1761
+ var result, possibleLangKeys = [];
1762
+ if ($preferredLanguage) {
1763
+ possibleLangKeys.push($preferredLanguage);
1764
+ }
1765
+ if ($uses) {
1766
+ possibleLangKeys.push($uses);
1767
+ }
1768
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1769
+ possibleLangKeys = possibleLangKeys.concat($fallbackLanguage);
1770
+ }
1771
+ for (var j = 0, d = possibleLangKeys.length; j < d; j++) {
1772
+ var possibleLangKey = possibleLangKeys[j];
1773
+ if ($translationTable[possibleLangKey]) {
1774
+ if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') {
1775
+ result = determineTranslationInstant(translationId, interpolateParams, interpolationId);
1776
+ } else if ($notFoundIndicatorLeft || $notFoundIndicatorRight) {
1777
+ result = applyNotFoundIndicators(translationId);
1778
+ }
1779
+ }
1780
+ if (typeof result !== 'undefined') {
1781
+ break;
1782
+ }
1783
+ }
1784
+
1785
+ if (!result && result !== '') {
1786
+ // Return translation of default interpolator if not found anything.
1787
+ result = defaultInterpolator.interpolate(translationId, interpolateParams);
1788
+ if ($missingTranslationHandlerFactory && !pendingLoader) {
1789
+ result = translateByHandler(translationId);
1790
+ }
1791
+ }
1792
+
1793
+ return result;
1794
+ };
1795
+
1796
+ /**
1797
+ * @ngdoc function
1798
+ * @name pascalprecht.translate.$translate#versionInfo
1799
+ * @methodOf pascalprecht.translate.$translate
1800
+ *
1801
+ * @description
1802
+ * Returns the current version information for the angular-translate library
1803
+ *
1804
+ * @return {string} angular-translate version
1805
+ */
1806
+ $translate.versionInfo = function () {
1807
+ return version;
1808
+ };
1809
+
1810
+ /**
1811
+ * @ngdoc function
1812
+ * @name pascalprecht.translate.$translate#loaderCache
1813
+ * @methodOf pascalprecht.translate.$translate
1814
+ *
1815
+ * @description
1816
+ * Returns the defined loaderCache.
1817
+ *
1818
+ * @return {boolean|string|object} current value of loaderCache
1819
+ */
1820
+ $translate.loaderCache = function () {
1821
+ return loaderCache;
1822
+ };
1823
+
1824
+ // internal purpose only
1825
+ $translate.directivePriority = function () {
1826
+ return directivePriority;
1827
+ };
1828
+
1829
+ if ($loaderFactory) {
1830
+
1831
+ // If at least one async loader is defined and there are no
1832
+ // (default) translations available we should try to load them.
1833
+ if (angular.equals($translationTable, {})) {
1834
+ $translate.use($translate.use());
1835
+ }
1836
+
1837
+ // Also, if there are any fallback language registered, we start
1838
+ // loading them asynchronously as soon as we can.
1839
+ if ($fallbackLanguage && $fallbackLanguage.length) {
1840
+ var processAsyncResult = function (translation) {
1841
+ translations(translation.key, translation.table);
1842
+ $rootScope.$emit('$translateChangeEnd', { language: translation.key });
1843
+ return translation;
1844
+ };
1845
+ for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
1846
+ langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]).then(processAsyncResult);
1847
+ }
1848
+ }
1849
+ }
1850
+
1851
+ return $translate;
1852
+ }
1853
+ ];
1854
+ }]);
1855
+
1856
+ /**
1857
+ * @ngdoc object
1858
+ * @name pascalprecht.translate.$translateDefaultInterpolation
1859
+ * @requires $interpolate
1860
+ *
1861
+ * @description
1862
+ * Uses angular's `$interpolate` services to interpolate strings against some values.
1863
+ *
1864
+ * @return {object} $translateInterpolator Interpolator service
1865
+ */
1866
+ angular.module('pascalprecht.translate').factory('$translateDefaultInterpolation', ['$interpolate', function ($interpolate) {
1867
+
1868
+ var $translateInterpolator = {},
1869
+ $locale,
1870
+ $identifier = 'default',
1871
+ $sanitizeValueStrategy = null,
1872
+ // map of all sanitize strategies
1873
+ sanitizeValueStrategies = {
1874
+ escaped: function (params) {
1875
+ var result = {};
1876
+ for (var key in params) {
1877
+ if (Object.prototype.hasOwnProperty.call(params, key)) {
1878
+ if (angular.isNumber(params[key])) {
1879
+ result[key] = params[key];
1880
+ } else {
1881
+ result[key] = angular.element('<div></div>').text(params[key]).html();
1882
+ }
1883
+ }
1884
+ }
1885
+ return result;
1886
+ }
1887
+ };
1888
+
1889
+ var sanitizeParams = function (params) {
1890
+ var result;
1891
+ if (angular.isFunction(sanitizeValueStrategies[$sanitizeValueStrategy])) {
1892
+ result = sanitizeValueStrategies[$sanitizeValueStrategy](params);
1893
+ } else {
1894
+ result = params;
1895
+ }
1896
+ return result;
1897
+ };
1898
+
1899
+ /**
1900
+ * @ngdoc function
1901
+ * @name pascalprecht.translate.$translateDefaultInterpolation#setLocale
1902
+ * @methodOf pascalprecht.translate.$translateDefaultInterpolation
1903
+ *
1904
+ * @description
1905
+ * Sets current locale (this is currently not use in this interpolation).
1906
+ *
1907
+ * @param {string} locale Language key or locale.
1908
+ */
1909
+ $translateInterpolator.setLocale = function (locale) {
1910
+ $locale = locale;
1911
+ };
1912
+
1913
+ /**
1914
+ * @ngdoc function
1915
+ * @name pascalprecht.translate.$translateDefaultInterpolation#getInterpolationIdentifier
1916
+ * @methodOf pascalprecht.translate.$translateDefaultInterpolation
1917
+ *
1918
+ * @description
1919
+ * Returns an identifier for this interpolation service.
1920
+ *
1921
+ * @returns {string} $identifier
1922
+ */
1923
+ $translateInterpolator.getInterpolationIdentifier = function () {
1924
+ return $identifier;
1925
+ };
1926
+
1927
+ $translateInterpolator.useSanitizeValueStrategy = function (value) {
1928
+ $sanitizeValueStrategy = value;
1929
+ return this;
1930
+ };
1931
+
1932
+ /**
1933
+ * @ngdoc function
1934
+ * @name pascalprecht.translate.$translateDefaultInterpolation#interpolate
1935
+ * @methodOf pascalprecht.translate.$translateDefaultInterpolation
1936
+ *
1937
+ * @description
1938
+ * Interpolates given string agains given interpolate params using angulars
1939
+ * `$interpolate` service.
1940
+ *
1941
+ * @returns {string} interpolated string.
1942
+ */
1943
+ $translateInterpolator.interpolate = function (string, interpolateParams) {
1944
+ if ($sanitizeValueStrategy) {
1945
+ interpolateParams = sanitizeParams(interpolateParams);
1946
+ }
1947
+ return $interpolate(string)(interpolateParams || {});
1948
+ };
1949
+
1950
+ return $translateInterpolator;
1951
+ }]);
1952
+
1953
+ angular.module('pascalprecht.translate').constant('$STORAGE_KEY', 'NG_TRANSLATE_LANG_KEY');
1954
+
1955
+ angular.module('pascalprecht.translate')
1956
+ /**
1957
+ * @ngdoc directive
1958
+ * @name pascalprecht.translate.directive:translate
1959
+ * @requires $compile
1960
+ * @requires $filter
1961
+ * @requires $interpolate
1962
+ * @restrict A
1963
+ *
1964
+ * @description
1965
+ * Translates given translation id either through attribute or DOM content.
1966
+ * Internally it uses `translate` filter to translate translation id. It possible to
1967
+ * pass an optional `translate-values` object literal as string into translation id.
1968
+ *
1969
+ * @param {string=} translate Translation id which could be either string or interpolated string.
1970
+ * @param {string=} translate-values Values to pass into translation id. Can be passed as object literal string or interpolated object.
1971
+ * @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute.
1972
+ * @param {string=} translate-default will be used unless translation was successful
1973
+ * @param {boolean=} translate-compile (default true if present) defines locally activation of {@link pascalprecht.translate.$translate#usePostCompiling}
1974
+ *
1975
+ * @example
1976
+ <example module="ngView">
1977
+ <file name="index.html">
1978
+ <div ng-controller="TranslateCtrl">
1979
+
1980
+ <pre translate="TRANSLATION_ID"></pre>
1981
+ <pre translate>TRANSLATION_ID</pre>
1982
+ <pre translate translate-attr-title="TRANSLATION_ID"></pre>
1983
+ <pre translate="{{translationId}}"></pre>
1984
+ <pre translate>{{translationId}}</pre>
1985
+ <pre translate="WITH_VALUES" translate-values="{value: 5}"></pre>
1986
+ <pre translate translate-values="{value: 5}">WITH_VALUES</pre>
1987
+ <pre translate="WITH_VALUES" translate-values="{{values}}"></pre>
1988
+ <pre translate translate-values="{{values}}">WITH_VALUES</pre>
1989
+ <pre translate translate-attr-title="WITH_VALUES" translate-values="{{values}}"></pre>
1990
+
1991
+ </div>
1992
+ </file>
1993
+ <file name="script.js">
1994
+ angular.module('ngView', ['pascalprecht.translate'])
1995
+
1996
+ .config(function ($translateProvider) {
1997
+
1998
+ $translateProvider.translations('en',{
1999
+ 'TRANSLATION_ID': 'Hello there!',
2000
+ 'WITH_VALUES': 'The following value is dynamic: {{value}}'
2001
+ }).preferredLanguage('en');
2002
+
2003
+ });
2004
+
2005
+ angular.module('ngView').controller('TranslateCtrl', function ($scope) {
2006
+ $scope.translationId = 'TRANSLATION_ID';
2007
+
2008
+ $scope.values = {
2009
+ value: 78
2010
+ };
2011
+ });
2012
+ </file>
2013
+ <file name="scenario.js">
2014
+ it('should translate', function () {
2015
+ inject(function ($rootScope, $compile) {
2016
+ $rootScope.translationId = 'TRANSLATION_ID';
2017
+
2018
+ element = $compile('<p translate="TRANSLATION_ID"></p>')($rootScope);
2019
+ $rootScope.$digest();
2020
+ expect(element.text()).toBe('Hello there!');
2021
+
2022
+ element = $compile('<p translate="{{translationId}}"></p>')($rootScope);
2023
+ $rootScope.$digest();
2024
+ expect(element.text()).toBe('Hello there!');
2025
+
2026
+ element = $compile('<p translate>TRANSLATION_ID</p>')($rootScope);
2027
+ $rootScope.$digest();
2028
+ expect(element.text()).toBe('Hello there!');
2029
+
2030
+ element = $compile('<p translate>{{translationId}}</p>')($rootScope);
2031
+ $rootScope.$digest();
2032
+ expect(element.text()).toBe('Hello there!');
2033
+
2034
+ element = $compile('<p translate translate-attr-title="TRANSLATION_ID"></p>')($rootScope);
2035
+ $rootScope.$digest();
2036
+ expect(element.attr('title')).toBe('Hello there!');
2037
+ });
2038
+ });
2039
+ </file>
2040
+ </example>
2041
+ */
2042
+ .directive('translate', ['$translate', '$q', '$interpolate', '$compile', '$parse', '$rootScope', function ($translate, $q, $interpolate, $compile, $parse, $rootScope) {
2043
+
2044
+ /**
2045
+ * @name trim
2046
+ * @private
2047
+ *
2048
+ * @description
2049
+ * trim polyfill
2050
+ *
2051
+ * @returns {string} The string stripped of whitespace from both ends
2052
+ */
2053
+ var trim = function() {
2054
+ return this.replace(/^\s+|\s+$/g, '');
2055
+ };
2056
+
2057
+ return {
2058
+ restrict: 'AE',
2059
+ scope: true,
2060
+ priority: $translate.directivePriority(),
2061
+ compile: function (tElement, tAttr) {
2062
+
2063
+ var translateValuesExist = (tAttr.translateValues) ?
2064
+ tAttr.translateValues : undefined;
2065
+
2066
+ var translateInterpolation = (tAttr.translateInterpolation) ?
2067
+ tAttr.translateInterpolation : undefined;
2068
+
2069
+ var translateValueExist = tElement[0].outerHTML.match(/translate-value-+/i);
2070
+
2071
+ var interpolateRegExp = '^(.*)(' + $interpolate.startSymbol() + '.*' + $interpolate.endSymbol() + ')(.*)',
2072
+ watcherRegExp = '^(.*)' + $interpolate.startSymbol() + '(.*)' + $interpolate.endSymbol() + '(.*)';
2073
+
2074
+ return function linkFn(scope, iElement, iAttr) {
2075
+
2076
+ scope.interpolateParams = {};
2077
+ scope.preText = '';
2078
+ scope.postText = '';
2079
+ var translationIds = {};
2080
+
2081
+ // Ensures any change of the attribute "translate" containing the id will
2082
+ // be re-stored to the scope's "translationId".
2083
+ // If the attribute has no content, the element's text value (white spaces trimmed off) will be used.
2084
+ var observeElementTranslation = function (translationId) {
2085
+
2086
+ // Remove any old watcher
2087
+ if (angular.isFunction(observeElementTranslation._unwatchOld)) {
2088
+ observeElementTranslation._unwatchOld();
2089
+ observeElementTranslation._unwatchOld = undefined;
2090
+ }
2091
+
2092
+ if (angular.equals(translationId , '') || !angular.isDefined(translationId)) {
2093
+ // Resolve translation id by inner html if required
2094
+ var interpolateMatches = trim.apply(iElement.text()).match(interpolateRegExp);
2095
+ // Interpolate translation id if required
2096
+ if (angular.isArray(interpolateMatches)) {
2097
+ scope.preText = interpolateMatches[1];
2098
+ scope.postText = interpolateMatches[3];
2099
+ translationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent);
2100
+ var watcherMatches = iElement.text().match(watcherRegExp);
2101
+ if (angular.isArray(watcherMatches) && watcherMatches[2] && watcherMatches[2].length) {
2102
+ observeElementTranslation._unwatchOld = scope.$watch(watcherMatches[2], function (newValue) {
2103
+ translationIds.translate = newValue;
2104
+ updateTranslations();
2105
+ });
2106
+ }
2107
+ } else {
2108
+ translationIds.translate = iElement.text().replace(/^\s+|\s+$/g,'');
2109
+ }
2110
+ } else {
2111
+ translationIds.translate = translationId;
2112
+ }
2113
+ updateTranslations();
2114
+ };
2115
+
2116
+ var observeAttributeTranslation = function (translateAttr) {
2117
+ iAttr.$observe(translateAttr, function (translationId) {
2118
+ translationIds[translateAttr] = translationId;
2119
+ updateTranslations();
2120
+ });
2121
+ };
2122
+
2123
+ var firstAttributeChangedEvent = true;
2124
+ iAttr.$observe('translate', function (translationId) {
2125
+ if (typeof translationId === 'undefined') {
2126
+ // case of element "<translate>xyz</translate>"
2127
+ observeElementTranslation('');
2128
+ } else {
2129
+ // case of regular attribute
2130
+ if (translationId !== '' || !firstAttributeChangedEvent) {
2131
+ translationIds.translate = translationId;
2132
+ updateTranslations();
2133
+ }
2134
+ }
2135
+ firstAttributeChangedEvent = false;
2136
+ });
2137
+
2138
+ for (var translateAttr in iAttr) {
2139
+ if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr') {
2140
+ observeAttributeTranslation(translateAttr);
2141
+ }
2142
+ }
2143
+
2144
+ iAttr.$observe('translateDefault', function (value) {
2145
+ scope.defaultText = value;
2146
+ });
2147
+
2148
+ if (translateValuesExist) {
2149
+ iAttr.$observe('translateValues', function (interpolateParams) {
2150
+ if (interpolateParams) {
2151
+ scope.$parent.$watch(function () {
2152
+ angular.extend(scope.interpolateParams, $parse(interpolateParams)(scope.$parent));
2153
+ });
2154
+ }
2155
+ });
2156
+ }
2157
+
2158
+ if (translateValueExist) {
2159
+ var observeValueAttribute = function (attrName) {
2160
+ iAttr.$observe(attrName, function (value) {
2161
+ var attributeName = angular.lowercase(attrName.substr(14, 1)) + attrName.substr(15);
2162
+ scope.interpolateParams[attributeName] = value;
2163
+ });
2164
+ };
2165
+ for (var attr in iAttr) {
2166
+ if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') {
2167
+ observeValueAttribute(attr);
2168
+ }
2169
+ }
2170
+ }
2171
+
2172
+ // Master update function
2173
+ var updateTranslations = function () {
2174
+ for (var key in translationIds) {
2175
+ if (translationIds.hasOwnProperty(key)) {
2176
+ updateTranslation(key, translationIds[key], scope, scope.interpolateParams, scope.defaultText);
2177
+ }
2178
+ }
2179
+ };
2180
+
2181
+ // Put translation processing function outside loop
2182
+ var updateTranslation = function(translateAttr, translationId, scope, interpolateParams, defaultTranslationText) {
2183
+ if (translationId) {
2184
+ $translate(translationId, interpolateParams, translateInterpolation, defaultTranslationText)
2185
+ .then(function (translation) {
2186
+ applyTranslation(translation, scope, true, translateAttr);
2187
+ }, function (translationId) {
2188
+ applyTranslation(translationId, scope, false, translateAttr);
2189
+ });
2190
+ } else {
2191
+ // as an empty string cannot be translated, we can solve this using successful=false
2192
+ applyTranslation(translationId, scope, false, translateAttr);
2193
+ }
2194
+ };
2195
+
2196
+ var applyTranslation = function (value, scope, successful, translateAttr) {
2197
+ if (translateAttr === 'translate') {
2198
+ // default translate into innerHTML
2199
+ if (!successful && typeof scope.defaultText !== 'undefined') {
2200
+ value = scope.defaultText;
2201
+ }
2202
+ iElement.html(scope.preText + value + scope.postText);
2203
+ var globallyEnabled = $translate.isPostCompilingEnabled();
2204
+ var locallyDefined = typeof tAttr.translateCompile !== 'undefined';
2205
+ var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false';
2206
+ if ((globallyEnabled && !locallyDefined) || locallyEnabled) {
2207
+ $compile(iElement.contents())(scope);
2208
+ }
2209
+ } else {
2210
+ // translate attribute
2211
+ if (!successful && typeof scope.defaultText !== 'undefined') {
2212
+ value = scope.defaultText;
2213
+ }
2214
+ var attributeName = iAttr.$attr[translateAttr].substr(15);
2215
+ iElement.attr(attributeName, value);
2216
+ }
2217
+ };
2218
+
2219
+ scope.$watch('interpolateParams', updateTranslations, true);
2220
+
2221
+ // Ensures the text will be refreshed after the current language was changed
2222
+ // w/ $translate.use(...)
2223
+ var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations);
2224
+
2225
+ // ensure translation will be looked up at least one
2226
+ if (iElement.text().length) {
2227
+ observeElementTranslation('');
2228
+ }
2229
+ updateTranslations();
2230
+ scope.$on('$destroy', unbind);
2231
+ };
2232
+ }
2233
+ };
2234
+ }]);
2235
+
2236
+ angular.module('pascalprecht.translate')
2237
+ /**
2238
+ * @ngdoc directive
2239
+ * @name pascalprecht.translate.directive:translateCloak
2240
+ * @requires $rootScope
2241
+ * @requires $translate
2242
+ * @restrict A
2243
+ *
2244
+ * $description
2245
+ * Adds a `translate-cloak` class name to the given element where this directive
2246
+ * is applied initially and removes it, once a loader has finished loading.
2247
+ *
2248
+ * This directive can be used to prevent initial flickering when loading translation
2249
+ * data asynchronously.
2250
+ *
2251
+ * The class name is defined in
2252
+ * {@link pascalprecht.translate.$translateProvider#cloakClassName $translate.cloakClassName()}.
2253
+ *
2254
+ * @param {string=} translate-cloak If a translationId is provided, it will be used for showing
2255
+ * or hiding the cloak. Basically it relies on the translation
2256
+ * resolve.
2257
+ */
2258
+ .directive('translateCloak', ['$rootScope', '$translate', function ($rootScope, $translate) {
2259
+
2260
+ return {
2261
+ compile: function (tElement) {
2262
+ var applyCloak = function () {
2263
+ tElement.addClass($translate.cloakClassName());
2264
+ },
2265
+ removeCloak = function () {
2266
+ tElement.removeClass($translate.cloakClassName());
2267
+ },
2268
+ removeListener = $rootScope.$on('$translateChangeEnd', function () {
2269
+ removeCloak();
2270
+ removeListener();
2271
+ removeListener = null;
2272
+ });
2273
+ applyCloak();
2274
+
2275
+ return function linkFn(scope, iElement, iAttr) {
2276
+ // Register a watcher for the defined translation allowing a fine tuned cloak
2277
+ if (iAttr.translateCloak && iAttr.translateCloak.length) {
2278
+ iAttr.$observe('translateCloak', function (translationId) {
2279
+ $translate(translationId).then(removeCloak, applyCloak);
2280
+ });
2281
+ }
2282
+ };
2283
+ }
2284
+ };
2285
+ }]);
2286
+
2287
+ angular.module('pascalprecht.translate')
2288
+ /**
2289
+ * @ngdoc filter
2290
+ * @name pascalprecht.translate.filter:translate
2291
+ * @requires $parse
2292
+ * @requires pascalprecht.translate.$translate
2293
+ * @function
2294
+ *
2295
+ * @description
2296
+ * Uses `$translate` service to translate contents. Accepts interpolate parameters
2297
+ * to pass dynamized values though translation.
2298
+ *
2299
+ * @param {string} translationId A translation id to be translated.
2300
+ * @param {*=} interpolateParams Optional object literal (as hash or string) to pass values into translation.
2301
+ *
2302
+ * @returns {string} Translated text.
2303
+ *
2304
+ * @example
2305
+ <example module="ngView">
2306
+ <file name="index.html">
2307
+ <div ng-controller="TranslateCtrl">
2308
+
2309
+ <pre>{{ 'TRANSLATION_ID' | translate }}</pre>
2310
+ <pre>{{ translationId | translate }}</pre>
2311
+ <pre>{{ 'WITH_VALUES' | translate:'{value: 5}' }}</pre>
2312
+ <pre>{{ 'WITH_VALUES' | translate:values }}</pre>
2313
+
2314
+ </div>
2315
+ </file>
2316
+ <file name="script.js">
2317
+ angular.module('ngView', ['pascalprecht.translate'])
2318
+
2319
+ .config(function ($translateProvider) {
2320
+
2321
+ $translateProvider.translations('en', {
2322
+ 'TRANSLATION_ID': 'Hello there!',
2323
+ 'WITH_VALUES': 'The following value is dynamic: {{value}}'
2324
+ });
2325
+ $translateProvider.preferredLanguage('en');
2326
+
2327
+ });
2328
+
2329
+ angular.module('ngView').controller('TranslateCtrl', function ($scope) {
2330
+ $scope.translationId = 'TRANSLATION_ID';
2331
+
2332
+ $scope.values = {
2333
+ value: 78
2334
+ };
2335
+ });
2336
+ </file>
2337
+ </example>
2338
+ */
2339
+ .filter('translate', ['$parse', '$translate', function ($parse, $translate) {
2340
+ var translateFilter = function (translationId, interpolateParams, interpolation) {
2341
+
2342
+ if (!angular.isObject(interpolateParams)) {
2343
+ interpolateParams = $parse(interpolateParams)(this);
2344
+ }
2345
+
2346
+ return $translate.instant(translationId, interpolateParams, interpolation);
2347
+ };
2348
+
2349
+ // Since AngularJS 1.3, filters which are not stateless (depending at the scope)
2350
+ // have to explicit define this behavior.
2351
+ translateFilter.$stateful = true;
2352
+
2353
+ return translateFilter;
2354
+ }]);