dotfiles/.config/spicetify/CustomApps/lyrics-plus/Translator.js
2024-06-26 12:05:08 +02:00

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));
}
}