146 lines
3.7 KiB
JavaScript
146 lines
3.7 KiB
JavaScript
const kuroshiroPath = "https://cdn.jsdelivr.net/npm/kuroshiro@1.2.0/dist/kuroshiro.min.js";
|
|
const kuromojiPath = "https://cdn.jsdelivr.net/npm/kuroshiro-analyzer-kuromoji@1.1.0/dist/kuroshiro-analyzer-kuromoji.min.js";
|
|
const aromanize = "https://cdn.jsdelivr.net/npm/aromanize@0.1.5/aromanize.min.js";
|
|
const openCCPath = "https://cdn.jsdelivr.net/npm/opencc-js@1.0.5/dist/umd/full.min.js";
|
|
|
|
const dictPath = "https:/cdn.jsdelivr.net/npm/kuromoji@0.1.2/dict";
|
|
|
|
class Translator {
|
|
constructor(lang) {
|
|
this.finished = {
|
|
ja: false,
|
|
ko: false,
|
|
zh: false
|
|
};
|
|
|
|
this.applyKuromojiFix();
|
|
this.injectExternals(lang);
|
|
this.createTranslator(lang);
|
|
}
|
|
|
|
includeExternal(url) {
|
|
if (CONFIG.visual.translate && !document.querySelector(`script[src="${url}"]`)) {
|
|
const script = document.createElement("script");
|
|
script.setAttribute("type", "text/javascript");
|
|
script.setAttribute("src", url);
|
|
document.head.appendChild(script);
|
|
}
|
|
}
|
|
|
|
injectExternals(lang) {
|
|
switch (lang?.slice(0, 2)) {
|
|
case "ja":
|
|
this.includeExternal(kuromojiPath);
|
|
this.includeExternal(kuroshiroPath);
|
|
break;
|
|
case "ko":
|
|
this.includeExternal(aromanize);
|
|
break;
|
|
case "zh":
|
|
this.includeExternal(openCCPath);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix an issue with kuromoji when loading dict from external urls
|
|
* Adapted from: https://github.com/mobilusoss/textlint-browser-runner/pull/7
|
|
*/
|
|
applyKuromojiFix() {
|
|
if (typeof XMLHttpRequest.prototype.realOpen !== "undefined") return;
|
|
XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open;
|
|
XMLHttpRequest.prototype.open = function (method, url, bool) {
|
|
if (url.indexOf(dictPath.replace("https://", "https:/")) === 0) {
|
|
this.realOpen(method, url.replace("https:/", "https://"), bool);
|
|
} else {
|
|
this.realOpen(method, url, bool);
|
|
}
|
|
};
|
|
}
|
|
|
|
async createTranslator(lang) {
|
|
switch (lang.slice(0, 2)) {
|
|
case "ja":
|
|
if (this.kuroshiro) return;
|
|
if (typeof Kuroshiro === "undefined" || typeof KuromojiAnalyzer === "undefined") {
|
|
await Translator.#sleep(50);
|
|
return this.createTranslator(lang);
|
|
}
|
|
|
|
this.kuroshiro = new Kuroshiro.default();
|
|
this.kuroshiro.init(new KuromojiAnalyzer({ dictPath })).then(
|
|
function () {
|
|
this.finished.ja = true;
|
|
}.bind(this)
|
|
);
|
|
|
|
break;
|
|
case "ko":
|
|
if (this.Aromanize) return;
|
|
if (typeof Aromanize === "undefined") {
|
|
await Translator.#sleep(50);
|
|
return this.createTranslator(lang);
|
|
}
|
|
|
|
this.Aromanize = Aromanize;
|
|
this.finished.ko = true;
|
|
break;
|
|
case "zh":
|
|
if (this.OpenCC) return;
|
|
if (typeof OpenCC === "undefined") {
|
|
await Translator.#sleep(50);
|
|
return this.createTranslator(lang);
|
|
}
|
|
|
|
this.OpenCC = OpenCC;
|
|
this.finished.zh = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
async romajifyText(text, target = "romaji", mode = "spaced") {
|
|
if (!this.finished.ja) {
|
|
await Translator.#sleep(100);
|
|
return this.romajifyText(text, target, mode);
|
|
}
|
|
|
|
return this.kuroshiro.convert(text, {
|
|
to: target,
|
|
mode: mode
|
|
});
|
|
}
|
|
|
|
async convertToRomaja(text, target) {
|
|
if (!this.finished.ko) {
|
|
await Translator.#sleep(100);
|
|
return this.convertToRomaja(text, target);
|
|
}
|
|
|
|
if (target === "hangul") return text;
|
|
return Aromanize.hangulToLatin(text, "rr-translit");
|
|
}
|
|
|
|
async convertChinese(text, from, target) {
|
|
if (!this.finished.zh) {
|
|
await Translator.#sleep(100);
|
|
return this.convertChinese(text, from, target);
|
|
}
|
|
|
|
const converter = this.OpenCC.Converter({
|
|
from: from,
|
|
to: target
|
|
});
|
|
|
|
return converter(text);
|
|
}
|
|
|
|
/**
|
|
* Async wrapper of `setTimeout`.
|
|
*
|
|
* @param {number} ms
|
|
* @returns {Promise<void>}
|
|
*/
|
|
static async #sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
}
|