-
Notifications
You must be signed in to change notification settings - Fork 85
Expand file tree
/
Copy pathlib.js
More file actions
311 lines (283 loc) · 9.93 KB
/
lib.js
File metadata and controls
311 lines (283 loc) · 9.93 KB
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
import PropTypes from 'prop-types';
import Cookies from 'universal-cookie';
import merge from 'lodash.merge';
import '@formatjs/intl-pluralrules/polyfill';
import '@formatjs/intl-pluralrules/locale-data/ar';
import '@formatjs/intl-pluralrules/locale-data/en';
import '@formatjs/intl-pluralrules/locale-data/es';
import '@formatjs/intl-pluralrules/locale-data/fr';
import '@formatjs/intl-pluralrules/locale-data/zh';
import '@formatjs/intl-pluralrules/locale-data/ca';
import '@formatjs/intl-pluralrules/locale-data/he';
import '@formatjs/intl-pluralrules/locale-data/id';
import '@formatjs/intl-pluralrules/locale-data/ko';
import '@formatjs/intl-pluralrules/locale-data/pl';
import '@formatjs/intl-pluralrules/locale-data/pt';
import '@formatjs/intl-pluralrules/locale-data/ru';
import '@formatjs/intl-pluralrules/locale-data/th';
import '@formatjs/intl-pluralrules/locale-data/uk';
import '@formatjs/intl-pluralrules/locale-data/vi';
import '@formatjs/intl-relativetimeformat/polyfill';
import '@formatjs/intl-relativetimeformat/locale-data/ar';
import '@formatjs/intl-relativetimeformat/locale-data/en';
import '@formatjs/intl-relativetimeformat/locale-data/es';
import '@formatjs/intl-relativetimeformat/locale-data/fr';
import '@formatjs/intl-relativetimeformat/locale-data/zh';
import '@formatjs/intl-relativetimeformat/locale-data/ca';
import '@formatjs/intl-relativetimeformat/locale-data/he';
import '@formatjs/intl-relativetimeformat/locale-data/id';
import '@formatjs/intl-relativetimeformat/locale-data/ko';
import '@formatjs/intl-relativetimeformat/locale-data/pl';
import '@formatjs/intl-relativetimeformat/locale-data/pt';
import '@formatjs/intl-relativetimeformat/locale-data/ru';
import '@formatjs/intl-relativetimeformat/locale-data/th';
import '@formatjs/intl-relativetimeformat/locale-data/uk';
import '@formatjs/intl-relativetimeformat/locale-data/vi';
const cookies = new Cookies();
const supportedLocales = [
'ar', // Arabic
// NOTE: 'en' is not included in this list intentionally, since it's the fallback.
'es-419', // Spanish, Latin American
'fa', // Farsi
'fa-ir', // Farsi, Iran
'fr', // French
'zh-cn', // Chinese, Simplified
'ca', // Catalan
'he', // Hebrew
'id', // Indonesian
'ko-kr', // Korean (Korea)
'pl', // Polish
'pt-br', // Portuguese (Brazil)
'ru', // Russian
'th', // Thai
'uk', // Ukrainian
'vi', // Vietnamese
];
const rtlLocales = [
'ar', // Arabic
'he', // Hebrew
'fa', // Farsi (not currently supported)
'fa-ir', // Farsi Iran
'ur', // Urdu (not currently supported)
];
let config = null;
let loggingService = null;
let messages = null;
/**
* @memberof module:Internationalization
*
* Prior versions of react-intl (our primary implementation of the i18n service) included a
* PropTypes-based 'shape' for its `intl` object. This has since been removed. For legacy
* compatibility, we include an `intlShape` export that is set to PropTypes.object. Usage of this
* export is deprecated.
*
* @deprecated
*/
export const intlShape = PropTypes.object;
/**
*
* @ignore
* @returns {LoggingService}
*/
export const getLoggingService = () => loggingService;
/**
* @memberof module:Internationalization
*/
export const LOCALE_TOPIC = 'LOCALE';
/**
* @memberof module:Internationalization
*/
export const LOCALE_CHANGED = `${LOCALE_TOPIC}.CHANGED`;
/**
*
* @memberof module:Internationalization
* @returns {Cookies}
*/
export function getCookies() {
return cookies;
}
/**
* Some of our dependencies function on primary language subtags, rather than full locales.
* This function strips a locale down to that first subtag. Depending on the code, this
* may be 2 or more characters.
*
* @param {string} code
* @memberof module:Internationalization
*/
export function getPrimaryLanguageSubtag(code) {
return code.split('-')[0];
}
/**
* Finds the closest supported locale to the one provided. This is done in three steps:
*
* 1. Returning the locale itself if its exact language code is supported.
* 2. Returning the primary language subtag of the language code if it is supported (ar for ar-eg,
* for instance).
* 3. Returning 'en' if neither of the above produce a supported locale.
*
* @param {string} locale
* @returns {string}
* @memberof module:Internationalization
*/
export function findSupportedLocale(locale) {
if (messages[locale] !== undefined) {
return locale;
}
if (messages[getPrimaryLanguageSubtag(locale)] !== undefined) {
return getPrimaryLanguageSubtag(locale);
}
return 'en';
}
/**
* Get the locale from the cookie or, failing that, the browser setting.
* Gracefully fall back to a more general primary language subtag or to English (en)
* if we don't support that language.
*
* @param {string} locale If a locale is provided, returns the closest supported locale. Optional.
* @throws An error if i18n has not yet been configured.
* @returns {string}
* @memberof module:Internationalization
*/
export function getLocale(locale) {
if (messages === null) {
throw new Error('getLocale called before configuring i18n. Call configure with messages first.');
}
// 1. Explicit application request
if (locale !== undefined) {
return findSupportedLocale(locale);
}
// 2. User setting in cookie
const cookieLangPref = cookies
.get(config.LANGUAGE_PREFERENCE_COOKIE_NAME);
if (cookieLangPref) {
return findSupportedLocale(cookieLangPref.toLowerCase());
}
// 3. Browser language (default)
// Note that some browers prefer upper case for the region part of the locale, while others don't.
// Thus the toLowerCase, for consistency.
// https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/language
return findSupportedLocale(globalThis.navigator.language.toLowerCase());
}
/**
* Returns messages for the provided locale, or the user's preferred locale if no argument is
* provided.
*
* @param {string} [locale=getLocale()]
* @memberof module:Internationalization
*/
export function getMessages(locale = getLocale()) {
return messages[locale];
}
/**
* Returns the list of supported locales based on the configured messages.
* This list is dynamically generated from the translation messages that were
* provided during i18n configuration. Always includes the current locale.
*
* @throws An error if i18n has not yet been configured.
* @returns {string[]} Array of supported locale codes
* @memberof module:Internationalization
*/
export function getSupportedLocaleList() {
if (messages === null) {
throw new Error('getSupportedLocaleList called before configuring i18n. Call configure with messages first.');
}
const locales = Object.keys(messages);
if (!locales.includes('en')) {
locales.push('en');
}
return locales;
}
/**
* Determines if the provided locale is a right-to-left language.
*
* @param {string} locale
* @memberof module:Internationalization
*/
export function isRtl(locale) {
return rtlLocales.includes(locale);
}
/**
* Handles applying the RTL stylesheet and "dir=rtl" attribute to the html tag if the current locale
* is a RTL language.
*
* @memberof module:Internationalization
*/
export function handleRtl() {
if (isRtl(getLocale())) {
globalThis.document.getElementsByTagName('html')[0].setAttribute('dir', 'rtl');
} else {
globalThis.document.getElementsByTagName('html')[0].setAttribute('dir', 'ltr');
}
}
const messagesShape = {
ar: PropTypes.objectOf(PropTypes.string), // Arabic
en: PropTypes.objectOf(PropTypes.string),
'es-419': PropTypes.objectOf(PropTypes.string), // Spanish, Latin American
fr: PropTypes.objectOf(PropTypes.string), // French
'zh-cn': PropTypes.objectOf(PropTypes.string), // Chinese, Simplified
ca: PropTypes.objectOf(PropTypes.string), // Catalan
he: PropTypes.objectOf(PropTypes.string), // Hebrew
id: PropTypes.objectOf(PropTypes.string), // Indonesian
'ko-kr': PropTypes.objectOf(PropTypes.string), // Korean (Korea)
pl: PropTypes.objectOf(PropTypes.string), // Polish
'pt-br': PropTypes.objectOf(PropTypes.string), // Portuguese (Brazil)
ru: PropTypes.objectOf(PropTypes.string), // Russian
th: PropTypes.objectOf(PropTypes.string), // Thai
uk: PropTypes.objectOf(PropTypes.string), // Ukrainian
vi: PropTypes.objectOf(PropTypes.string), // Vietnamese
};
const optionsShape = {
config: PropTypes.object.isRequired,
loggingService: PropTypes.shape({
logError: PropTypes.func.isRequired,
}).isRequired,
messages: PropTypes.oneOfType([
PropTypes.shape(messagesShape),
PropTypes.arrayOf(PropTypes.shape(messagesShape)),
]).isRequired,
};
/**
*
*
* @param {Object} newMessages
* @returns {Object}
* @memberof module:Internationalization
*/
export function mergeMessages(newMessages) {
const msgs = Array.isArray(newMessages) ? merge({}, ...newMessages) : newMessages;
messages = merge(messages, msgs);
return messages;
}
/**
* Configures the i18n library with messages for your application.
*
* Logs a warning if it detects a locale it doesn't expect (as defined by the supportedLocales list
* above), or if an expected locale is not provided.
*
* @param {Object} options
* @param {LoggingService} options.loggingService
* @param {Object} options.config
* @param {Object} options.messages
* @memberof module:Internationalization
*/
export function configure(options) {
PropTypes.checkPropTypes(optionsShape, options, 'property', 'i18n');
// eslint-disable-next-line prefer-destructuring
loggingService = options.loggingService;
// eslint-disable-next-line prefer-destructuring
config = options.config;
messages = Array.isArray(options.messages) ? merge({}, ...options.messages) : options.messages;
if (config.ENVIRONMENT !== 'production') {
Object.keys(messages).forEach((key) => {
if (supportedLocales.indexOf(key) < 0) {
console.warn(`Unexpected locale: ${key}`); // eslint-disable-line no-console
}
});
supportedLocales.forEach((key) => {
if (messages[key] === undefined) {
console.warn(`Missing locale: ${key}`); // eslint-disable-line no-console
}
});
}
handleRtl();
}