From b3b1d4a62800b6dba916494977677febc4bcf714 Mon Sep 17 00:00:00 2001 From: tophf Date: Mon, 17 Apr 2017 18:54:39 +0300 Subject: [PATCH] own page load microopt: reduce blocking on i18n * localStorage cache is faster than chrome.i18n.get * TreeWalker is faster than tHTML for removing extraneous whitespace * simple for() is faster than for-of with [...iterable] --- background.js | 12 ++++++++++++ localization.js | 43 +++++++++++++++++++++++++++++++++---------- options/index.html | 2 +- popup.html | 2 +- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/background.js b/background.js index 4eee2c94..e8eafe44 100644 --- a/background.js +++ b/background.js @@ -37,6 +37,18 @@ function webNavigationListener(method, data) { }); } +// reset i18n cache on language change + +setTimeout(() => { + const {browserUIlanguage} = tryJSONparse(localStorage.L10N) || {}; + const UIlang = chrome.i18n.getUILanguage(); + if (browserUIlanguage != UIlang) { + localStorage.L10N = JSON.stringify({ + browserUIlanguage: UIlang, + }); + } +}); + // messaging chrome.runtime.onMessage.addListener(onRuntimeMessage); diff --git a/localization.js b/localization.js index 9df3b395..2a6ac4c4 100644 --- a/localization.js +++ b/localization.js @@ -5,10 +5,14 @@ tDocLoader(); function t(key, params) { - const s = chrome.i18n.getMessage(key, params); + const cache = !params && t.cache[key]; + const s = cache || chrome.i18n.getMessage(key, params); if (s == '') { throw `Missing string "${key}"`; } + if (!params && !cache) { + t.cache[key] = s; + } return s; } @@ -35,24 +39,38 @@ function tHTML(html) { function tNodeList(nodes) { - for (const node of [...nodes]) { + const PREFIX = 'i18n-'; + for (let n = nodes.length; --n >= 0;) { + const node = nodes[n]; // skip non-ELEMENT_NODE if (node.nodeType != 1) { continue; } if (node.localName == 'template') { + const elements = node.content.querySelectorAll('*'); + tNodeList(elements); + template[node.dataset.id] = elements[0]; // compress inter-tag whitespace to reduce number of DOM nodes by 25% - template[node.dataset.id] = tHTML(node.innerHTML); + const walker = document.createTreeWalker(elements[0], NodeFilter.SHOW_TEXT); + const toRemove = []; + while (walker.nextNode()) { + const textNode = walker.currentNode; + if (!textNode.nodeValue.trim()) { + toRemove.push(textNode); + } + } + toRemove.forEach(el => el.remove()); continue; } - for (const attr of [...node.attributes]) { - let name = attr.nodeName; - if (name.indexOf('i18n-') != 0) { + for (let a = node.attributes.length; --a >= 0;) { + const attr = node.attributes[a]; + const name = attr.nodeName; + if (!name.startsWith(PREFIX)) { continue; } - name = name.substr(5); // 'i18n-'.length + const type = name.substr(PREFIX.length); const value = t(attr.value); - switch (name) { + switch (type) { case 'text': node.insertBefore(document.createTextNode(value), node.firstChild); break; @@ -63,15 +81,17 @@ function tNodeList(nodes) { node.insertAdjacentHTML('afterbegin', value); break; default: - node.setAttribute(name, value); + node.setAttribute(type, value); } - node.removeAttribute(attr.nodeName); + node.removeAttribute(name); } } } function tDocLoader() { + t.cache = tryJSONparse(localStorage.L10N) || {}; + const cacheLength = Object.keys(t.cache).length; // localize HEAD tNodeList(document.getElementsByTagName('*')); @@ -85,6 +105,9 @@ function tDocLoader() { const onLoad = () => { tDocLoader.stop(); process(observer.takeRecords()); + if (cacheLength != Object.keys(t.cache).length) { + localStorage.L10N = JSON.stringify(t.cache); + } }; tDocLoader.start = () => { observer.observe(document, {subtree: true, childList: true}); diff --git a/options/index.html b/options/index.html index 527f2c17..a476b4a6 100644 --- a/options/index.html +++ b/options/index.html @@ -4,8 +4,8 @@ Stylus - + diff --git a/popup.html b/popup.html index 4b56fcc0..9ad62b9a 100644 --- a/popup.html +++ b/popup.html @@ -56,8 +56,8 @@ - +