-
Notifications
You must be signed in to change notification settings - Fork 1
/
i18n.js
152 lines (146 loc) · 4.82 KB
/
i18n.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
// Core
import createStore from './createStore';
import createTranslator from './createTranslator';
// Utils
import { createSubscriber } from './utils';
// Constants
import { I18N_EVENTS, STORE_EVENTS } from './constants';
/**
* Provides a instance of i18njs to handle multiple locales asynchronously
* @function i18n
* @param {options} options
* @returns {object} i18n instance
* @example
* const i18n = i18njs({
* locale: 'es',
* locales: ['de', 'es', 'en'],
* resolver: (locale) => ({ foo: 'bar' }),
* });
*/
const i18n = (options = {}) => {
/**
* Option struct.
* @typedef {object} options
* @property {?string} locale predefined default locale, if setted it will execute setLocale internally to load the locale resource, keep it undefined to handle this behaviour outside the logic.
* @property {?(string[]|object)} locales Array of strings of available locales using the standard [ISO_639-1](https://es.wikipedia.org/wiki/ISO_639-1)
* @property {function} resolver used to resolve asyncronous locale resources
*/
options = { ...options };
// options.locales has to be an array of string with the supported locales,
// otherwise if object is provided only the keys will be used
let locales = options.locales;
let cache = {};
if (!Array.isArray(options.locales)) {
locales = Object.keys(options.locales || {});
cache = { ...options.locales };
}
// if options.locales has own properties defined it will be used to extend
// the cache as sync catalog
const fallbacks = options.fallbacks || {};
const store = createStore({ ...options, cache, locales });
const defaultLocale = options.locale;
let currentLocale = defaultLocale;
const self = Object.assign(store, {
// To preserve the translator functions we pass a null catalog
// to force the checker raise a warning when the translators being called
/**
* Translation function
* @alias i18n.translate
* @type {object}
*/
translate: createTranslator(null),
/**
* Updates the current locale and refresh translate
* @method i18n.setLocale
* @param {string} locale [description]
* @return {promise}
*/
setLocale (locale) {
let targetLocale = !cache[locale] && fallbacks[locale] ? fallbacks[locale] : locale;
targetLocale = currentLocale = (locales.includes(targetLocale) ? targetLocale : defaultLocale) || locale;
store.emit(I18N_EVENTS.LOADING, { locale });
return store
.resolve(targetLocale)
.then((catalog) => {
const translate = self.translate = createTranslator(catalog);
store.emit(I18N_EVENTS.LOADED, { locale, translate, catalog });
return self;
});
},
/**
* Obtain and array of string in ISO_639 format with all loaded locales
* @method i18n.getLocales
* @return {Array} Array of string [ISO_639-1](https://es.wikipedia.org/wiki/ISO_639-1)
*/
getLocales () {
return Object.keys(locales);
},
/**
* Obtain current locale ISO_639
* @method i18n.getLocale
* @return {string} [ISO_639-1](https://es.wikipedia.org/wiki/ISO_639-1)
*/
getLocale () {
return currentLocale;
},
/**
* Get current catalog of literal translations from cache
* @method i18n.getCatalog
* @return {object}
*/
getCatalog (locale = currentLocale) {
return cache[locale];
},
/**
* Get all catalogs of literal translations
* @method i18n.getCatalogs
* @return {object}
*/
getCatalogs () {
return cache;
},
/**
* Subcribe to the changes of loading state flow
* @method i18n.subscribe
* @return {object}
* @example
* const unsubscribe = i18n.subscribe(({ type, locale }) => {
* switch(type) {
* case 'loading':
* // dispatch function to handle loading state
* break;
* case 'loaded':
* // dispatch function to handle loading state
* break;
* case 'error':
* // dispatch function to handle error state
* break;
* }
* });
*/
// Create a subscriber method with all posible event
subscribe: createSubscriber(store, [...Object.values(I18N_EVENTS), ...Object.values(STORE_EVENTS)]),
});
// If currentLocale is setted then force to remap trls with the selected locale
if (currentLocale) {
self.setLocale(currentLocale);
}
return self;
};
/**
* Fetch i18n instance with preloaded locale resource
* @module i18njs.fetch
* @param {!string} options.locale
* @example
* i18njs
* .fetch({
* locale: 'es',
* resolver: () => Promise.resolve({ 'foo': 'bar' })
* ...options
* })
* .then((i18n) => {
* i18n.translate('foo') // bar
* });
*/
export const fetch = ({ locale, ...options } = {}) => i18n(options).setLocale(locale);
export default i18n;