Format code

This commit is contained in:
the1812 2019-11-17 13:48:13 +08:00
parent dceebf05aa
commit 61b25eeedc

View File

@ -1,158 +1,125 @@
const languageCodeMap: { [key: string]: string } = { const languageCodeMap: { [key: string]: string } = {
"日本語": "ja-JP", "日本語": "ja-JP",
"English": "en-US", "English": "en-US",
"Deutsch": "de-DE", "Deutsch": "de-DE",
}; };
export class Translator export class Translator {
{ static textNode: TextNodeTranslator;
static textNode: TextNodeTranslator; static title: TitleTranslator;
static title: TitleTranslator; static placeholder: PlaceholderTranslator;
static placeholder: PlaceholderTranslator; static settingsDropdown: SettingsDropdownTranslator;
static settingsDropdown: SettingsDropdownTranslator; static sensitiveTranslators: Translator[];
static sensitiveTranslators: Translator[]; static map: Map<string, any>;
static map: Map<string, any>; static regex: [RegExp, string][];
static regex: [RegExp, string][];
accepts(node: Node) { return node.nodeType === Node.ELEMENT_NODE; } accepts(node: Node) { return node.nodeType === Node.ELEMENT_NODE; }
getValue(node: Node) { return node.nodeValue; } getValue(node: Node) { return node.nodeValue; }
setValue(node: Node, value: string) { node.nodeValue = value; } setValue(node: Node, value: string) { node.nodeValue = value; }
getElement(node: Node) { return node as Element; } getElement(node: Node) { return node as Element; }
translate(node: Node) translate(node: Node) {
{ let value = this.getValue(node);
let value = this.getValue(node); if (!value || typeof value !== "string" || value === "*") {
if (!value || typeof value !== "string" || value === "*") return;
{
return;
}
value = value.trim();
const translation = Translator.map.get(value);
if (translation === undefined)
{
const result = Translator.regex.find(([r]) => r.test(value!));
if (result)
{
const [regex, replacement] = result;
this.setValue(node, value.replace(regex, replacement));
}
}
else 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, not } = subTranslation;
if (this.getElement(node).matches(selector) !== Boolean(not))
{
finalTranslation = text;
}
}
}
if (finalTranslation !== null)
{
this.setValue(node, finalTranslation);
}
}
else
{
const { text, selector, not } = translation;
if (this.getElement(node).matches(selector) !== Boolean(not))
{
this.setValue(node, text);
}
}
} }
// https://stackoverflow.com/questions/10730309/find-all-text-nodes-in-html-page value = value.trim();
static walk(rootElement: Node, action: (node: Node) => void) const translation = Translator.map.get(value);
{ if (translation === undefined) {
const walker = document.createTreeWalker(rootElement, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null, false); const result = Translator.regex.find(([r]) => r.test(value!));
let node = walker.nextNode(); if (result) {
while (node) const [regex, replacement] = result;
{ this.setValue(node, value.replace(regex, replacement));
action(node); }
node = walker.nextNode();
}
} }
static translate(rootElement: Node) else if (typeof translation === "string") {
{ this.setValue(node, translation);
if (rootElement.nodeType === Node.TEXT_NODE)
{
Translator.textNode.translate(rootElement);
return;
}
const translateNode = (node: Node) =>
{
for (const translator of Translator.sensitiveTranslators)
{
if (translator.accepts(node))
{
translator.translate(node);
}
}
};
translateNode(rootElement);
Translator.walk(rootElement, translateNode);
} }
static translateCssMatches() else if (Array.isArray(translation)) {
{ let finalTranslation = null;
const selectors = Translator.map.get("*"); for (const subTranslation of translation) {
if (!selectors) if (typeof subTranslation === "string") {
{ finalTranslation = subTranslation;
return;
} }
for (const { selector, text } of selectors) else {
{ const { text, selector, not } = subTranslation;
const element = document.querySelector(selector); if (this.getElement(node).matches(selector) !== Boolean(not)) {
if (element) finalTranslation = text;
{ }
[...element.childNodes].filter(it => it.nodeType === Node.TEXT_NODE).forEach(it => it.nodeValue = text);
}
} }
}
if (finalTranslation !== null) {
this.setValue(node, finalTranslation);
}
} }
else {
const { text, selector, not } = translation;
if (this.getElement(node).matches(selector) !== Boolean(not)) {
this.setValue(node, text);
}
}
}
// https://stackoverflow.com/questions/10730309/find-all-text-nodes-in-html-page
static walk(rootElement: Node, action: (node: Node) => void) {
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: Node) {
if (rootElement.nodeType === Node.TEXT_NODE) {
Translator.textNode.translate(rootElement);
return;
}
const translateNode = (node: Node) => {
for (const translator of Translator.sensitiveTranslators) {
if (translator.accepts(node)) {
translator.translate(node);
}
}
};
translateNode(rootElement);
Translator.walk(rootElement, translateNode);
}
static translateCssMatches() {
const selectors = Translator.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);
}
}
}
} }
export class TextNodeTranslator extends Translator export class TextNodeTranslator extends Translator {
{ accepts(node: Node) { return node.nodeType === Node.TEXT_NODE; }
accepts(node: Node) { return node.nodeType === Node.TEXT_NODE; } getElement(node: Node) {
getElement(node: Node) return node.parentElement!;
{ }
return node.parentElement!;
}
} }
export class TitleTranslator extends Translator export class TitleTranslator extends Translator {
{ getValue(node: Node) { return (node as Element).getAttribute("title"); }
getValue(node: Node) { return (node as Element).getAttribute("title"); } setValue(node: Node, value: string) {
setValue(node: Node, value: string) (node as Element).setAttribute("title", value);
{ }
(node as Element).setAttribute("title", value);
}
} }
export class PlaceholderTranslator extends Translator export class PlaceholderTranslator extends Translator {
{ // accepts(node: Node) { return node.nodeName === "INPUT" && (node as HTMLInputElement).type.toUpperCase() === "TEXT" || node.nodeName === "TEXTAREA"; }
// accepts(node: Node) { return node.nodeName === "INPUT" && (node as HTMLInputElement).type.toUpperCase() === "TEXT" || node.nodeName === "TEXTAREA"; } getValue(node: Node) { return (node as Element).getAttribute("placeholder"); }
getValue(node: Node) { return (node as Element).getAttribute("placeholder"); } setValue(node: Node, value: string) {
setValue(node: Node, value: string) (node as Element).setAttribute("placeholder", value);
{ }
(node as Element).setAttribute("placeholder", value);
}
} }
export class SettingsDropdownTranslator extends Translator export class SettingsDropdownTranslator extends Translator {
{ accepts(node: Node) { return node instanceof HTMLInputElement && node.hasAttribute("key"); }
accepts(node: Node) { return node instanceof HTMLInputElement && node.hasAttribute("key"); } getValue(node: Node) { return (node as HTMLInputElement).value; }
getValue(node: Node) { return (node as HTMLInputElement).value; } setValue(node: Node, value: string) {
setValue(node: Node, value: string) (node as HTMLInputElement).value = value;
{ }
(node as HTMLInputElement).value = value;
}
} }
Translator.textNode = new TextNodeTranslator; Translator.textNode = new TextNodeTranslator;
@ -161,53 +128,42 @@ Translator.placeholder = new PlaceholderTranslator;
Translator.settingsDropdown = new SettingsDropdownTranslator; Translator.settingsDropdown = new SettingsDropdownTranslator;
Translator.sensitiveTranslators = [Translator.textNode, Translator.title, Translator.placeholder]; Translator.sensitiveTranslators = [Translator.textNode, Translator.title, Translator.placeholder];
const startTranslate = async () => const startTranslate = async () => {
{ const languageCode = languageCodeMap[settings.i18nLanguage];
const languageCode = languageCodeMap[settings.i18nLanguage]; const { map, regex } = await import(`./i18n.${languageCode}`);
const { map, regex } = await import(`./i18n.${languageCode}`); document.documentElement.setAttribute("lang", languageCode);
document.documentElement.setAttribute("lang", languageCode); Translator.map = map as Map<string, any>;
Translator.map = map as Map<string, any>; Translator.regex = [...regex.entries()] as Array<[RegExp, string]>;
Translator.regex = [...regex.entries()] as Array<[RegExp, string]>;
Translator.translate(document.body); Translator.translate(document.body);
Translator.translateCssMatches(); Translator.translateCssMatches();
Observer.observe("body", records => Observer.observe("body", records => {
{ records.forEach(it => {
records.forEach(it => if (it.type === "childList") {
{ if (it.addedNodes.length > 0) {
if (it.type === "childList") Translator.translateCssMatches();
{ }
if (it.addedNodes.length > 0) it.addedNodes.forEach(node => {
{ Translator.translate(node);
Translator.translateCssMatches();
}
it.addedNodes.forEach(node =>
{
Translator.translate(node);
});
}
else if (it.type === "characterData")
{
Translator.textNode.translate(it.target);
}
else if (it.type === "attributes")
{
if (it.attributeName === "title")
{
Translator.title.translate(it.target);
}
else if (it.attributeName === "placeholder")
{
Translator.placeholder.translate(it.target);
}
}
}); });
}, { characterData: true, childList: true, subtree: true, attributes: true }); }
const iconPanel = await SpinQuery.select(".gui-settings-icon-panel"); else if (it.type === "characterData") {
iconPanel!.addEventListener("be:load", () => Translator.textNode.translate(it.target);
{ }
Translator.walk(document.querySelector(".gui-settings-box")!, node => Translator.settingsDropdown.translate(node)); else if (it.type === "attributes") {
}, { once: true }); if (it.attributeName === "title") {
Translator.title.translate(it.target);
}
else if (it.attributeName === "placeholder") {
Translator.placeholder.translate(it.target);
}
}
});
}, { characterData: true, childList: true, subtree: true, attributes: true });
const iconPanel = await SpinQuery.select(".gui-settings-icon-panel");
iconPanel!.addEventListener("be:load", () => {
Translator.walk(document.querySelector(".gui-settings-box")!, node => Translator.settingsDropdown.translate(node));
}, { once: true });
}; };
startTranslate(); startTranslate();
// if (document.readyState === "complete") // if (document.readyState === "complete")
@ -220,15 +176,15 @@ startTranslate();
// } // }
export default { export default {
export: { export: {
Translator, Translator,
TextNodeTranslator, TextNodeTranslator,
TitleTranslator, TitleTranslator,
PlaceholderTranslator, PlaceholderTranslator,
}, },
dropdown: { dropdown: {
key: "i18nLanguage", key: "i18nLanguage",
// items: Object.keys(languageCodeMap), // items: Object.keys(languageCodeMap),
items: [`日本語`], items: [`日本語`],
}, },
}; };