Files
risuai/src/ts/translator/translator.ts
2023-11-13 23:40:30 +09:00

190 lines
4.9 KiB
TypeScript

import { get } from "svelte/store"
import { translatorPlugin } from "../plugins/plugins"
import { DataBase } from "../storage/database"
import { globalFetch } from "../storage/globalApi"
import { alertError } from "../alert"
let cache={
origin: [''],
trans: ['']
}
let waitTrans = 0
export async function translate(text:string, reverse:boolean) {
let db = get(DataBase)
const plug = await translatorPlugin(text, reverse ? db.translator: 'en', reverse ? 'en' : db.translator)
if(plug){
return plug.content
}
if(!reverse){
const ind = cache.origin.indexOf(text)
if(ind !== -1){
return cache.trans[ind]
}
}
else{
const ind = cache.trans.indexOf(text)
if(ind !== -1){
return cache.origin[ind]
}
}
return runTranslator(text, reverse, db.translator,db.aiModel.startsWith('novellist') ? 'ja' : 'en')
}
async function runTranslator(text:string, reverse:boolean, from:string,target:'en'|'ja') {
const arg = {
from: reverse ? from : target,
to: reverse ? target : from,
host: 'translate.googleapis.com',
}
const texts = text.split('\n')
let chunks:[string,boolean][] = [['', true]]
for(let i = 0; i < texts.length; i++){
if( texts[i].startsWith('{{img')
|| texts[i].startsWith('{{raw')
|| texts[i].startsWith('{{video')
|| texts[i].startsWith('{{audio')
&& texts[i].endsWith('}}')
|| texts[i].length === 0){
chunks.push([texts[i], false])
chunks.push(["", true])
}
else{
chunks[chunks.length-1][0] += texts[i]
}
}
let fullResult:string[] = []
for(const chunk of chunks){
if(chunk[1]){
const trimed = chunk[0].trim();
if(trimed.length === 0){
fullResult.push(chunk[0])
continue
}
const result = await translateMain(trimed, arg);
if(result.startsWith('ERR::')){
alertError(result)
return text
}
fullResult.push(result.trim())
}
else{
fullResult.push(chunk[0])
}
}
const result = fullResult.join("\n").trim()
cache.origin.push(reverse ? result : text)
cache.trans.push(reverse ? text : result)
return result
}
async function translateMain(text:string, arg:{from:string, to:string, host:string}){
let db = get(DataBase)
if(db.translatorType === 'deepl'){
let url = db.deeplOptions.freeApi ? "https://api-free.deepl.com/v2/translate" : "https://api.deepl.com/v2/translate"
const f = await globalFetch(url, {
headers: {
"Authorization": "DeepL-Auth-Key " + db.deeplOptions.key,
},
body: {
text: text,
source_lang: arg.from.toLocaleUpperCase(),
target_lang: arg.to.toLocaleUpperCase(),
}
})
if(!f.ok){
return 'ERR::DeepL API Error' + (await f.data)
}
return f.data.translations[0].text
}
const url = `https://${arg.host}/translate_a/single?client=gtx&dt=t&sl=${arg.from}&tl=${arg.to}&q=` + encodeURIComponent(text)
const f = await fetch(url, {
method: "GET",
})
const res = await f.json()
if(typeof(res) === 'string'){
return res as unknown as string
}
const result = (res[0].map((s) => s[0]).filter(Boolean).join('') as string).replace(/\* ([^*]+)\*/g, '*$1*').replace(/\*([^*]+) \*/g, '*$1*');
return result
}
export async function translateVox(text:string) {
const plug = await translatorPlugin(text, 'en', 'ja')
if(plug){
return plug.content
}
return jaTrans(text)
}
async function jaTrans(text:string) {
return await runTranslator(text, true, 'en','ja')
}
export async function translateHTML(html: string, reverse:boolean): Promise<string> {
const dom = new DOMParser().parseFromString(html, 'text/html');
// Recursive function to translate all text nodes
async function translateNode(node: Node): Promise<void> {
if (node.nodeType === Node.TEXT_NODE) {
// Translate the text content of the node
if(node.textContent){
node.textContent = await translate(node.textContent || '', reverse);
}
} else if(node.nodeType === Node.ELEMENT_NODE) {
// Translate child nodes
for (const child of Array.from(node.childNodes)) {
await translateNode(child);
}
}
}
// Start translation from the body element
await translateNode(dom.body);
// Serialize the DOM back to HTML
const serializer = new XMLSerializer();
const translatedHTML = serializer.serializeToString(dom.body);
// Return the translated HTML, excluding the outer <body> tags if needed
return translatedHTML
}