i18nizeelement 0.3.0 → 0.5.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.
@@ -1,293 +1,312 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
- typeof define === 'function' && define.amd ? define(factory) :
4
- (global = global || self, global.i18nizeElementJamilihPlugin = factory());
5
- }(this, (function () { 'use strict';
6
-
7
- /**
8
- * Copyright 2015, Yahoo! Inc.
9
- * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
10
- */
11
-
12
- var self;
13
- var RtlDetectLib = self = {
14
- // eslint-disable-line consistent-this
15
- // Private functions - star
16
- _escapeRegExpPattern: function (str) {
17
- if (typeof str !== 'string') {
18
- return str;
19
- }
20
-
21
- return str.replace(/([\.\*\+\^\$\[\]\\\(\)\|\{\}\,\-\:\?])/g, '\\$1'); // eslint-disable-line no-useless-escape
22
- },
23
- _toLowerCase: function (str, reserveReturnValue) {
24
- if (typeof str !== 'string') {
25
- return reserveReturnValue && str;
26
- }
27
-
28
- return str.toLowerCase();
29
- },
30
- _toUpperCase: function (str, reserveReturnValue) {
31
- if (typeof str !== 'string') {
32
- return reserveReturnValue && str;
33
- }
34
-
35
- return str.toUpperCase();
36
- },
37
- _trim: function (str, delimiter, reserveReturnValue) {
38
- var patterns = [];
39
- var regexp;
40
-
41
- var addPatterns = function (pattern) {
42
- // Build trim RegExp pattern and push it to patterns array
43
- patterns.push('^' + pattern + '+|' + pattern + '+$');
44
- }; // fix reserveReturnValue value
45
-
46
-
47
- if (typeof delimiter === 'boolean') {
48
- reserveReturnValue = delimiter;
49
- delimiter = null;
50
- }
51
-
52
- if (typeof str !== 'string') {
53
- return reserveReturnValue && str;
54
- } // Trim based on delimiter array values
55
-
56
-
57
- if (Array.isArray(delimiter)) {
58
- // Loop through delimiter array
59
- delimiter.map(function (item) {
60
- // Escape delimiter to be valid RegExp Pattern
61
- var pattern = self._escapeRegExpPattern(item); // Push pattern to patterns array
62
-
63
-
64
- addPatterns(pattern);
65
- });
66
- } // Trim based on delimiter string value
67
-
68
-
69
- if (typeof delimiter === 'string') {
70
- // Escape delimiter to be valid RegExp Pattern
71
- var patternDelimiter = self._escapeRegExpPattern(delimiter); // push pattern to patterns array
72
-
73
-
74
- addPatterns(patternDelimiter);
75
- } // If delimiter is not defined, Trim white spaces
76
-
77
-
78
- if (!delimiter) {
79
- // Push white space pattern to patterns array
80
- addPatterns('\\s');
81
- } // Build RegExp pattern
82
-
83
-
84
- var pattern = '(' + patterns.join('|') + ')'; // Build RegExp object
85
-
86
- regexp = new RegExp(pattern, 'g'); // trim string for all patterns
87
-
88
- while (str.match(regexp)) {
89
- str = str.replace(regexp, '');
90
- } // Return trim string
91
-
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.i18nizeElementJamilihPlugin = factory());
5
+ })(this, (function () { 'use strict';
6
+
7
+ /**
8
+ * Copyright 2015, Yahoo! Inc.
9
+ * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
10
+ */
11
+
12
+ // Private vars - star
13
+ const _regexEscape = /([\.\*\+\^\$\[\]\\\(\)\|\{\}\,\-\:\?])/g; // eslint-disable-line no-useless-escape
14
+ const _regexParseLocale = /^([a-zA-Z]+)([-a-zA-Z]*)$/;
15
+ // Private vars - end
16
+
17
+ class Locale {
18
+ // eslint-disable-line consistent-this
19
+
20
+ constructor(locale) {
21
+ if (typeof locale !== 'string') {
22
+ throw new TypeError('A string argument is expected');
23
+ }
24
+ if (!_regexParseLocale.test(locale)) {
25
+ throw new RangeError('A proper locale must be provided');
26
+ }
27
+ this._locale = locale;
28
+ }
92
29
 
30
+ // Private functions - star
31
+ static _escapeRegExpPattern(str) {
32
+ if (typeof str !== 'string') {
93
33
  return str;
94
- },
95
- _parseLocale: function (strLocale) {
96
- // parse locale regex object
97
- var regex = /^([a-zA-Z]*)([_\-a-zA-Z]*)$/;
98
- var matches = regex.exec(strLocale); // exec regex
99
-
100
- var parsedLocale;
101
- var lang;
102
- var countryCode;
103
-
104
- if (!strLocale || !matches) {
105
- return;
106
- } // fix countryCode string by trimming '-' and '_'
107
-
108
-
109
- matches[2] = self._trim(matches[2], ['-', '_']);
110
- lang = self._toLowerCase(matches[1]);
111
- countryCode = self._toUpperCase(matches[2]) || countryCode; // object with lang, countryCode properties
112
-
113
- parsedLocale = {
114
- lang: lang,
115
- countryCode: countryCode
116
- }; // return parsed locale object
117
-
118
- return parsedLocale;
119
- },
120
- // Private functions - End
121
- // Public functions - star
122
- isRtlLang: function (strLocale) {
123
- var objLocale = self._parseLocale(strLocale);
124
-
125
- if (!objLocale) {
126
- return;
127
- } // return true if the intel string lang exists in the BID RTL LANGS array else return false
128
-
129
-
130
- return self._BIDI_RTL_LANGS.indexOf(objLocale.lang) >= 0;
131
- },
132
- getLangDir: function (strLocale) {
133
- // return 'rtl' if the intel string lang exists in the BID RTL LANGS array else return 'ltr'
134
- return self.isRtlLang(strLocale) ? 'rtl' : 'ltr';
135
- } // Public functions - End
136
-
137
- }; // Const BIDI_RTL_LANGS Array
138
- // BIDI_RTL_LANGS ref: http://en.wikipedia.org/wiki/Right-to-left
139
- // Table of scripts in Unicode: https://en.wikipedia.org/wiki/Script_(Unicode)
140
-
141
- Object.defineProperty(self, '_BIDI_RTL_LANGS', {
142
- value: ['ae',
143
- /* Avestan */
144
- 'ar',
145
- /* 'العربية', Arabic */
146
- 'arc',
147
- /* Aramaic */
148
- 'bcc',
149
- /* 'بلوچی مکرانی', Southern Balochi */
150
- 'bqi',
151
- /* 'بختياري', Bakthiari */
152
- 'ckb',
153
- /* 'Soranî / کوردی', Sorani */
154
- 'dv',
155
- /* Dhivehi */
156
- 'fa',
157
- /* 'فارسی', Persian */
158
- 'glk',
159
- /* 'گیلکی', Gilaki */
160
- 'he',
161
- /* 'עברית', Hebrew */
162
- 'ku',
163
- /* 'Kurdî / كوردی', Kurdish */
164
- 'mzn',
165
- /* 'مازِرونی', Mazanderani */
166
- 'nqo',
167
- /* N'Ko */
168
- 'pnb',
169
- /* 'پنجابی', Western Punjabi */
170
- 'ps',
171
- /* 'پښتو', Pashto, */
172
- 'sd',
173
- /* 'سنڌي', Sindhi */
174
- 'ug',
175
- /* 'Uyghurche / ئۇيغۇرچە', Uyghur */
176
- 'ur',
177
- /* 'اردو', Urdu */
178
- 'yi'
179
- /* 'ייִדיש', Yiddish */
180
- ],
181
- writable: false,
182
- enumerable: true,
183
- configurable: false
184
- });
185
- var rtlDetect = RtlDetectLib;
186
-
187
- var rtlDetect_1 = {
188
- isRtlLang: rtlDetect.isRtlLang,
189
- getLangDir: rtlDetect.getLangDir
190
- };
191
-
192
- // e.g., `i18nizeElement(document.title, {language: 'ar-AR'});`
193
-
194
- const i18nizeElement = (element, {
195
- language,
196
- // These avoid setting if found to be already set with the same value
197
- // on the closest ancestor
198
- avoidLangIfSet = true,
199
- avoidDirIfSet = true,
200
- // This avoids setting LTR if there is no ancestor with `dir` (since
201
- // LTR is the default)
202
- avoidLTRByDefault = true
203
- } = {}) => {
204
- if (!element || !language) {
205
- throw new TypeError('You must supply an element and language');
206
34
  }
207
-
208
- let presetLangElement; // We set `lang` if:
209
- // 1. The user is not avoiding setting `lang` when the supplied language
210
- // is the same as the closest ancestor with `lang`
211
- // 2. There is no ancestor with `lang`
212
- // 3. The closest ancestor with `lang` has a different language from
213
- // the supplied
214
-
215
- if (avoidLangIfSet) {
216
- presetLangElement = element.closest('[lang]');
35
+ return str.replace(_regexEscape, '\\$1');
36
+ }
37
+ static _toLowerCase(str, reserveReturnValue) {
38
+ if (typeof str !== 'string') {
39
+ return reserveReturnValue && str;
217
40
  }
218
-
219
- if (!presetLangElement || presetLangElement.lang !== language) {
220
- element.lang = language;
221
- } // See <https://github.com/tc39/ecma402/issues/205> for request for JavaScript to detect this
222
- // We set `dir` if:
223
- // 1. The user is not avoiding setting `dir` when the supplied
224
- // language's directionality is the same as the closest ancestor
225
- // with `dir`
226
- // 2. There is no ancestor with `dir` (and this is either not an LTR
227
- // direction or the user isn't avoiding (the default) LTR)
228
- // 3. The closest ancestor with `dir` has a different `dir` from the
229
- // direction of the supplied language
230
-
231
-
232
- const dir = rtlDetect_1.getLangDir(language);
233
- let presetDirElement;
234
-
235
- if (avoidDirIfSet || // If avoiding the default LTR except when different (even if not
236
- // avoiding an (RTL) already-set dir), we need to know if different
237
- avoidLTRByDefault && dir === 'ltr') {
238
- presetDirElement = element.closest('[dir]');
41
+ return str.toLowerCase();
42
+ }
43
+ static _toUpperCase(str, reserveReturnValue) {
44
+ if (typeof str !== 'string') {
45
+ return reserveReturnValue && str;
46
+ }
47
+ return str.toUpperCase();
48
+ }
49
+ static _trim(str, delimiter, reserveReturnValue) {
50
+ const patterns = [];
51
+ let regexp;
52
+ const addPatterns = function (pattern) {
53
+ // Build trim RegExp pattern and push it to patterns array
54
+ patterns.push('^' + pattern + '+|' + pattern + '+$');
55
+ };
56
+
57
+ // fix reserveReturnValue value
58
+ if (typeof delimiter === 'boolean') {
59
+ reserveReturnValue = delimiter;
60
+ delimiter = null;
61
+ }
62
+ if (typeof str !== 'string') {
63
+ return reserveReturnValue && str;
239
64
  }
240
65
 
241
- if ( // If the closest ancestor with `dir` is different, we need to
242
- // set regardless
243
- presetDirElement && presetDirElement.dir !== dir || // We set if the user is not avoiding (whether there is no ancestor
244
- // with `div` or even when there is and the language has the same
245
- // directionality as the closest ancestor with `dir`) OR
246
- !avoidDirIfSet || // ...if there is no ancestor with `dir` AND...
247
- !presetDirElement && ( // this is either not an LTR direction or the user isn't
248
- // avoiding (the default) LTR
249
- !avoidLTRByDefault || dir !== 'ltr')) {
250
- element.dir = dir;
251
- /* istanbul ignore next */
252
-
253
- if (dir === 'ttb') {
254
- // Assumes https://github.com/shadiabuhilal/rtl-detect/issues/2
255
- element.style.writingMode = 'vertical-lr';
256
- }
66
+ // Trim based on delimiter array values
67
+ if (Array.isArray(delimiter)) {
68
+ // Loop through delimiter array
69
+ delimiter.map(item => {
70
+ // Escape delimiter to be valid RegExp Pattern
71
+ const pattern = this._escapeRegExpPattern(item);
72
+ // Push pattern to patterns array
73
+ addPatterns(pattern);
74
+ });
257
75
  }
258
- };
259
76
 
260
- const plugin = {
261
- name: '$_language',
77
+ // Trim based on delimiter string value
78
+ if (typeof delimiter === 'string') {
79
+ // Escape delimiter to be valid RegExp Pattern
80
+ const patternDelimiter = this._escapeRegExpPattern(delimiter);
81
+ // push pattern to patterns array
82
+ addPatterns(patternDelimiter);
83
+ }
262
84
 
263
- async set({
264
- element,
265
- attribute: {
266
- value: options
267
- }
268
- }) {
269
- if (typeof options === 'string') {
270
- options = {
271
- language: options
272
- };
273
- } else if (Array.isArray(options)) {
274
- options = { ...options[1],
275
- language: options[0]
276
- };
277
- } // We don't immediately internationalize
278
- // because Jamilih has not yet appended
279
- // the child element into the ancestors
280
- // it is being built with
85
+ // If delimiter is not defined, Trim white spaces
86
+ if (!delimiter) {
87
+ // Push white space pattern to patterns array
88
+ addPatterns('\\s');
89
+ }
281
90
 
91
+ // Build RegExp pattern
92
+ const pattern = '(' + patterns.join('|') + ')';
93
+ // Build RegExp object
94
+ regexp = new RegExp(pattern, 'g');
282
95
 
283
- await Promise.resolve();
284
- i18nizeElement(element, { ...options
285
- });
96
+ // trim string for all patterns
97
+ while (str.match(regexp)) {
98
+ str = str.replace(regexp, '');
286
99
  }
287
100
 
288
- };
289
-
290
- return plugin;
101
+ // Return trim string
102
+ return str;
103
+ }
104
+ static _parseLocale(strLocale) {
105
+ var matches = _regexParseLocale.exec(strLocale); // exec regex
106
+ let parsedLocale;
107
+ let lang;
108
+ let countryCode;
109
+ if (!strLocale || !matches) {
110
+ return;
111
+ }
291
112
 
292
- })));
113
+ // fix countryCode string by trimming '-' and '_'
114
+ matches[2] = this._trim(matches[2], ['-', '_']);
115
+ lang = this._toLowerCase(matches[1]);
116
+ countryCode = this._toUpperCase(matches[2]) || countryCode;
117
+
118
+ // object with lang, countryCode properties
119
+ parsedLocale = {
120
+ lang,
121
+ countryCode
122
+ };
123
+
124
+ // return parsed locale object
125
+ return parsedLocale;
126
+ }
127
+ // Private functions - End
128
+
129
+ _isRtlLang() {
130
+ const objLocale = this.constructor._parseLocale(this._locale);
131
+
132
+ // return true if the intel string lang exists in the BID RTL LANGS array else return false
133
+ return this.constructor._BIDI_RTL_LANGS.indexOf(objLocale.lang) >= 0;
134
+ }
135
+
136
+ // Public functions - star
137
+ get textInfo() {
138
+ // return 'rtl' if the intel string lang exists in the BID RTL LANGS array else return 'ltr'
139
+ const direction = this._isRtlLang() ? 'rtl' : 'ltr';
140
+ return {
141
+ direction
142
+ };
143
+ }
144
+
145
+ // Public functions - End
146
+ }
147
+
148
+ // Why not working as static property
149
+ Locale._BIDI_RTL_LANGS = [
150
+ // Const BIDI_RTL_LANGS Array
151
+ // BIDI_RTL_LANGS ref: http://en.wikipedia.org/wiki/Right-to-left
152
+ // Table of scripts in Unicode: https://en.wikipedia.org/wiki/Script_(Unicode)
153
+ 'ae', /* Avestan */
154
+ 'ar', /* 'العربية', Arabic */
155
+ 'arc', /* Aramaic */
156
+ 'bcc', /* 'بلوچی مکرانی', Southern Balochi */
157
+ 'bqi', /* 'بختياري', Bakthiari */
158
+ 'ckb', /* 'Soranî / کوردی', Sorani */
159
+ 'dv', /* Dhivehi */
160
+ 'fa', /* 'فارسی', Persian */
161
+ 'glk', /* 'گیلکی', Gilaki */
162
+ 'he', /* 'עברית', Hebrew */
163
+ 'ku', /* 'Kurdî / كوردی', Kurdish */
164
+ 'mzn', /* 'مازِرونی', Mazanderani */
165
+ 'nqo', /* N'Ko */
166
+ 'pnb', /* 'پنجابی', Western Punjabi */
167
+ 'prs', /* 'دری', Darī */
168
+ 'ps', /* 'پښتو', Pashto, */
169
+ 'sd', /* 'سنڌي', Sindhi */
170
+ 'ug', /* 'Uyghurche / ئۇيغۇرچە', Uyghur */
171
+ 'ur', /* 'اردو', Urdu */
172
+ 'yi' /* 'ייִדיש', Yiddish */];
173
+
174
+ if (typeof Intl === 'undefined') {
175
+ globalThis.Intl = {};
176
+ }
177
+ if (typeof Intl.Locale === 'undefined') {
178
+ Intl.Locale = Locale;
179
+ }
180
+
181
+ // e.g., `i18nizeElement(document.title, {language: 'ar-AR'});`
182
+
183
+ /**
184
+ * @param {HTMLElement} element
185
+ * @param {{
186
+ * language?: string,
187
+ * avoidLangIfSet?: boolean,
188
+ * avoidDirIfSet?: boolean,
189
+ * avoidLTRByDefault?: boolean
190
+ * }} cfg
191
+ */
192
+ const i18nizeElement = (element, {
193
+ language,
194
+ // These avoid setting if found to be already set with the same value
195
+ // on the closest ancestor
196
+ avoidLangIfSet = true,
197
+ avoidDirIfSet = true,
198
+ // This avoids setting LTR if there is no ancestor with `dir` (since
199
+ // LTR is the default)
200
+ avoidLTRByDefault = true
201
+ } = {}) => {
202
+ if (!element || !language) {
203
+ throw new TypeError('You must supply an element and language');
204
+ }
205
+ let presetLangElement;
206
+
207
+ // We set `lang` if:
208
+ // 1. The user is not avoiding setting `lang` when the supplied language
209
+ // is the same as the closest ancestor with `lang`
210
+ // 2. There is no ancestor with `lang`
211
+ // 3. The closest ancestor with `lang` has a different language from
212
+ // the supplied
213
+ if (avoidLangIfSet) {
214
+ presetLangElement = /** @type {HTMLElement} */
215
+ element.closest('[lang]');
216
+ }
217
+ if (!presetLangElement || presetLangElement.lang !== language) {
218
+ element.lang = language;
219
+ }
220
+
221
+ // See <https://github.com/tc39/ecma402/issues/205> for request for JavaScript to detect this
222
+ // We set `dir` if:
223
+ // 1. The user is not avoiding setting `dir` when the supplied
224
+ // language's directionality is the same as the closest ancestor
225
+ // with `dir`
226
+ // 2. There is no ancestor with `dir` (and this is either not an LTR
227
+ // direction or the user isn't avoiding (the default) LTR)
228
+ // 3. The closest ancestor with `dir` has a different `dir` from the
229
+ // direction of the supplied language
230
+ const {
231
+ direction: dir
232
+ } =
233
+ /**
234
+ * @type {Intl.Locale & {
235
+ * textInfo: {direction: "ltr"|"rtl"|"ttb"}
236
+ * }}
237
+ */
238
+ new Intl.Locale(language).textInfo;
239
+
240
+ /** @type {HTMLElement|undefined} */
241
+ let presetDirElement;
242
+ if (avoidDirIfSet ||
243
+ // If avoiding the default LTR except when different (even if not
244
+ // avoiding an (RTL) already-set dir), we need to know if different
245
+ avoidLTRByDefault && dir === 'ltr') {
246
+ presetDirElement = /** @type {HTMLElement} */
247
+ element.closest('[dir]');
248
+ }
249
+ if (
250
+ // If the closest ancestor with `dir` is different, we need to
251
+ // set regardless
252
+ presetDirElement && presetDirElement.dir !== dir ||
253
+ // We set if the user is not avoiding (whether there is no ancestor
254
+ // with `div` or even when there is and the language has the same
255
+ // directionality as the closest ancestor with `dir`) OR
256
+ !avoidDirIfSet ||
257
+ // ...if there is no ancestor with `dir` AND...
258
+ !presetDirElement && (
259
+ // this is either not an LTR direction or the user isn't
260
+ // avoiding (the default) LTR
261
+ !avoidLTRByDefault || dir !== 'ltr')) {
262
+ element.dir = dir;
263
+
264
+ /* c8 ignore next 3 */
265
+ if (dir === 'ttb') {
266
+ element.style.writingMode = 'vertical-lr';
267
+ }
268
+ }
269
+ };
270
+
271
+ /** @type {import('jamilih').JamilihPlugin} */
272
+ const plugin = {
273
+ name: '$_language',
274
+ async set({
275
+ element,
276
+ attribute: {
277
+ value
278
+ }
279
+ }) {
280
+ let options = /** @type {import('jamilih').PluginValue} */
281
+ value;
282
+ if (typeof options === 'string') {
283
+ options = {
284
+ language: options
285
+ };
286
+ } else if (Array.isArray(options)) {
287
+ options = {
288
+ ...options[1],
289
+ language: options[0]
290
+ };
291
+ }
292
+ // We don't immediately internationalize
293
+ // because Jamilih has not yet appended
294
+ // the child element into the ancestors
295
+ // it is being built with
296
+ await Promise.resolve();
297
+ i18nizeElement( /** @type {HTMLElement} */element, {
298
+ ...
299
+ /**
300
+ * @type {{
301
+ * [key: string]: any;
302
+ * }}
303
+ */
304
+ options
305
+ });
306
+ }
307
+ };
308
+
309
+ return plugin;
310
+
311
+ }));
293
312
  //# sourceMappingURL=i18nizeelement-jamilih-plugin.umd.js.map