@ornery/web-components 1.1.8 → 4.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/src/i18n.js CHANGED
@@ -1,17 +1,15 @@
1
- const DataManager = require('./data-manager');
2
- const {template, getFromObj, shouldEncode, encodeHTML} = require('./utils');
3
- const withContext = require('./context-binding');
1
+ import DataManager from './data-manager.js';
2
+ import {template, getFromObj, toLowerMap} from './utils.js';
4
3
 
5
- if (typeof HTMLElement === 'undefined') {
6
- // eslint-disable-next-line no-global-assign
7
- HTMLElement = class {};
4
+ if (typeof globalThis.HTMLElement === 'undefined') {
5
+ globalThis.HTMLElement = class {};
8
6
  }
9
7
 
10
8
  /**
11
9
  *
12
10
  * @class I18n
13
11
  * @description Import strings here and call I18n.addStrings() with the supported locale identifier
14
- * and the strings object exported from the language file
12
+ * and the strings object exported from the locale file
15
13
  * By default, it will set the values on window.i18n, if defined when loaded, as the starting messages.
16
14
  * This is useful if you wish to server-side render HTML certain content before laoding scripts on the client.
17
15
  * @example
@@ -37,84 +35,93 @@ if (typeof HTMLElement === 'undefined') {
37
35
  * console.log(I18n.getMessages()) // will log the window.i18n object
38
36
  *
39
37
  */
40
- const I18n = new (class {
41
- constructor() {
42
- // language without region code;
43
- this._altLangRegex = /[_-]/i;
44
- this.setDefaultLang();
45
- this.setLang();
46
- if (typeof window !== 'undefined') {
47
- if (typeof navigator !== 'undefined') {
48
- this.setLang(navigator.language);
49
- }
50
- this.setMessages(window.i18n || {});
51
- } else {
52
- if (typeof process !== 'undefined') {
53
- const {env} = process;
54
- this.setLang(
55
- env.LANG ||
56
- env.LANGUAGE ||
57
- env.LC_ALL ||
58
- env.LC_MESSAGES ||
59
- this._defaultLang,
60
- );
61
- }
62
- }
63
- }
64
-
65
- setDefaultLang(lang = 'en') {
66
- this._defaultLang = lang.toLowerCase();
67
- this._fallbackMessages = this.getMessages(this._defaultLang);
38
+ class I18n {
39
+ constructor(options) {
40
+ const {
41
+ store = new DataManager(),
42
+ messages = null,
43
+ locale = null,
44
+ fallbackLocale = {
45
+ 'default': 'en',
46
+ 'de-ch': ['fr', 'it'],
47
+ 'zh-hant': ['zh-hans'],
48
+ 'es-cl': ['es-ar'],
49
+ 'es': ['en'],
50
+ 'pt': ['es-ar'],
51
+ },
52
+ } = options;
53
+ this.store = store;
54
+ this.setFallbackLocale(fallbackLocale);
55
+ if (locale) this.setLocale(locale);
56
+ if (messages) this.setMessages(messages);
57
+ }
58
+
59
+ setFallbackLocale(fallbackLocales) {
60
+ this._fallbackLocale = toLowerMap(fallbackLocales);
68
61
  }
69
62
  /**
70
63
  * @memberof I18n
71
- * @return {String} lang
72
- * @description returns the current language. defaults to the browser's navigator.language value.
64
+ * @return {String} locale
65
+ * @description returns the current locale. defaults to the browser's navigator.locale value.
73
66
  * @example
74
67
  *
75
- * navigator.language = 'en-US';
68
+ * navigator.locale = 'en-US';
76
69
  * import { I18n } from '@ornery/web-components';
77
70
  *
78
- * console.log(I18n.getLang()) // "en-US"
71
+ * console.log(I18n.getLocale()) // "en-US"
79
72
  */
80
- getLang() {
81
- return DataManager.get('i18n-language') || '';
82
- }
83
- getRootLang(lang) {
84
- return (lang || this.getLang()).split(this._altLangRegex)[0];
73
+ getLocale() {
74
+ return this.store.get('i18n-locale') || '';
75
+ }
76
+
77
+ getFallbackLocale() {
78
+ const locale = this.getLocale();
79
+ const defaultLocale = this._fallbackLocale.default;
80
+ const fallbackMap = this._fallbackLocale[locale];
81
+ let fallbackLocale;
82
+ if (fallbackMap) {
83
+ const allMessages = this.store.get('i18n-messages') || {};
84
+ for (let i = 0; i < fallbackMap.length; i++) {
85
+ const fbl = fallbackMap[i];
86
+ if (allMessages[fbl]) {
87
+ fallbackLocale = fbl;
88
+ break;
89
+ }
90
+ }
91
+ }
92
+ return fallbackLocale || defaultLocale || 'en';
85
93
  }
86
94
  /**
87
95
  * @memberof I18n
88
- * @param {String} lang
89
- * @return {String} lang
90
- * @description sets the current i18n language. This does not change the browser language.
96
+ * @param {String} locale
97
+ * @return {String} locale
98
+ * @description sets the current i18n locale. This does not change the browser locale.
91
99
  * @example
92
100
  *
93
101
  * import { I18n } from '@ornery/web-components';
94
102
  *
95
- * I18n.setLang('en-US')
96
- * console.log(I18n.getLang()) //'en-US'
103
+ * I18n.setLocale('en-US')
104
+ * console.log(I18n.getLocale()) //'en-US'
97
105
  */
98
- setLang(lang = '') {
99
- return DataManager.set('i18n-language', lang);
106
+ setLocale(locale = '') {
107
+ return this.store.set('i18n-locale', locale);
100
108
  }
101
109
  /**
102
110
  * @memberof I18n
103
- * @param {String} lang
104
- * @return {String} lang
111
+ * @param {String} locale
112
+ * @return {String} locale
105
113
  * @description returns the current i18n messages set in the DataManager
106
114
  */
107
- getMessages(lang) {
108
- const allMessages = DataManager.get('i18n-messages') || {};
109
- if (lang === 'all') {
115
+ getMessages(locale = null) {
116
+ const allMessages = this.store.get('i18n-messages') || {};
117
+ if (locale === 'all') {
110
118
  return allMessages;
111
119
  } else {
112
- lang = lang || this.getLang();
113
- const rootLang = this.getRootLang(lang);
120
+ locale = locale || this.getLocale();
121
+ const fallbackLocale = this.getFallbackLocale();
114
122
  return {
115
- ...this._fallbackMessages,
116
- ...allMessages[rootLang],
117
- ...allMessages[lang],
123
+ ...allMessages[fallbackLocale] || {},
124
+ ...allMessages[locale] || {},
118
125
  };
119
126
  }
120
127
  }
@@ -136,15 +143,14 @@ const I18n = new (class {
136
143
  * })
137
144
  */
138
145
  setMessages(values) {
139
- const response = DataManager.set('i18n-messages', values);
140
- this.setFallbackMessages();
146
+ const response = this.store.set('i18n-messages', values);
141
147
  return response;
142
148
  }
143
149
  /**
144
150
  * @memberof I18n
145
- * @param {{String}|{Object}} lang
151
+ * @param {{String}|{Object}} locale
146
152
  * @param {Object} newStrings
147
- * @description add more strings to the existing language set.
153
+ * @description add more strings to the existing locale set.
148
154
  *
149
155
  * @example
150
156
  *
@@ -154,39 +160,30 @@ const I18n = new (class {
154
160
  * 'tokenized.message': "I have a ${color} ${animal}"
155
161
  * });
156
162
  */
157
- addMessages(lang, newStrings) {
158
- if (typeof lang !== 'string') {
159
- newStrings = lang;
160
- lang = this.getLang();
163
+ addMessages(locale, newStrings) {
164
+ if (typeof locale !== 'string') {
165
+ newStrings = locale;
166
+ locale = this.getLocale();
161
167
  }
162
- lang = lang.toLowerCase();
163
- const rootLang = this.getRootLang(lang);
168
+ locale = locale.toLowerCase();
169
+ const fallbackLocale = this.getFallbackLocale();
164
170
  const existing = this.getMessages('all');
165
- existing[lang] = {
166
- ...(existing[lang] || {}),
171
+ existing[locale] = {
172
+ ...(existing[locale] || {}),
167
173
  ...newStrings,
168
174
  };
169
- if (rootLang !== lang) {
170
- existing[rootLang] = {
171
- ...(existing[rootLang] || {}),
175
+ if (fallbackLocale !== locale) {
176
+ existing[fallbackLocale] = {
177
+ ...(existing[fallbackLocale] || {}),
172
178
  ...newStrings,
173
179
  };
174
180
  }
175
181
  this.setMessages(existing);
176
- this.setFallbackMessages();
177
- }
178
-
179
- setFallbackMessages(){
180
- const rootLang = this.getRootLang(this._defaultLang);
181
- this._fallbackMessages = {
182
- ...this.getMessages(this._defaultLang),
183
- ...this.getMessages(rootLang)
184
- };
185
182
  }
186
183
 
187
184
  /**
188
185
  * @memberof I18n
189
- * @param {String} key they key of the string to retrieve from the current language set.
186
+ * @param {String} key they key of the string to retrieve from the current locale set.
190
187
  * @param {Object} data Optional, The data to process tokens in the string with.
191
188
  * @return {String} Returns the value for the key. Processed if a data context is provided as the second argument.
192
189
  * @description Returns the value for the key. If a context is provided as the second argument for tokens,
@@ -206,7 +203,6 @@ const I18n = new (class {
206
203
  */
207
204
  get(key, data = {}) {
208
205
  const context = {
209
- ...this._fallbackMessages,
210
206
  ...this.getMessages(),
211
207
  ...data,
212
208
  };
@@ -217,7 +213,7 @@ const I18n = new (class {
217
213
  * @memberof I18n
218
214
  * @param {String} namespace
219
215
  * @param {String} context
220
- * @return {Object} Returns all the messages for the given language. Filtered to namespace if provided.
216
+ * @return {Object} Returns all the messages for the given locale. Filtered to namespace if provided.
221
217
  * @description If a namespace is provided, returns all the key value pairs for that
222
218
  * namespace without the namespace in the keys.
223
219
  *
@@ -243,106 +239,10 @@ const I18n = new (class {
243
239
  return this.getMessages('all');
244
240
  }
245
241
  }
246
- })();
247
-
248
- /**
249
- * @class I18nMessage
250
- * @description <i18n-message> HTML element. Provides tranlsation and interpolation for
251
- * translatable strings
252
- * @param {String} key the key for the strings based on current language. can be set as the innerHTML or
253
- * defined as the attibutes: key, id, data-key, data-id
254
- * @param {JSON} values can be passed as data-* attributes or as a json-parseable object string as "data-values"
255
- * @param {String} dataAttributes
256
- * @example <caption>Given the following configuration</caption>
257
- * import { I18n } from '@ornery/web-components';
258
- * I18n.addMessages('en-US', {
259
- * 'translatable.message.name': "I'm a translated string from i18n",
260
- * 'tokenized.message': "I have a ${color} ${animal}"
261
- * });
262
- * @example @lang html <caption>With the following usage</caption>
263
- * <i18n-message>translatable.message.name</i18n-message>
264
- * <div>
265
- * <i18n-message data-values="{'color: 'grey', 'animal': 'monkey'}">tokenized.message</i18n-message>
266
- * <i18n-message data-color="grey" data-animal="monkey">tokenized.message</i18n-message>
267
- * <i18n-message key="tokenized.message"/>
268
- * <!-- React does not pass key or ref props so you can use "data-key" or "data-id" as well-->
269
- * <i18n-message data-key="tokenized.message"/>
270
- * <i18n-message id="translatable.message.name"/>
271
- * <i18n-message data-id="translatable.message.name"/>
272
- * </div>
273
- *
274
- * @example @lang html <caption>Renders the HTML</caption>
275
- * <i18n-message>I'm a translated string from i18n</i18n-message>
276
- * <i18n-message>I have a grey monkey</i18n-message>
277
- */
278
- class I18nMessage extends HTMLElement {
279
- constructor() {
280
- super();
281
- }
282
-
283
- static get observedAttributes() {
284
- return ['key', 'id', 'data-values'];
285
- }
286
-
287
- get useShadow() {
288
- if (this.hasAttribute('shadow')) {
289
- const current = this.getAttribute('shadow');
290
- if (current === 'false') {
291
- return false;
292
- }
293
- }
294
- return true;
295
- }
296
-
297
- get translate() {
298
- return this.getAttribute('key') || this.getAttribute('id');
299
- }
300
-
301
- attributeChangedCallback(name) {
302
- this.update();
303
- }
304
-
305
- update() {
306
- const root = this.shadowRoot || this;
307
- const context = {...this.getAttribute('data-values'), ...this.dataset};
308
- let newMesage = I18n.get(this.translate, context);
309
- if (shouldEncode(newMesage)) {
310
- newMesage = encodeHTML(newMesage);
311
- }
312
- if (!root.innerHTML) {
313
- root.innerHTML = newMesage;
314
- } else if (newMesage !== this.translate || newMesage !== root.innerHTML) {
315
- root.innerHTML = newMesage;
316
- }
317
- }
318
242
 
319
- connectedCallback() {
320
- if (this.useShadow && !this.shadowRoot) this.attachShadow({mode: 'open'});
321
- this._i18nListener = DataManager.subscribe((newVals) => {
322
- this.update();
323
- });
324
- const attrObserver = new MutationObserver(() => this.update());
325
- attrObserver.observe(this, {attributes: true, childList: this.useShadow});
243
+ subscribe(callback) {
244
+ return this.store.subscribe(callback);
326
245
  }
327
-
328
- disconnectedCallback() {
329
- if (this._i18nListener) {
330
- this._i18nListener.destroy();
331
- this._i18nListener = null;
332
- }
333
- }
334
- }
335
-
336
- if (
337
- typeof window !== 'undefined' &&
338
- typeof customElements !== 'undefined' &&
339
- !customElements.get('i18n-message')
340
- ) {
341
- window.I18n = I18n;
342
- customElements.define('i18n-message', withContext(I18nMessage));
343
246
  }
344
247
 
345
- module.exports = {
346
- I18n,
347
- I18nMessage,
348
- };
248
+ export { I18n };
package/src/index.js CHANGED
@@ -1,13 +1,11 @@
1
- import ContextBinding from './context-binding';
2
- import DataManager from './data-manager';
3
- import EventMap from './event-map';
4
- import HTMLLoader from './loaders/html-loader';
5
- export * from './utils';
1
+ import ContextBinding from './context-binding.js';
2
+ import DataManager from './data-manager.js';
3
+ import EventMap from './event-map.js';
4
+ export * from './utils.js';
5
+ export * from './i18n.js';
6
6
 
7
7
  export {
8
8
  ContextBinding,
9
9
  DataManager,
10
10
  EventMap,
11
11
  };
12
-
13
- export default HTMLLoader;
@@ -1,4 +1,4 @@
1
- const Parser = require('fastparse');
1
+ import Parser from 'fastparse';
2
2
 
3
3
  const attrsParser = new Parser({
4
4
  outside: {
@@ -41,7 +41,7 @@ const attrsParser = new Parser({
41
41
  },
42
42
  });
43
43
 
44
- module.exports = (html, tagAttr) => {
44
+ export default (html, tagAttr) => {
45
45
  return attrsParser.parse('outside', html, {
46
46
  currentTag: null,
47
47
  results: [],