(async () => { const languageCodeMap = { "日本語": "ja-JP", "English": "en-US", }; const { map } = await import(`./i18n.${languageCodeMap[settings.i18nLanguage]}`); class Translator { accepts(node) { return node.nodeType === Node.ELEMENT_NODE; } getValue(node) { return node.nodeValue; } setValue(node, value) { node.nodeValue = value; } getElement(node) { return node; } translate(node) { const value = this.getValue(node); if (!value || typeof value !== "string") { return; } const translation = map.get(value.trim()); if (translation === undefined) { return; } if (typeof translation === "string") { this.setValue(node, translation); } else if (Array.isArray(translation)) { let finalTranslation = null; for (const subTranslation of translation) { if (typeof subTranslation === "string") { finalTranslation = subTranslation; } else { const { text, selector } = subTranslation; if (this.getElement(node).matches(selector)) { finalTranslation = text; } } } if (finalTranslation !== null) { this.setValue(node, finalTranslation); } } else { const { text, selector } = translation; if (this.getElement(node).matches(selector)) { this.setValue(node, text); } } } // https://stackoverflow.com/questions/10730309/find-all-text-nodes-in-html-page static walk(rootElement, action) { const walker = document.createTreeWalker(rootElement, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null, false); let node = walker.nextNode(); while (node) { action(node); node = walker.nextNode(); } } static translate(rootElement) { if (rootElement.nodeType === Node.TEXT_NODE) { Translator.textNode.translate(rootElement); return; } Translator.walk(rootElement, node => { for (const translator of Translator.allTranslators) { if (translator.accepts(node)) { translator.translate(node); } } }); } static translateCssMatches() { const selectors = map.get("*"); if (!selectors) { return; } for (const { selector, text } of selectors) { const element = document.querySelector(selector); if (element) { [...element.childNodes].filter(it => it.nodeType === Node.TEXT_NODE).forEach(it => it.nodeValue = text); } } } } class TextNodeTranslator extends Translator { accepts(node) { return node.nodeType === Node.TEXT_NODE; } getElement(node) { return node.parentElement; } } class TitleTranslator extends Translator { getValue(node) { return node.getAttribute("title"); } setValue(node, value) { node.setAttribute("title", value); } } class PlaceholderTranslator extends Translator { accepts(node) { return node.nodeName === "INPUT" && node.type.toUpperCase() === "TEXT" || node.nodeName === "TEXTAREA"; } getValue(node) { return node.getAttribute("placeholder"); } setValue(node, value) { node.setAttribute("placeholder", value); } } Translator.textNode = new TextNodeTranslator; Translator.title = new TitleTranslator; Translator.placeholder = new PlaceholderTranslator; Translator.allTranslators = [Translator.textNode, Translator.title, Translator.placeholder]; Translator.translate(document.body); Translator.translateCssMatches(); Observer.childListSubtree("body", records => { records.forEach(it => { if (it.addedNodes.length > 0) { Translator.translateCssMatches(); } it.addedNodes.forEach(node => { Translator.translate(node); }); }); }); })();