Skip to content

Commit cd66bda

Browse files
authored
TASK-47876 Use SessionStorage to cache ResourceBundle in client Side for better FrontEnd performances (#224)
Prior to this change, the I18N Resource bundles of a page are loaded one by one using HTTP requests. Each application will attempt to get its resource bundles by a `fetch` HTTP and then merge the result to the centralized VueI18N object used by all Vue applications of the page. This improvement will use `sessionStorage` to store the list of resources used by a page, and for each I18N Resource, its content will be stored in `sessionStorage` as well. With both information combined and stored into `sessionStorage`, the initialization of the required resources per page will be retrieved at initialization phase without repeating the `mergeLocaleMessage` operation for each I18N bundle and without sending a `fetch` request, only for the whole first site display during the Browser Session (Not tab session).
1 parent 7624f1c commit cd66bda

File tree

1 file changed

+54
-37
lines changed
  • commons-extension-webapp/src/main/webapp/javascript

1 file changed

+54
-37
lines changed

commons-extension-webapp/src/main/webapp/javascript/exo-i18n.js

+54-37
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,39 @@
11
(function () {
22

3-
var i18n = new VueI18n({
3+
const i18NFetchedURLsKey = `${window.location.pathname}_${eXo.env.portal.language}_${eXo.env.client.assetsVersion}_URLs`;
4+
const i18NFetchedURLsValue = sessionStorage && sessionStorage.getItem(i18NFetchedURLsKey);
5+
const i18NFetchedURLs = i18NFetchedURLsValue && JSON.parse(i18NFetchedURLsValue) || [];
6+
window.eXoI18N = {
7+
executingFetches: {},
8+
i18NFetchedURLs,
9+
};
10+
11+
const preloadedMessages = {};
12+
window.eXoI18N.i18NFetchedURLs.forEach((url, index) => {
13+
const cachedMessages = sessionStorage && sessionStorage.getItem(url);
14+
if (cachedMessages && cachedMessages.length) {
15+
Object.assign(preloadedMessages, JSON.parse(cachedMessages));
16+
} else {
17+
delete window.eXoI18N.i18NFetchedURLs[index];
18+
}
19+
});
20+
window.eXoI18N.i18NFetchedURLs = window.eXoI18N.i18NFetchedURLs.filter(url => !!url);
21+
22+
const i18nMessages = {};
23+
i18nMessages[eXo.env.portal.language] = preloadedMessages;
24+
const i18n = new VueI18n({
425
locale: eXo.env.portal.language, // set locale
526
fallbackLocale: 'en',
6-
messages: {}
27+
messages: i18nMessages,
728
});
829

9-
window.eXoI18N = window.eXoI18N || {};
10-
window.eXoI18N.executingFetches = window.eXoI18N.executingFetches || {};
11-
window.eXoI18N.i18NFetchedURLs = window.eXoI18N.i18NFetchedURLs || [];
12-
window.eXoI18N.i18NPreloadedMessages = window.eXoI18N.i18NPreloadedMessages || {};
13-
1430
/**
1531
* Load translated strings from the given URLs and for the given language
1632
* @param {string} lang - Language to load strings for
1733
* @param {(string|string[])} urls - Single URL or array of URLs to load i18n files from
18-
* @param {string} synchLoading - Whether to fetch I18N in 'sync' (default), 'async' or 'defer' (after page loading)
1934
* @returns {Promise} Promise giving the i18n object with loaded translated strings
2035
*/
21-
function loadLanguageAsync(lang, urls, synchLoading) {
36+
function loadLanguageAsync(lang, urls) {
2237
if (!lang) {
2338
lang = i18n.locale;
2439
}
@@ -29,18 +44,9 @@
2944

3045
const promises = [];
3146
urls.forEach(url => {
32-
if (synchLoading === 'async') {
33-
fetchLangFile(url, lang);
34-
} else if (synchLoading === 'defer') {
35-
// Load bundles after page loaded event emited
36-
eXo.env.portal.addOnLoadCallback(() => {
37-
fetchLangFile(url, lang);
38-
});
39-
} else { // 'sync', default option
40-
const promise = fetchLangFile(url, lang);
41-
if (promise) {
42-
promises.push(promise);
43-
}
47+
const promise = fetchLangFile(url, lang);
48+
if (promise) {
49+
promises.push(promise);
4450
}
4551
});
4652
return Promise.all(promises).then(() => i18n);
@@ -53,27 +59,38 @@
5359
url = `${url}?v=${eXo.env.client.assetsVersion}`
5460
}
5561

56-
const msgs = window.eXoI18N.i18NPreloadedMessages[url];
57-
if (msgs) {
58-
delete window.eXoI18N.i18NPreloadedMessages[url];
59-
i18n.mergeLocaleMessage(lang, msgs);
60-
return Promise.resolve(i18n);
61-
} else if (window.eXoI18N.i18NFetchedURLs.indexOf(url) >= 0) {
62+
if (window.eXoI18N.executingFetches[url]) {
6263
return window.eXoI18N.executingFetches[url];
64+
} else if (window.eXoI18N.i18NFetchedURLs.indexOf(url) >= 0) {
65+
return Promise.resolve(i18n);
6366
} else {
64-
const i18NFetch = fetch(url)
65-
.then(resp => resp && resp.ok && resp.json())
66-
.then(msgs => {
67-
if (msgs) {
68-
i18n.mergeLocaleMessage(lang, msgs);
69-
i18n.locale = lang;
67+
const cachedMessages = sessionStorage && sessionStorage.getItem(url);
68+
const i18NFetch = (cachedMessages && Promise.resolve(JSON.parse(cachedMessages)) || fetch(url).then(resp => resp && resp.ok && resp.json()));
69+
window.eXoI18N.executingFetches[url] = i18NFetch;
70+
return i18NFetch
71+
.then(data => {
72+
if (data) {
73+
i18n.mergeLocaleMessage(lang, data);
74+
if (!cachedMessages) {
75+
try {
76+
sessionStorage.setItem(url, JSON.stringify(data));
77+
} catch (e) {
78+
// QuotaExceededError can be thrown, thus nothing to do here
79+
}
80+
}
7081
}
7182
return i18n;
7283
})
73-
.finally(() => delete window.eXoI18N.executingFetches[url]);
74-
window.eXoI18N.executingFetches[url] = i18NFetch;
75-
window.eXoI18N.i18NFetchedURLs.push(url);
76-
return i18NFetch;
84+
.finally(() => {
85+
delete window.eXoI18N.executingFetches[url];
86+
window.eXoI18N.i18NFetchedURLs.push(url);
87+
try {
88+
sessionStorage.setItem(i18NFetchedURLsKey, JSON.stringify(window.eXoI18N.i18NFetchedURLs));
89+
} catch (e) {
90+
// QuotaExceededError can be thrown, thus nothing to do here
91+
}
92+
return i18n;
93+
});
7794
}
7895
}
7996

0 commit comments

Comments
 (0)