-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlocalization.js
316 lines (298 loc) · 10.8 KB
/
localization.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// @themost-framework 2.0 Codename Blueshift Copyright (c) 2017-2025, THEMOST LP All rights reserved
var AbstractMethodError = require("@themost/common").AbstractMethodError;
var AbstractClassError = require("@themost/common").AbstractClassError;
var LangUtils = require('@themost/common').LangUtils;
var Args = require('@themost/common').Args;
var TraceUtils = require('@themost/common').TraceUtils;
var path = require('path');
var HttpApplicationService = require('./types').HttpApplicationService;
var _ = require('lodash');
var Symbol = require('symbol');
var sprintf = require('sprintf').sprintf;
var i18n = require('i18n');
var optionsProperty = Symbol('options');
/**
* @abstract
* @class
* @constructor
* @param {HttpApplication} app
* @augments HttpApplicationService
*/
function LocalizationStrategy(app) {
LocalizationStrategy.super_.bind(this)(app);
if (this.constructor === LocalizationStrategy.prototype.constructor) {
throw new AbstractClassError();
}
}
LangUtils.inherits(LocalizationStrategy, HttpApplicationService);
/**
* Gets a collection of available cultures
* @abstract
* @public
* @returns {Array.<string>}
*/
LocalizationStrategy.prototype.getCultures = function() {
throw new AbstractMethodError();
};
/**
* Gets the default culture of an HTTP application
* @abstract
* @public
* @returns {string}
*/
LocalizationStrategy.prototype.getDefaultCulture = function() {
throw new AbstractMethodError();
};
/**
* Returns true if the given culture exists in available cultures
* @param {string} culture
* @returns {boolean}
*/
LocalizationStrategy.prototype.hasCulture = function(culture) {
Args.notString(culture,'Culture');
return typeof _.find(this.getCultures(), function(x) {
return x===culture;
}) === 'string';
};
/**
* Returns the localized string of the given string
* @param {string} locale - The target locale
* @param {...string} str - The string or key which is going to be localized
* @abstract
* @returns {*}
*/
// eslint-disable-next-line no-unused-vars
LocalizationStrategy.prototype.getLocaleString = function(locale, str) {
throw new AbstractMethodError();
};
/**
* Sets localization data for the specified locale
* @param {string} locale - A string which represents the target locale
* @param {Object} data - An object which represents a collection of value-key pairs that are going to be used as localization data
* @param {boolean=} shouldMerge - A boolean value which indicates whether the specified localization data will be appended to existing localization data or not.
* @abstract
*/
// eslint-disable-next-line no-unused-vars
LocalizationStrategy.prototype.setLocaleString = function(locale, data, shouldMerge) {
throw new AbstractMethodError();
};
var culturesProperty = Symbol('cultures');
var defaultCultureProperty = Symbol('defaultCulture');
var librariesProperty = Symbol('libraries');
var defaultLibProperty = "global";
/**
* @class
* @constructor
* @param {HttpApplication} app
* @augments LocalizationStrategy
*/
function DefaultLocalizationStrategy(app) {
DefaultLocalizationStrategy.super_.bind(this)(app);
var configuration = this.getApplication().getConfiguration();
this[culturesProperty] = ['en-us'];
this[defaultCultureProperty] = 'en-us';
this[librariesProperty] = {};
if (configuration.settings) {
if (configuration.settings && configuration.settings.hasOwnProperty('localization')) {
/**
* @type {{cultures:Array,default:string}}
*/
var localization = configuration.settings['localization'];
if (_.isArray(localization.cultures))
this[culturesProperty] = localization.cultures;
if (_.isString(localization.default))
this[defaultCultureProperty] = localization.default;
}
}
}
LangUtils.inherits(DefaultLocalizationStrategy, LocalizationStrategy);
/**
* Gets a collection of available cultures
* @public
* @returns {Array.<string>}
*/
DefaultLocalizationStrategy.prototype.getCultures = function() {
return this[culturesProperty];
};
/**
* Gets the default culture of an HTTP application
* @abstract
* @public
* @returns {string}
*/
DefaultLocalizationStrategy.prototype.getDefaultCulture = function() {
return this[defaultCultureProperty];
};
/**
* Resolves localization file path based on the given locale
* @param {string} locale
*/
DefaultLocalizationStrategy.prototype.resolveLocalePath = function(locale) {
return path.resolve(this.getApplication().getExecutionPath(),_.template('locales/global.${locale}.json')({ locale:locale }));
};
/**
* Returns the localized string of the given string
* @param {string} locale - The target locale
* @param {...*} str - The string or key which is going to be localized
* @abstract
* @returns {*}
*/
// eslint-disable-next-line no-unused-vars
DefaultLocalizationStrategy.prototype.getLocaleString = function(locale, str) {
var lib = 'global';
var libraries = this[librariesProperty];
var locLibrary;
var locText;
var args;
if (libraries.hasOwnProperty(lib)) {
locLibrary = libraries[lib];
if (locLibrary.hasOwnProperty(locale)) {
// get localized text
locText = locLibrary[locale][str] || str;
// get arguments for final text
args = Array.prototype.slice.call(arguments, 1);
// replace the first arguments with the localized text
args[0] = locText;
// format text
return sprintf.apply(sprintf, args);
}
}
var libraryFile;
try {
libraryFile = this.resolveLocalePath(locale);
}
catch(err) {
if (err.code === 'ENOENT' || err.code === 'MODULE_NOT_FOUND') {
TraceUtils.debug('Cannot find localization module' + libraryFile + '.');
return str;
}
throw err;
}
try {
if (libraries.hasOwnProperty(lib))
locLibrary = libraries[lib];
else
locLibrary = libraries[lib] = { };
locLibrary[locale] = require(libraryFile);
// get localized text
locText = locLibrary[locale][str] || str;
// get arguments for final text
args = Array.prototype.slice.call(arguments, 1);
// replace the first arguments with the localized text
args[0] = locText;
// format text
return sprintf.apply(sprintf, args);
}
catch (err) {
if (err.code === 'ENOENT' || err.code === 'MODULE_NOT_FOUND') {
TraceUtils.debug('Cannot find localization module' + libraryFile + '.');
return str;
}
throw err;
}
};
/**
* Sets localization data for the specified locale
* @param {string} locale - A string which represents the target locale
* @param {Object} data - An object which represents a collection of value-key pairs that are going to be used as localization data
* @param {boolean=} shouldMerge - A boolean value which indicates whether the specified localization data will be appended to existing localization data or not.
* If parameter is missing the specified data will be merged.
*/
DefaultLocalizationStrategy.prototype.setLocaleString = function(locale, data, shouldMerge) {
var libraries = this[librariesProperty];
//check if the given locale exists in application locales
if (this.getCultures().indexOf(locale)<0) {
throw new Error('Invalid locale. The specified locale does not exist in application locales.');
}
//validate locale data libraries["global"]["en-us"]
libraries[defaultLibProperty] = libraries[defaultLibProperty] || {};
libraries[defaultLibProperty][locale] = libraries[defaultLibProperty][locale] || {};
if (typeof shouldMerge === 'undefined' || shouldMerge) {
_.assign(libraries[defaultLibProperty][locale], data);
}
else {
libraries[defaultLibProperty][locale] = data;
}
};
/**
* @class
* @constructor
* @param {HttpApplication} app
* @augments LocalizationStrategy
*/
function I18nLocalizationStrategy(app) {
I18nLocalizationStrategy.super_.bind(this)(app);
// get application locales and default locales
var configuration = this.getApplication().getConfiguration();
// get i18n options
var options = _.assign({
"locales": [ "en" ],
"defaultLocale": "en",
"directory": path.resolve(this.getApplication().getExecutionPath(),'i18n'),
"autoReload": false
}, configuration.getSourceAt('settings/i18n'));
// set options
this[culturesProperty] = options.locales;
this[defaultCultureProperty] = options.defaultLocale;
i18n.configure(options);
}
LangUtils.inherits(I18nLocalizationStrategy, DefaultLocalizationStrategy);
/**
* Returns the localized string of the given string
* @param {string} locale - The target locale
* @param {...*} str - The string or key which is going to be localized
* @abstract
* @returns {string}
*/
/* eslint-disable-next-line no-unused-vars */
I18nLocalizationStrategy.prototype.getLocaleString = function(locale, str) {
return i18n.__.apply({
locale: locale
}, Array.prototype.slice.call(arguments, 1));
};
I18nLocalizationStrategy.prototype.resolveLocalePath = function(locale) {
if (typeof locale !== 'string') {
throw new Error('Invalid locale parameter. Expected string.');
}
return path.resolve(this.getApplication().getExecutionPath(),_.template('i18n/${locale}.json')({ locale:locale.substr(0,2) }));
};
/**
* Gets an array of strings which represents the available cultures
* @returns {Array<string>}
*/
I18nLocalizationStrategy.prototype.getCultures = function() {
return this[culturesProperty];
};
/**
* Gets the default culture of an HTTP application
* @abstract
* @public
* @returns {string}
*/
I18nLocalizationStrategy.prototype.getDefaultCulture = function() {
return this[defaultCultureProperty];
};
/**
* Sets localization data for the specified locale
* @param {string} locale - A string which represents the target locale
* @param {Object} data - An object which represents a collection of value-key pairs that are going to be used as localization data
* @param {boolean=} shouldMerge - A boolean value which indicates whether the specified localization data will be appended to existing localization data or not.
* If parameter is missing the specified data will be merged.
*/
I18nLocalizationStrategy.prototype.setLocaleString = function(locale, data, shouldMerge) {
// get catalog
var catalog = i18n.getCatalog(locale);
// if data should be merged
if (typeof shouldMerge === 'undefined' || shouldMerge) {
_.assign(catalog, data);
}
else {
_.assign(catalog, data);
}
};
if (typeof exports !== 'undefined')
{
module.exports.LocalizationStrategy = LocalizationStrategy;
module.exports.DefaultLocalizationStrategy = DefaultLocalizationStrategy;
module.exports.I18nLocalizationStrategy = I18nLocalizationStrategy;
}