-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
internationalization.js
194 lines (174 loc) · 5.98 KB
/
internationalization.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
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
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
let fallbackResources, languages;
if (typeof IS_MINIFIED === 'undefined') {
// internationalization is only for the unminified build
const translationsModule = require('../../translations');
fallbackResources = translationsModule.default;
languages = translationsModule.languages;
if (typeof P5_DEV_BUILD !== 'undefined') {
// When the library is built in development mode ( using npm run dev )
// we want to use the current translation files on the disk, which may have
// been updated but not yet pushed to the CDN.
let completeResources = require('../../translations/dev');
for (const language of Object.keys(completeResources)) {
// In es_translation, language is es and namespace is translation
// In es_MX_translation, language is es-MX and namespace is translation
const parts = language.split('_');
const lng = parts.slice(0, parts.length - 1).join('-');
const ns = parts[parts.length - 1];
fallbackResources[lng] = fallbackResources[lng] || {};
fallbackResources[lng][ns] = completeResources[language];
}
}
}
/**
* This is our i18next "backend" plugin. It tries to fetch languages
* from a CDN.
*/
class FetchResources {
constructor(services, options) {
this.init(services, options);
}
// run fetch with a timeout. Automatically rejects on timeout
// default timeout = 2000 ms
fetchWithTimeout(url, options, timeout = 2000) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeout)
)
]);
}
init(services, options = {}) {
this.services = services;
this.options = options;
}
read(language, namespace, callback) {
const loadPath = this.options.loadPath;
if (language === this.options.fallback) {
// if the default language of the user is the same as our inbuilt fallback,
// there's no need to fetch resources from the cdn. This won't actually
// need to run when we use "partialBundledLanguages" in the init
// function.
callback(null, fallbackResources[language][namespace]);
} else if (languages.includes(language)) {
// The user's language is included in the list of languages
// that we so far added translations for.
const url = this.services.interpolator.interpolate(loadPath, {
lng: language,
ns: namespace
});
this.loadUrl(url, callback);
} else {
// We don't have translations for this language. i18next will use
// the default language instead.
callback('Not found', false);
}
}
loadUrl(url, callback) {
this.fetchWithTimeout(url)
.then(
response => {
const ok = response.ok;
if (!ok) {
// caught in the catch() below
throw new Error(`failed loading ${url}`);
}
return response.json();
},
() => {
// caught in the catch() below
throw new Error(`failed loading ${url}`);
}
)
.then(data => {
return callback(null, data);
})
.catch(callback);
}
}
FetchResources.type = 'backend';
/**
* This is our translation function. Give it a key and
* it will retrieve the appropriate string
* (within supported languages) according to the
* user's browser's language settings.
* @function translator
* @param {String} key a key that corresponds to a message in our translation files
* @param {Object} values values for use in the message under the given `key`
* @returns {String} message (with values inserted) in the user's browser language
* @private
*/
export let translator = (key, values) => {
console.debug('p5.js translator called before translations were loaded');
// Certain FES functionality may trigger before translations are downloaded.
// Using "partialBundledLanguages" option during initialization, we can
// still use our fallback language to display messages
i18next.t(key, values); /* i18next-extract-disable-line */
};
// (We'll set this to a real value in the init function below!)
/**
* Set up our translation function, with loaded languages
*/
export const initialize = () => {
let i18init = i18next
.use(LanguageDetector)
.use(FetchResources)
.init({
fallbackLng: 'en',
nestingPrefix: '$tr(',
nestingSuffix: ')',
defaultNS: 'translation',
returnEmptyString: false,
interpolation: {
escapeValue: false
},
detection: {
checkWhitelist: false,
// prevent storing or locating language from cookie or localStorage
// more info on https://github.com/processing/p5.js/issues/4862
order: ['querystring', 'navigator', 'htmlTag', 'path', 'subdomain'],
caches: []
},
backend: {
fallback: 'en',
loadPath:
'https://cdn.jsdelivr.net/npm/p5/translations/{{lng}}/{{ns}}.json'
},
partialBundledLanguages: true,
resources: fallbackResources
})
.then(
translateFn => {
translator = translateFn;
},
e => console.debug(`Translations failed to load (${e})`)
);
// i18next.init() returns a promise that resolves when the translations
// are loaded. We use this in core/init.js to hold p5 initialization until
// we have the translation files.
return i18init;
};
/**
* Returns a list of languages we have translations loaded for
*/
export const availableTranslatorLanguages = () => {
return i18next.languages;
};
/**
* Returns the current language selected for translation
*/
export const currentTranslatorLanguage = language => {
return i18next.language;
};
/**
* Sets the current language for translation
* Returns a promise that resolved when loading is finished,
* or rejects if it fails.
*/
export const setTranslatorLanguage = language => {
return i18next.changeLanguage(language || undefined, e =>
console.debug(`Translations failed to load (${e})`)
);
};