Adds a translate buttons for character messages in the chat for beta.character.ai
当前为
// ==UserScript==
// @name Translate Buttons for beta.character.ai
// @namespace TranslateButtonsForBetaCharacterAI
// @description Adds a translate buttons for character messages in the chat for beta.character.ai
// @version 2.0.1
// @author CriDos
// @icon https://www.google.com/s2/favicons?sz=64&domain=beta.character.ai
// @match https://beta.character.ai/chat?char=*
// @grant GM_xmlhttpRequest
// @run-at document-end
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js
// @license MIT
// ==/UserScript==
'use strict';
console.log(`Translate Buttons initializing...`);
let debug = false;
setInterval(addTranslateButtons, 1000);
function addTranslateButtons() {
var charMessages = document.getElementsByClassName("char-msg");
for (var i = 0; i < charMessages.length; i++) {
const msgNode = charMessages[i];
const markdownWrapper = msgNode.querySelector(".markdown-wrapper");
if (!markdownWrapper) {
continue;
}
const parentMsgMarkdown = msgNode.parentElement;
if (parentMsgMarkdown.isAutoTranslate) {
continue;
}
parentMsgMarkdown.isAutoTranslate = true;
setInterval(async () => {
await translateNode(msgNode);
}, 500);
}
}
async function translateNode(node) {
const translateClassName = "translate-markdown";
const parentMsgMarkdown = node.parentElement;
const msgMarkdownContent = node.outerHTML;
if (node.storeContent == msgMarkdownContent) {
return;
}
node.storeContent = msgMarkdownContent;
var translateNode = parentMsgMarkdown.querySelector(`.${translateClassName}`);
if (translateNode == null) {
translateNode = node.cloneNode(true);
translateNode.classList.add(translateClassName);
parentMsgMarkdown.insertBefore(translateNode, parentMsgMarkdown.firstChild);
}
var msgMarkdownClone = node.cloneNode(true);
msgMarkdownClone.classList.add(translateClassName);
const msgMarkdownCloneContent = msgMarkdownClone.outerHTML;
msgMarkdownClone = null;
translateNode.outerHTML = await translateHTML(msgMarkdownCloneContent, "en", navigator.language);
}
async function translateHTML(html, sLang, tLang) {
const excludeTagRegex = /<(pre|code)[^>]*>([\s\S]*?)<\/(pre|code)>/g;
const excludeTags = [];
const excludePlaceholder = 'e0x0c';
let translateHTML = html;
let excludeTagsMatch;
while (excludeTagsMatch = excludeTagRegex.exec(html)) {
excludeTags.push(excludeTagsMatch[0]);
translateHTML = translateHTML.replace(excludeTagsMatch[0], `${excludePlaceholder}${excludeTags.length - 1}`);
}
if (debug) {
console.log(`preTranslateHTML: ${html}`);
}
translateHTML = await translateText(translateHTML, sLang, tLang);
for (let i = 0; i < excludeTags.length; i++) {
translateHTML = translateHTML.replace(`${excludePlaceholder}${i}`, excludeTags[i]);
}
if (debug) {
console.log(`postTranslateHTML: ${translateHTML}`);
}
return translateHTML;
}
async function translateText(text, sLang, tLang) {
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&format=text&sl=${sLang}&tl=${tLang}&dt=t&q=${encodeURIComponent(text)}`;
try {
if (debug) {
console.log(`preTranslate: ${text}`);
}
const response = await doXHR(url);
const responseText = JSON.parse(response.responseText);
let postTranslate = "";
responseText[0].forEach(part => {
postTranslate += part[0];
});
if (debug) {
console.log(`postTranslate: ${postTranslate}`);
}
return postTranslate;
} catch (error) {
console.error(error);
}
}
async function doXHR(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}