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 +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +27 -0
- data/lib/angular-translate-rails.rb +6 -0
- data/test/angular-translate-rails_test.rb +7 -0
- data/test/dummy/Rakefile +3 -0
- data/test/dummy/config/application.rb +22 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/environment.rb +3 -0
- data/test/dummy/config/environments/development.rb +20 -0
- data/test/dummy/config/environments/production.rb +20 -0
- data/test/dummy/config/environments/test.rb +22 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +1 -0
- data/test/dummy/config/initializers/inflections.rb +1 -0
- data/test/dummy/config/initializers/mime_types.rb +1 -0
- data/test/dummy/config/initializers/secret_token.rb +3 -0
- data/test/dummy/config/initializers/session_store.rb +1 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +4 -0
- data/test/dummy/config/locales/en.yml +2 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/remove-config.ru +4 -0
- data/test/integration/resource_test.rb +13 -0
- data/test/test_helper.rb +12 -0
- data/vendor/assets/javascripts/angular-translate.js +2359 -0
- metadata +118 -0
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
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.
|
data/test/dummy/Rakefile
ADDED
@@ -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,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 @@
|
|
1
|
+
Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
|
@@ -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
|
data/test/test_helper.rb
ADDED
@@ -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
|
+
}]);
|