Add german and traditional chinese

This commit is contained in:
the1812 2019-03-27 17:25:19 +08:00
parent 5046d02d91
commit f8eff2afeb
13 changed files with 220 additions and 139 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -359,6 +359,7 @@ function loadResources()
{
const resource = new Resource(data.path, data.styles);
resource.key = key;
resource.dropdown = data.dropdown;
if (data.displayNames)
{
resource.displayName = data.displayNames[key];
@ -1545,6 +1546,12 @@ Resource.manifest = {
i18nJapanese: {
path: "i18n.ja-JP.min.js",
},
i18nTraditionalChinese: {
path: "i18n.zh-TW.min.js",
},
i18nGerman: {
path: "i18n.de-DE.min.js",
},
i18n: {
path: "i18n.min.js",
style: "important",
@ -1886,6 +1893,7 @@ class ResourceManager
}
}
await Promise.all(Object.values(Resource.manifest)
.concat(Object.values(Resource.all))
.filter(it => it.dropdown)
.map(it => applyDropdownOption(it.dropdown))
);

View File

@ -359,6 +359,7 @@ function loadResources()
{
const resource = new Resource(data.path, data.styles);
resource.key = key;
resource.dropdown = data.dropdown;
if (data.displayNames)
{
resource.displayName = data.displayNames[key];
@ -1545,6 +1546,12 @@ Resource.manifest = {
i18nJapanese: {
path: "i18n.ja-JP.min.js",
},
i18nTraditionalChinese: {
path: "i18n.zh-TW.min.js",
},
i18nGerman: {
path: "i18n.de-DE.min.js",
},
i18n: {
path: "i18n.min.js",
style: "important",
@ -1886,6 +1893,7 @@ class ResourceManager
}
}
await Promise.all(Object.values(Resource.manifest)
.concat(Object.values(Resource.all))
.filter(it => it.dropdown)
.map(it => applyDropdownOption(it.dropdown))
);

View File

@ -11,6 +11,7 @@ export function loadResources()
{
const resource = new Resource(data.path, data.styles);
resource.key = key;
resource.dropdown = data.dropdown;
if (data.displayNames)
{
resource.displayName = data.displayNames[key];

View File

@ -250,6 +250,7 @@ export class ResourceManager
}
}
await Promise.all(Object.values(Resource.manifest)
.concat(Object.values(Resource.all))
.filter(it => it.dropdown)
.map(it => applyDropdownOption(it.dropdown))
);

View File

@ -493,6 +493,12 @@ Resource.manifest = {
i18nJapanese: {
path: "i18n.ja-JP.min.js",
},
i18nTraditionalChinese: {
path: "i18n.zh-TW.min.js",
},
i18nGerman: {
path: "i18n.de-DE.min.js",
},
i18n: {
path: "i18n.min.js",
style: "important",

1
min/i18n.de-DE.min.js vendored Normal file
View File

@ -0,0 +1 @@
(()=>{return(r,e)=>{const n=new Map([[`*`,[]]]);return{export:{map:n}}}})();

2
min/i18n.min.js vendored
View File

@ -1 +1 @@
(()=>{return(e,t)=>{(async()=>{const s={"日本語":"ja-JP",English:"en-US"};const{map:a}=await t.importAsync(`i18n.${s[e.i18nLanguage]}`);class n{accepts(e){return e.nodeType===Node.ELEMENT_NODE}getValue(e){return e.nodeValue}setValue(e,t){e.nodeValue=t}getElement(e){return e}translate(e){const t=this.getValue(e);if(!t||typeof t!=="string"){return}const s=a.get(t.trim());if(s===undefined){return}if(typeof s==="string"){this.setValue(e,s)}else if(Array.isArray(s)){let t=null;for(const a of s){if(typeof a==="string"){t=a}else{const{text:s,selector:n}=a;if(this.getElement(e).matches(n)){t=s}}}if(t!==null){this.setValue(e,t)}}else{const{text:t,selector:a}=s;if(this.getElement(e).matches(a)){this.setValue(e,t)}}}static walk(e,t){const s=document.createTreeWalker(e,NodeFilter.SHOW_TEXT|NodeFilter.SHOW_ELEMENT,null,false);let a=s.nextNode();while(a){t(a);a=s.nextNode()}}static translate(e){if(e.nodeType===Node.TEXT_NODE){n.textNode.translate(e);return}n.walk(e,e=>{for(const t of n.allTranslators){if(t.accepts(e)){t.translate(e)}}})}static translateCssMatches(){const e=a.get("*");if(!e){return}for(const{selector:t,text:s}of e){const e=document.querySelector(t);if(e){[...e.childNodes].filter(e=>e.nodeType===Node.TEXT_NODE).forEach(e=>e.nodeValue=s)}}}}class r extends n{accepts(e){return e.nodeType===Node.TEXT_NODE}getElement(e){return e.parentElement}}class l extends n{getValue(e){return e.getAttribute("title")}setValue(e,t){e.setAttribute("title",t)}}class o extends n{accepts(e){return e.nodeName==="INPUT"&&e.type.toUpperCase()==="TEXT"||e.nodeName==="TEXTAREA"}getValue(e){return e.getAttribute("placeholder")}setValue(e,t){e.setAttribute("placeholder",t)}}n.textNode=new r;n.title=new l;n.placeholder=new o;n.allTranslators=[n.textNode,n.title,n.placeholder];n.translate(document.body);n.translateCssMatches();Observer.childListSubtree("body",e=>{e.forEach(e=>{if(e.addedNodes.length>0){n.translateCssMatches()}e.addedNodes.forEach(e=>{n.translate(e)})})})})()}})();
(()=>{return(e,t)=>{const s={"日本語":"ja-JP",English:"en-US","正體中文":"zh-TW",Deutsch:"de-DE"};class a{accepts(e){return e.nodeType===Node.ELEMENT_NODE}getValue(e){return e.nodeValue}setValue(e,t){e.nodeValue=t}getElement(e){return e}translate(e){const t=this.getValue(e);if(!t||typeof t!=="string"){return}const s=map.get(t.trim());if(s===undefined){return}if(typeof s==="string"){this.setValue(e,s)}else if(Array.isArray(s)){let t=null;for(const a of s){if(typeof a==="string"){t=a}else{const{text:s,selector:r}=a;if(this.getElement(e).matches(r)){t=s}}}if(t!==null){this.setValue(e,t)}}else{const{text:t,selector:a}=s;if(this.getElement(e).matches(a)){this.setValue(e,t)}}}static walk(e,t){const s=document.createTreeWalker(e,NodeFilter.SHOW_TEXT|NodeFilter.SHOW_ELEMENT,null,false);let a=s.nextNode();while(a){t(a);a=s.nextNode()}}static translate(e){if(e.nodeType===Node.TEXT_NODE){a.textNode.translate(e);return}a.walk(e,e=>{for(const t of a.allTranslators){if(t.accepts(e)){t.translate(e)}}})}static translateCssMatches(){const e=map.get("*");if(!e){return}for(const{selector:t,text:s}of e){const e=document.querySelector(t);if(e){[...e.childNodes].filter(e=>e.nodeType===Node.TEXT_NODE).forEach(e=>e.nodeValue=s)}}}}class r extends a{accepts(e){return e.nodeType===Node.TEXT_NODE}getElement(e){return e.parentElement}}class l extends a{getValue(e){return e.getAttribute("title")}setValue(e,t){e.setAttribute("title",t)}}class n extends a{accepts(e){return e.nodeName==="INPUT"&&e.type.toUpperCase()==="TEXT"||e.nodeName==="TEXTAREA"}getValue(e){return e.getAttribute("placeholder")}setValue(e,t){e.setAttribute("placeholder",t)}}a.textNode=new r;a.title=new l;a.placeholder=new n;a.allTranslators=[a.textNode,a.title,a.placeholder];(async()=>{const{map:r}=await t.importAsync(`i18n.${s[e.i18nLanguage]}`);a.translate(document.body);a.translateCssMatches();Observer.childListSubtree("body",e=>{e.forEach(e=>{if(e.addedNodes.length>0){a.translateCssMatches()}e.addedNodes.forEach(e=>{a.translate(e)})})})})();return{export:{Translator:a,TextNodeTranslator:r,TitleTranslator:l,PlaceholderTranslator:n}}}})();

1
min/i18n.zh-TW.min.js vendored Normal file
View File

@ -0,0 +1 @@
(()=>{return(r,e)=>{const n=new Map([[`*`,[]]]);return{export:{map:n}}}})();

9
utils/i18n/i18n.de-DE.js Normal file
View File

@ -0,0 +1,9 @@
export const map = new Map([
/* Your translation here */
[`*`, [
/* CSS translation here */
]],
]);
export default {
export: { map },
};

View File

@ -1,139 +1,143 @@
const languageCodeMap = {
"日本語": "ja-JP",
"English": "en-US",
"正體中文": "zh-TW",
"Deutsch": "de-DE",
};
export 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);
}
}
}
}
export class TextNodeTranslator extends Translator
{
accepts(node) { return node.nodeType === Node.TEXT_NODE; }
getElement(node)
{
return node.parentElement;
}
}
export class TitleTranslator extends Translator
{
getValue(node) { return node.getAttribute("title"); }
setValue(node, value)
{
node.setAttribute("title", value);
}
}
export 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];
(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();
@ -151,4 +155,17 @@
});
});
});
})();
})();
export default {
export: {
Translator,
TextNodeTranslator,
TitleTranslator,
PlaceholderTranslator,
},
// dropdown: {
// key: "i18nLanguage",
// items: Object.keys(languageCodeMap),
// },
};

9
utils/i18n/i18n.zh-TW.js Normal file
View File

@ -0,0 +1,9 @@
export const map = new Map([
/* Your translation here */
[`*`, [
/* CSS translation here */
]],
]);
export default {
export: { map },
};