@ornery/web-components 1.1.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/data-manager.js +50 -51
- package/src/i18n.js +207 -287
- package/src/utils.js +44 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ornery/web-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "WebComponents html loader and optional runtime mixins to enable creation of custom HTML elements using es6 template literal syntax in *.html files.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
package/src/data-manager.js
CHANGED
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
const EventMap = require('./event-map');
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
/**
|
|
3
|
+
/**
|
|
5
4
|
* @class DataStore
|
|
6
5
|
* @description Configuration values can be set and propagated to consuming
|
|
7
6
|
* components via this static class or through
|
|
8
7
|
* the corresponding wc-config element
|
|
9
8
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
class DataManager {
|
|
10
|
+
constructor() {
|
|
11
|
+
this._state = new EventMap();
|
|
12
|
+
}
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
/**
|
|
16
15
|
* @memberOf DataStore
|
|
17
16
|
* @param {String} key
|
|
18
17
|
* @return {Object} the current value of the requested property name.
|
|
19
18
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
get(key) {
|
|
20
|
+
return key ? this._state.get(key) : this._state.getAll();
|
|
21
|
+
}
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
/**
|
|
25
24
|
* @memberOf DataStore
|
|
26
25
|
* @return {Object} the current state object.
|
|
27
26
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
getState() {
|
|
28
|
+
return this._state.getAll();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
32
31
|
* @memberOf DataStore
|
|
33
32
|
* @param {String|Object} key the name of the value to set.
|
|
34
33
|
* It can also be called with an {} query to set multiple values at once.
|
|
@@ -36,59 +35,59 @@ module.exports = (() => {
|
|
|
36
35
|
* @return {{state}|*}
|
|
37
36
|
* @description wraps this.set
|
|
38
37
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
return this._state.replace({...this._state.getAll(), ...query});
|
|
38
|
+
set(key, value) {
|
|
39
|
+
let query = key;
|
|
40
|
+
if (value) {
|
|
41
|
+
// we have a single value
|
|
42
|
+
query = {[key]: value};
|
|
46
43
|
}
|
|
44
|
+
return this._state.replace({...this._state.getAll(), ...query});
|
|
45
|
+
}
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
/**
|
|
49
48
|
* @memberOf DataStore
|
|
50
49
|
* @param {Object} newState the new state object.
|
|
51
50
|
* @return {{state}|*}
|
|
52
51
|
* @description wraps this.set
|
|
53
52
|
*/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
setState(newState) {
|
|
54
|
+
return this.set(newState);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
58
57
|
* @memberOf DataStore
|
|
59
58
|
* @param {Function} callback is the function to execute when any property changes.
|
|
60
59
|
* @return {{destroy}|*}
|
|
61
60
|
* @description call destroy() on the returned object to remove the event listener.
|
|
62
61
|
*/
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
subscribe(callback) {
|
|
63
|
+
return callback && this._state.on('set', callback);
|
|
64
|
+
}
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
/**
|
|
68
67
|
* @memberOf DataStore
|
|
69
68
|
* @param {Array} keys the property names to be notified when they mutate
|
|
70
69
|
* @param {Function} callback the callback to be executed when any of the value for any of those keys have changed.
|
|
71
70
|
* @return {{destroy}|*}
|
|
72
71
|
* @description call destroy() on the returned object to remove the event listener.
|
|
73
72
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
89
|
-
updates && callback(updates, newState, oldState);
|
|
73
|
+
subscribeTo(keys, callback) {
|
|
74
|
+
keys = typeof (keys) === 'string' ? [keys] : keys;
|
|
75
|
+
return this.subscribe((event, newState, oldState) => {
|
|
76
|
+
let updates;
|
|
77
|
+
keys.forEach((property) => {
|
|
78
|
+
if (newState[property] !== oldState[property]) {
|
|
79
|
+
updates = {
|
|
80
|
+
...updates,
|
|
81
|
+
[property]: {
|
|
82
|
+
oldValue: oldState[property],
|
|
83
|
+
newValue: newState[property],
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
90
87
|
});
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
88
|
+
updates && callback(updates, newState, oldState);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = DataManager;
|
package/src/i18n.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
const DataManager = require(
|
|
2
|
-
const {
|
|
3
|
-
const withContext = require("./context-binding");
|
|
1
|
+
const DataManager = require('./data-manager');
|
|
2
|
+
const {template, getFromObj, toLowerMap} = require('./utils');
|
|
4
3
|
|
|
5
|
-
if (typeof HTMLElement ===
|
|
6
|
-
|
|
4
|
+
if (typeof HTMLElement === 'undefined') {
|
|
5
|
+
// eslint-disable-next-line no-global-assign
|
|
6
|
+
HTMLElement = class {};
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
*
|
|
11
11
|
* @class I18n
|
|
12
12
|
* @description Import strings here and call I18n.addStrings() with the supported locale identifier
|
|
13
|
-
* and the strings object exported from the
|
|
13
|
+
* and the strings object exported from the locale file
|
|
14
14
|
* By default, it will set the values on window.i18n, if defined when loaded, as the starting messages.
|
|
15
15
|
* This is useful if you wish to server-side render HTML certain content before laoding scripts on the client.
|
|
16
16
|
* @example
|
|
@@ -36,296 +36,216 @@ if (typeof HTMLElement === "undefined") {
|
|
|
36
36
|
* console.log(I18n.getMessages()) // will log the window.i18n object
|
|
37
37
|
*
|
|
38
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
|
-
setDefaultLang(lang = "en") {
|
|
65
|
-
this._defaultLang = lang.toLowerCase();
|
|
66
|
-
this._fallbackMessages = this.getMessages(this._defaultLang);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* @memberof I18n
|
|
70
|
-
* @return {String} lang
|
|
71
|
-
* @description returns the current language. defaults to the browser's navigator.language value.
|
|
72
|
-
* @example
|
|
73
|
-
*
|
|
74
|
-
* navigator.language = 'en-US';
|
|
75
|
-
* import { I18n } from '@ornery/web-components';
|
|
76
|
-
*
|
|
77
|
-
* console.log(I18n.getLang()) // "en-US"
|
|
78
|
-
*/
|
|
79
|
-
getLang() {
|
|
80
|
-
return DataManager.get("i18n-language") || '';
|
|
81
|
-
}
|
|
82
|
-
getRootLang() {
|
|
83
|
-
return this.getLang().split(this._altLangRegex)[0];
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* @memberof I18n
|
|
87
|
-
* @param {String} lang
|
|
88
|
-
* @description sets the current i18n language. This does not change the browser language.
|
|
89
|
-
* @example
|
|
90
|
-
*
|
|
91
|
-
* import { I18n } from '@ornery/web-components';
|
|
92
|
-
*
|
|
93
|
-
* I18n.setLang('en-US')
|
|
94
|
-
* console.log(I18n.getLang()) //'en-US'
|
|
95
|
-
*/
|
|
96
|
-
setLang(lang= "") {
|
|
97
|
-
return DataManager.set("i18n-language", lang)
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* @memberof I18n
|
|
101
|
-
* @return {String} lang
|
|
102
|
-
* @description returns the current i18n messages set in the DataManager
|
|
103
|
-
*/
|
|
104
|
-
getMessages(lang) {
|
|
105
|
-
const allMessages = DataManager.get("i18n-messages") || {};
|
|
106
|
-
if (lang === "all") {
|
|
107
|
-
return allMessages;
|
|
108
|
-
} else {
|
|
109
|
-
lang = lang || this.getLang();
|
|
110
|
-
const rootLang = lang
|
|
111
|
-
? lang.split(this._altLangRegex)[0]
|
|
112
|
-
: this.getRootLang();
|
|
113
|
-
return {
|
|
114
|
-
...allMessages[rootLang],
|
|
115
|
-
...allMessages[lang],
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* @memberof I18n
|
|
121
|
-
* @param {Object} values
|
|
122
|
-
* @description sets the strings as a whole. This overrides all existing strings.
|
|
123
|
-
* Use addMessages to add more strings to the existing set.
|
|
124
|
-
* @example
|
|
125
|
-
*
|
|
126
|
-
* import { I18n } from '@ornery/web-components';
|
|
127
|
-
*
|
|
128
|
-
* I18n.setMessages({
|
|
129
|
-
* 'en-US': {
|
|
130
|
-
* 'translatable.message.name': "I'm a translated string from i18n",
|
|
131
|
-
* 'tokenized.message': "I have a ${this.color} ${this.animal}"
|
|
132
|
-
* }
|
|
133
|
-
* })
|
|
134
|
-
*/
|
|
135
|
-
setMessages(values) {
|
|
136
|
-
return DataManager.set("i18n-messages", values);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* @memberof I18n
|
|
140
|
-
* @param {String} lang
|
|
141
|
-
* @param {Object} strings
|
|
142
|
-
* @description add more strings to the existing language set.
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
*
|
|
146
|
-
* import { I18n } from '@ornery/web-components';
|
|
147
|
-
* I18n.addMessages('en-US', {
|
|
148
|
-
* 'translatable.message.name': "I'm a translated string from i18n",
|
|
149
|
-
* 'tokenized.message': "I have a ${color} ${animal}"
|
|
150
|
-
* });
|
|
151
|
-
*/
|
|
152
|
-
addMessages(lang, newStrings) {
|
|
153
|
-
if (typeof lang !== "string") {
|
|
154
|
-
newStrings = lang;
|
|
155
|
-
lang = this.getLang();
|
|
156
|
-
}
|
|
157
|
-
lang = lang.toLowerCase();
|
|
158
|
-
const rootLang = lang.split(this._altLangRegex)[0];
|
|
159
|
-
const existing = this.getMessages("all");
|
|
160
|
-
existing[rootLang] = {
|
|
161
|
-
...(existing[rootLang] || {}),
|
|
162
|
-
...newStrings,
|
|
163
|
-
};
|
|
164
|
-
existing[lang] = {
|
|
165
|
-
...(existing[lang] || {}),
|
|
166
|
-
...newStrings,
|
|
167
|
-
};
|
|
168
|
-
this.setMessages(existing);
|
|
169
|
-
if (lang === this._defaultLang) {
|
|
170
|
-
this._fallbackMessages = this.getMessages(lang);
|
|
171
|
-
} else if (rootLang === this._defaultLang) {
|
|
172
|
-
this._fallbackMessages = this.getMessages(rootLang);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* @memberof I18n
|
|
178
|
-
* @param {String} key they key of the string to retrieve from the current language set.
|
|
179
|
-
* @param {Object} data Optional, The data to process tokens in the string with.
|
|
180
|
-
* @return {String} Returns the value for the key. Processed if a data context is provided as the second argument.
|
|
181
|
-
* @description Returns the value for the key. If a context is provided as the second argument for tokens,
|
|
182
|
-
* the tokens will be replaced in the returned string.
|
|
183
|
-
* @example
|
|
184
|
-
*
|
|
185
|
-
* import { I18n } from '@ornery/web-components';
|
|
186
|
-
* I18n.addMessages('en-US', {
|
|
187
|
-
* 'tokenized.message': "I have a ${color} ${animal}"
|
|
188
|
-
* });
|
|
189
|
-
*
|
|
190
|
-
* const stringTestData = {
|
|
191
|
-
* color: "grey",
|
|
192
|
-
* animal: "monkey"
|
|
193
|
-
* };
|
|
194
|
-
* console.log(I18n.get('tokenized.message', stringTestData)) // "I have a grey monkey"
|
|
195
|
-
*/
|
|
196
|
-
get(key, data = {}) {
|
|
197
|
-
const context = {
|
|
198
|
-
...this._fallbackMessages,
|
|
199
|
-
...this.getMessages(),
|
|
200
|
-
...data,
|
|
201
|
-
};
|
|
202
|
-
return template(getFromObj(key, context), context);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* @memberof I18n
|
|
207
|
-
* @param {String} namespace
|
|
208
|
-
* @return {Object} Returns all the messages for the given language. Filtered to namespace if provided.
|
|
209
|
-
* @description If a namespace is provided, returns all the key value pairs for that
|
|
210
|
-
* namespace without the namespace in the keys.
|
|
211
|
-
*
|
|
212
|
-
* @example
|
|
213
|
-
*
|
|
214
|
-
* import { I18n } from '@ornery/web-components';
|
|
215
|
-
* I18n.addMessages('en-US', {
|
|
216
|
-
* 'translatable.message.name': "I'm a translated string from i18n",
|
|
217
|
-
* 'tokenized.message': "I have a ${color} ${animal}"
|
|
218
|
-
* });
|
|
219
|
-
*
|
|
220
|
-
* console.log(I18n.getAll('tokenized', {color: "red", animal: "panda"})) // {"message": "I have a red panda"}
|
|
221
|
-
*/
|
|
222
|
-
getAll(namespace, data) {
|
|
223
|
-
if (namespace) {
|
|
224
|
-
return Object.keys(this.getMessages()).reduce((reducer, key) => {
|
|
225
|
-
if (key.startsWith(namespace)) {
|
|
226
|
-
reducer[key.replace(namespace + ".", "")] = this.get(key, data);
|
|
227
|
-
}
|
|
228
|
-
return reducer;
|
|
229
|
-
}, {});
|
|
230
|
-
} else {
|
|
231
|
-
return this.getMessages("all");
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
})();
|
|
39
|
+
class I18n {
|
|
40
|
+
constructor(options) {
|
|
41
|
+
const {
|
|
42
|
+
store = new DataManager(),
|
|
43
|
+
messages = null,
|
|
44
|
+
locale = null,
|
|
45
|
+
fallbackLocale = {
|
|
46
|
+
'default': 'en',
|
|
47
|
+
'de-ch': ['fr', 'it'],
|
|
48
|
+
'zh-hant': ['zh-hans'],
|
|
49
|
+
'es-cl': ['es-ar'],
|
|
50
|
+
'es': ['en'],
|
|
51
|
+
'pt': ['es-ar'],
|
|
52
|
+
},
|
|
53
|
+
} = options;
|
|
54
|
+
this.store = store;
|
|
55
|
+
this.setFallbackLocale(fallbackLocale);
|
|
56
|
+
if (locale) this.setLocale(locale);
|
|
57
|
+
if (messages) this.setMessages(messages);
|
|
58
|
+
}
|
|
235
59
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
* <i18n-message data-values="{'color: 'grey', 'animal': 'monkey'}">tokenized.message</i18n-message>
|
|
254
|
-
* <i18n-message data-color="grey" data-animal="monkey">tokenized.message</i18n-message>
|
|
255
|
-
* <i18n-message key="tokenized.message"/>
|
|
256
|
-
* <!-- React does not pass key or ref props so you can use "data-key" or "data-id" as well-->
|
|
257
|
-
* <i18n-message data-key="tokenized.message"/>
|
|
258
|
-
* <i18n-message id="translatable.message.name"/>
|
|
259
|
-
* <i18n-message data-id="translatable.message.name"/>
|
|
260
|
-
* </div>
|
|
261
|
-
*
|
|
262
|
-
* @example @lang html <caption>Renders the HTML</caption>
|
|
263
|
-
* <i18n-message>I'm a translated string from i18n</i18n-message>
|
|
264
|
-
* <i18n-message>I have a grey monkey</i18n-message>
|
|
265
|
-
*/
|
|
266
|
-
class I18nMessage extends HTMLElement {
|
|
267
|
-
constructor() {
|
|
268
|
-
super();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
static get observedAttributes() {
|
|
272
|
-
return ["key", "id", "data-values"];
|
|
273
|
-
}
|
|
60
|
+
setFallbackLocale(fallbackLocales) {
|
|
61
|
+
this._fallbackLocale = toLowerMap(fallbackLocales);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @memberof I18n
|
|
65
|
+
* @return {String} locale
|
|
66
|
+
* @description returns the current locale. defaults to the browser's navigator.locale value.
|
|
67
|
+
* @example
|
|
68
|
+
*
|
|
69
|
+
* navigator.locale = 'en-US';
|
|
70
|
+
* import { I18n } from '@ornery/web-components';
|
|
71
|
+
*
|
|
72
|
+
* console.log(I18n.getLocale()) // "en-US"
|
|
73
|
+
*/
|
|
74
|
+
getLocale() {
|
|
75
|
+
return this.store.get('i18n-locale') || '';
|
|
76
|
+
}
|
|
274
77
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
78
|
+
getFallbackLocale() {
|
|
79
|
+
const locale = this.getLocale();
|
|
80
|
+
const defaultLocale = this._fallbackLocale.default;
|
|
81
|
+
const fallbackMap = this._fallbackLocale[locale];
|
|
82
|
+
let fallbackLocale;
|
|
83
|
+
if (fallbackMap) {
|
|
84
|
+
const allMessages = this.store.get('i18n-messages') || {};
|
|
85
|
+
for (let i = 0; i < fallbackMap.length; i++) {
|
|
86
|
+
const fbl = fallbackMap[i];
|
|
87
|
+
if (allMessages[fbl]) {
|
|
88
|
+
fallbackLocale = fbl;
|
|
89
|
+
break;
|
|
281
90
|
}
|
|
282
|
-
|
|
283
|
-
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return fallbackLocale || defaultLocale || 'en';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* @memberof I18n
|
|
97
|
+
* @param {String} locale
|
|
98
|
+
* @return {String} locale
|
|
99
|
+
* @description sets the current i18n locale. This does not change the browser locale.
|
|
100
|
+
* @example
|
|
101
|
+
*
|
|
102
|
+
* import { I18n } from '@ornery/web-components';
|
|
103
|
+
*
|
|
104
|
+
* I18n.setLocale('en-US')
|
|
105
|
+
* console.log(I18n.getLocale()) //'en-US'
|
|
106
|
+
*/
|
|
107
|
+
setLocale(locale = '') {
|
|
108
|
+
return this.store.set('i18n-locale', locale);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* @memberof I18n
|
|
112
|
+
* @param {String} locale
|
|
113
|
+
* @return {String} locale
|
|
114
|
+
* @description returns the current i18n messages set in the DataManager
|
|
115
|
+
*/
|
|
116
|
+
getMessages(locale = null) {
|
|
117
|
+
const allMessages = this.store.get('i18n-messages') || {};
|
|
118
|
+
if (locale === 'all') {
|
|
119
|
+
return allMessages;
|
|
120
|
+
} else {
|
|
121
|
+
locale = locale || this.getLocale();
|
|
122
|
+
const fallbackLocale = this.getFallbackLocale();
|
|
123
|
+
return {
|
|
124
|
+
...allMessages[fallbackLocale] || {},
|
|
125
|
+
...allMessages[locale] || {},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* @memberof I18n
|
|
131
|
+
* @param {Object} values
|
|
132
|
+
* @return {{state}|*}
|
|
133
|
+
* @description sets the strings as a whole. This overrides all existing strings.
|
|
134
|
+
* Use addMessages to add more strings to the existing set.
|
|
135
|
+
* @example
|
|
136
|
+
*
|
|
137
|
+
* import { I18n } from '@ornery/web-components';
|
|
138
|
+
*
|
|
139
|
+
* I18n.setMessages({
|
|
140
|
+
* 'en-US': {
|
|
141
|
+
* 'translatable.message.name': "I'm a translated string from i18n",
|
|
142
|
+
* 'tokenized.message': "I have a ${this.color} ${this.animal}"
|
|
143
|
+
* }
|
|
144
|
+
* })
|
|
145
|
+
*/
|
|
146
|
+
setMessages(values) {
|
|
147
|
+
const response = this.store.set('i18n-messages', values);
|
|
148
|
+
return response;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* @memberof I18n
|
|
152
|
+
* @param {{String}|{Object}} locale
|
|
153
|
+
* @param {Object} newStrings
|
|
154
|
+
* @description add more strings to the existing locale set.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
*
|
|
158
|
+
* import { I18n } from '@ornery/web-components';
|
|
159
|
+
* I18n.addMessages('en-US', {
|
|
160
|
+
* 'translatable.message.name': "I'm a translated string from i18n",
|
|
161
|
+
* 'tokenized.message': "I have a ${color} ${animal}"
|
|
162
|
+
* });
|
|
163
|
+
*/
|
|
164
|
+
addMessages(locale, newStrings) {
|
|
165
|
+
if (typeof locale !== 'string') {
|
|
166
|
+
newStrings = locale;
|
|
167
|
+
locale = this.getLocale();
|
|
168
|
+
}
|
|
169
|
+
locale = locale.toLowerCase();
|
|
170
|
+
const fallbackLocale = this.getFallbackLocale();
|
|
171
|
+
const existing = this.getMessages('all');
|
|
172
|
+
existing[locale] = {
|
|
173
|
+
...(existing[locale] || {}),
|
|
174
|
+
...newStrings,
|
|
175
|
+
};
|
|
176
|
+
if (fallbackLocale !== locale) {
|
|
177
|
+
existing[fallbackLocale] = {
|
|
178
|
+
...(existing[fallbackLocale] || {}),
|
|
179
|
+
...newStrings,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
this.setMessages(existing);
|
|
183
|
+
}
|
|
284
184
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
185
|
+
/**
|
|
186
|
+
* @memberof I18n
|
|
187
|
+
* @param {String} key they key of the string to retrieve from the current locale set.
|
|
188
|
+
* @param {Object} data Optional, The data to process tokens in the string with.
|
|
189
|
+
* @return {String} Returns the value for the key. Processed if a data context is provided as the second argument.
|
|
190
|
+
* @description Returns the value for the key. If a context is provided as the second argument for tokens,
|
|
191
|
+
* the tokens will be replaced in the returned string.
|
|
192
|
+
* @example
|
|
193
|
+
*
|
|
194
|
+
* import { I18n } from '@ornery/web-components';
|
|
195
|
+
* I18n.addMessages('en-US', {
|
|
196
|
+
* 'tokenized.message': "I have a ${color} ${animal}"
|
|
197
|
+
* });
|
|
198
|
+
*
|
|
199
|
+
* const stringTestData = {
|
|
200
|
+
* color: "grey",
|
|
201
|
+
* animal: "monkey"
|
|
202
|
+
* };
|
|
203
|
+
* console.log(I18n.get('tokenized.message', stringTestData)) // "I have a grey monkey"
|
|
204
|
+
*/
|
|
205
|
+
get(key, data = {}) {
|
|
206
|
+
const context = {
|
|
207
|
+
...this.getMessages(),
|
|
208
|
+
...data,
|
|
209
|
+
};
|
|
210
|
+
return template(getFromObj(key, context), context);
|
|
211
|
+
}
|
|
288
212
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
213
|
+
/**
|
|
214
|
+
* @memberof I18n
|
|
215
|
+
* @param {String} namespace
|
|
216
|
+
* @param {String} context
|
|
217
|
+
* @return {Object} Returns all the messages for the given locale. Filtered to namespace if provided.
|
|
218
|
+
* @description If a namespace is provided, returns all the key value pairs for that
|
|
219
|
+
* namespace without the namespace in the keys.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
*
|
|
223
|
+
* import { I18n } from '@ornery/web-components';
|
|
224
|
+
* I18n.addMessages('en-US', {
|
|
225
|
+
* 'translatable.message.name': "I'm a translated string from i18n",
|
|
226
|
+
* 'tokenized.message': "I have a ${color} ${animal}"
|
|
227
|
+
* });
|
|
228
|
+
*
|
|
229
|
+
* console.log(I18n.getAll('tokenized', {color: "red", animal: "panda"})) // {"message": "I have a red panda"}
|
|
230
|
+
*/
|
|
231
|
+
getAll(namespace, context) {
|
|
232
|
+
if (namespace) {
|
|
233
|
+
return Object.keys(this.getMessages()).reduce((reducer, key) => {
|
|
234
|
+
if (key.startsWith(namespace)) {
|
|
235
|
+
reducer[key.replace(namespace + '.', '')] = this.get(key, context);
|
|
299
236
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
connectedCallback() {
|
|
308
|
-
if (this.useShadow && !this.shadowRoot) this.attachShadow({ mode: "open" });
|
|
309
|
-
this._i18nListener = DataManager.subscribe((newVals) => {
|
|
310
|
-
this.update();
|
|
311
|
-
});
|
|
312
|
-
const attrObserver = new MutationObserver(() => this.update());
|
|
313
|
-
attrObserver.observe(this, { attributes: true, childList: this.useShadow });
|
|
237
|
+
return reducer;
|
|
238
|
+
}, {});
|
|
239
|
+
} else {
|
|
240
|
+
return this.getMessages('all');
|
|
314
241
|
}
|
|
242
|
+
}
|
|
315
243
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
this._i18nListener = null;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (typeof window !== "undefined" && typeof customElements !== "undefined" && !customElements.get("i18n-message")) {
|
|
325
|
-
customElements.define("i18n-message", withContext(I18nMessage));
|
|
244
|
+
subscribe(callback) {
|
|
245
|
+
return this.store.subscribe(callback);
|
|
246
|
+
}
|
|
326
247
|
}
|
|
327
248
|
|
|
328
249
|
module.exports = {
|
|
329
|
-
|
|
330
|
-
I18nMessage,
|
|
250
|
+
I18n,
|
|
331
251
|
};
|
package/src/utils.js
CHANGED
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
*
|
|
15
15
|
* result == 'bar';
|
|
16
16
|
*/
|
|
17
|
-
const keyRegexp = /^[\w
|
|
17
|
+
const keyRegexp = /^[\w-]+(\.[\w-]+)+$/g;
|
|
18
18
|
const getFromObj = (path, obj = {}) => {
|
|
19
|
-
path = path.trim();
|
|
20
|
-
if (path != null){
|
|
19
|
+
path = path && path.trim();
|
|
20
|
+
if (path != null) {
|
|
21
21
|
if (obj[path] != null) {
|
|
22
22
|
return obj[path];
|
|
23
23
|
} else if (keyRegexp.test(path)) {
|
|
24
24
|
return path.split('.').reduce((res, key) => res[key] != null ? res[key] : path, obj);
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
}
|
|
27
27
|
return path;
|
|
28
28
|
};
|
|
@@ -44,9 +44,9 @@ const stripES6 = function(expr, context) {
|
|
|
44
44
|
let result = expr.replace(thisRegex, '');
|
|
45
45
|
let matchArr;
|
|
46
46
|
while (matchArr = nestedES6.exec(result)) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const [, outerMatch, key] = matchArr;
|
|
48
|
+
const replacement = getFromObj(key, context);
|
|
49
|
+
result = stripES6(result.replace(outerMatch, replacement).trim(), context);
|
|
50
50
|
}
|
|
51
51
|
return result.replace(es6Regex, (match, $1)=> getFromObj($1, context));
|
|
52
52
|
};
|
|
@@ -104,12 +104,12 @@ const template = stripES6;
|
|
|
104
104
|
const arrayParser = (val, key, params) => {
|
|
105
105
|
let current = params[key];
|
|
106
106
|
if (current) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
if (!Array.isArray(current)) {
|
|
108
|
+
current = [current];
|
|
109
|
+
}
|
|
110
|
+
current.push(val);
|
|
111
111
|
} else {
|
|
112
|
-
|
|
112
|
+
current = val;
|
|
113
113
|
}
|
|
114
114
|
return current;
|
|
115
115
|
};
|
|
@@ -144,12 +144,12 @@ const toParams = (str, options = {}) => {
|
|
|
144
144
|
const queryString = parts[1] || '';
|
|
145
145
|
const params = {};
|
|
146
146
|
queryString.split('&').forEach((val) => {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
147
|
+
const innerParts = val.split('=');
|
|
148
|
+
if (innerParts.length !== 2) return;
|
|
149
|
+
const paramKey = decodeURIComponent(innerParts[0]);
|
|
150
|
+
const paramVal = decodeURIComponent(innerParts[1]);
|
|
151
|
+
const parser = options[paramKey] || (() => paramVal);
|
|
152
|
+
params[paramKey] = arrayParser(parser(paramVal, paramKey, params), paramKey, params);
|
|
153
153
|
});
|
|
154
154
|
return params;
|
|
155
155
|
};
|
|
@@ -176,11 +176,11 @@ const toParams = (str, options = {}) => {
|
|
|
176
176
|
const toSearch = (options) => {
|
|
177
177
|
const filtered = Object.entries(options).filter((ent) => !!ent[1]);
|
|
178
178
|
return encodeURI(`?${filtered.map((ent) => {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
179
|
+
if (Array.isArray(ent[1])) {
|
|
180
|
+
return ent[1].map((val) => [ent[0], val].join('=')).join('&');
|
|
181
|
+
} else {
|
|
182
|
+
return ent.join('=');
|
|
183
|
+
}
|
|
184
184
|
}).join('&')}`);
|
|
185
185
|
};
|
|
186
186
|
|
|
@@ -210,13 +210,13 @@ const toSearch = (options) => {
|
|
|
210
210
|
const prefixKeys = (obj, prefix) => {
|
|
211
211
|
let keys = [];
|
|
212
212
|
if (Array.isArray(obj)) {
|
|
213
|
-
|
|
213
|
+
keys = obj.map((val, i) => i);
|
|
214
214
|
} else {
|
|
215
|
-
|
|
215
|
+
keys = Object.keys(obj);
|
|
216
216
|
}
|
|
217
217
|
return Object.assign(
|
|
218
218
|
{},
|
|
219
|
-
...keys.map((key) => ({[prefix + key]: obj[key]}))
|
|
219
|
+
...keys.map((key) => ({[prefix + key]: obj[key]})),
|
|
220
220
|
);
|
|
221
221
|
};
|
|
222
222
|
|
|
@@ -257,16 +257,25 @@ const toDataAttrs = (obj) => {
|
|
|
257
257
|
return prefixKeys(obj, 'data-');
|
|
258
258
|
};
|
|
259
259
|
const HTMLEncodable = /[\u00A0-\u9999<>]/g;
|
|
260
|
-
const encodeHTML = (stringVal =
|
|
260
|
+
const encodeHTML = (stringVal = '') => stringVal.replace(HTMLEncodable, (i) => `&#${i.charCodeAt(0)};`);
|
|
261
261
|
|
|
262
262
|
const withClosing = /<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi;
|
|
263
263
|
const selfClosing = /(<([^>]+)\/>)/ig;
|
|
264
|
-
const shouldEncode = (str) =>
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
264
|
+
const shouldEncode = (str) => (str || '').replace(withClosing, '').replace(selfClosing, '').trim();
|
|
265
|
+
|
|
266
|
+
const toLowerMap = (obj = {}) => {
|
|
267
|
+
if (Array.isArray(obj)) {
|
|
268
|
+
return obj.map(toLowerMap);
|
|
269
|
+
}
|
|
270
|
+
if (typeof obj === 'string') {
|
|
271
|
+
return obj.toLowerCase();
|
|
272
|
+
}
|
|
273
|
+
return Object.entries(obj).reduce((acc, [key, val]) => {
|
|
274
|
+
const lck = key.toLowerCase();
|
|
275
|
+
acc[lck] = toLowerMap(val);
|
|
276
|
+
return acc;
|
|
277
|
+
}, {});
|
|
278
|
+
};
|
|
270
279
|
|
|
271
280
|
module.exports = {
|
|
272
281
|
getFromObj,
|
|
@@ -278,5 +287,6 @@ module.exports = {
|
|
|
278
287
|
prefixKeys,
|
|
279
288
|
toDataAttrs,
|
|
280
289
|
shouldEncode,
|
|
281
|
-
encodeHTML
|
|
290
|
+
encodeHTML,
|
|
291
|
+
toLowerMap,
|
|
282
292
|
};
|