add a button to translate input message

This commit is contained in:
evpeople 2024-04-27 13:46:13 +08:00
parent 47b6562605
commit 4521dde455
No known key found for this signature in database
GPG Key ID: CF5768B6A73046FF
1 changed files with 443 additions and 404 deletions

View File

@ -1,18 +1,9 @@
export { translate }; import {callPopup, event_types, eventSource, getRequestHeaders, reloadCurrentChat, saveSettingsDebounced, substituteParams, updateMessageBlock,} from '../../../script.js';
import {extension_settings, getContext} from '../../extensions.js';
import {findSecret, secret_state, writeSecret} from '../../secrets.js';
import {splitRecursive} from '../../utils.js';
import { export {translate};
callPopup,
eventSource,
event_types,
getRequestHeaders,
reloadCurrentChat,
saveSettingsDebounced,
substituteParams,
updateMessageBlock,
} from '../../../script.js';
import { extension_settings, getContext } from '../../extensions.js';
import { findSecret, secret_state, writeSecret } from '../../secrets.js';
import { splitRecursive } from '../../utils.js';
export const autoModeOptions = { export const autoModeOptions = {
NONE: 'none', NONE: 'none',
@ -142,12 +133,21 @@ const KEY_REQUIRED = ['deepl', 'libre'];
const LOCAL_URL = ['libre', 'oneringtranslator', 'deeplx', 'lingva']; const LOCAL_URL = ['libre', 'oneringtranslator', 'deeplx', 'lingva'];
function showKeysButton() { function showKeysButton() {
const providerRequiresKey = KEY_REQUIRED.includes(extension_settings.translate.provider); const providerRequiresKey =
const providerOptionalUrl = LOCAL_URL.includes(extension_settings.translate.provider); KEY_REQUIRED.includes(extension_settings.translate.provider);
const providerOptionalUrl =
LOCAL_URL.includes(extension_settings.translate.provider);
$('#translate_key_button').toggle(providerRequiresKey); $('#translate_key_button').toggle(providerRequiresKey);
$('#translate_key_button').toggleClass('success', Boolean(secret_state[extension_settings.translate.provider])); $('#translate_key_button')
.toggleClass(
'success',
Boolean(secret_state[extension_settings.translate.provider]));
$('#translate_url_button').toggle(providerOptionalUrl); $('#translate_url_button').toggle(providerOptionalUrl);
$('#translate_url_button').toggleClass('success', Boolean(secret_state[extension_settings.translate.provider + '_url'])); $('#translate_url_button')
.toggleClass(
'success',
Boolean(
secret_state[extension_settings.translate.provider + '_url']));
} }
function loadSettings() { function loadSettings() {
@ -157,14 +157,21 @@ function loadSettings() {
} }
} }
$(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', true); $(`#translation_provider option[value="${
$(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', true); extension_settings.translate.provider}"]`)
$(`#translation_auto_mode option[value="${extension_settings.translate.auto_mode}"]`).attr('selected', true); .attr('selected', true);
$(`#translation_target_language option[value="${
extension_settings.translate.target_language}"]`)
.attr('selected', true);
$(`#translation_auto_mode option[value="${
extension_settings.translate.auto_mode}"]`)
.attr('selected', true);
showKeysButton(); showKeysButton();
} }
async function translateImpersonate(text) { async function translateImpersonate(text) {
const translatedText = await translate(text, extension_settings.translate.target_language); const translatedText =
await translate(text, extension_settings.translate.target_language);
$('#send_textarea').val(translatedText); $('#send_textarea').val(translatedText);
} }
@ -181,22 +188,24 @@ async function translateIncomingMessage(messageId) {
return; return;
} }
const textToTranslate = substituteParams(message.mes, context.name1, message.name); const textToTranslate =
const translation = await translate(textToTranslate, extension_settings.translate.target_language); substituteParams(message.mes, context.name1, message.name);
const translation = await translate(
textToTranslate, extension_settings.translate.target_language);
message.extra.display_text = translation; message.extra.display_text = translation;
updateMessageBlock(messageId, message); updateMessageBlock(messageId, message);
} }
async function translateProviderOneRing(text, lang) { async function translateProviderOneRing(text, lang) {
let from_lang = lang == extension_settings.translate.internal_language let from_lang = lang == extension_settings.translate.internal_language ?
? extension_settings.translate.target_language extension_settings.translate.target_language :
: extension_settings.translate.internal_language; extension_settings.translate.internal_language;
const response = await fetch('/api/translate/onering', { const response = await fetch('/api/translate/onering', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ text: text, from_lang: from_lang, to_lang: lang }), body: JSON.stringify({text: text, from_lang: from_lang, to_lang: lang}),
}); });
if (response.ok) { if (response.ok) {
@ -217,7 +226,7 @@ async function translateProviderLibre(text, lang) {
const response = await fetch('/api/translate/libre', { const response = await fetch('/api/translate/libre', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ text: text, lang: lang }), body: JSON.stringify({text: text, lang: lang}),
}); });
if (response.ok) { if (response.ok) {
@ -238,7 +247,7 @@ async function translateProviderGoogle(text, lang) {
const response = await fetch('/api/translate/google', { const response = await fetch('/api/translate/google', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ text: text, lang: lang }), body: JSON.stringify({text: text, lang: lang}),
}); });
if (response.ok) { if (response.ok) {
@ -259,7 +268,7 @@ async function translateProviderLingva(text, lang) {
const response = await fetch('/api/translate/lingva', { const response = await fetch('/api/translate/lingva', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ text: text, lang: lang }), body: JSON.stringify({text: text, lang: lang}),
}); });
if (response.ok) { if (response.ok) {
@ -284,7 +293,7 @@ async function translateProviderDeepl(text, lang) {
const response = await fetch('/api/translate/deepl', { const response = await fetch('/api/translate/deepl', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ text: text, lang: lang }), body: JSON.stringify({text: text, lang: lang}),
}); });
if (response.ok) { if (response.ok) {
@ -305,7 +314,7 @@ async function translateProviderDeepLX(text, lang) {
const response = await fetch('/api/translate/deeplx', { const response = await fetch('/api/translate/deeplx', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ text: text, lang: lang }), body: JSON.stringify({text: text, lang: lang}),
}); });
if (response.ok) { if (response.ok) {
@ -326,7 +335,7 @@ async function translateProviderBing(text, lang) {
const response = await fetch('/api/translate/bing', { const response = await fetch('/api/translate/bing', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ text: text, lang: lang }), body: JSON.stringify({text: text, lang: lang}),
}); });
if (response.ok) { if (response.ok) {
@ -341,7 +350,8 @@ async function translateProviderBing(text, lang) {
* Splits text into chunks and translates each chunk separately * Splits text into chunks and translates each chunk separately
* @param {string} text Text to translate * @param {string} text Text to translate
* @param {string} lang Target language code * @param {string} lang Target language code
* @param {(text: string, lang: string) => Promise<string>} translateFn Function to translate a single chunk (must return a Promise) * @param {(text: string, lang: string) => Promise<string>} translateFn Function
* to translate a single chunk (must return a Promise)
* @param {number} chunkSize Maximum chunk size * @param {number} chunkSize Maximum chunk size
* @returns {Promise<string>} Translated text * @returns {Promise<string>} Translated text
*/ */
@ -375,19 +385,24 @@ async function translate(text, lang) {
case 'libre': case 'libre':
return await translateProviderLibre(text, lang); return await translateProviderLibre(text, lang);
case 'google': case 'google':
return await chunkedTranslate(text, lang, translateProviderGoogle, 5000); return await chunkedTranslate(
text, lang, translateProviderGoogle, 5000);
case 'lingva': case 'lingva':
return await chunkedTranslate(text, lang, translateProviderLingva, 5000); return await chunkedTranslate(
text, lang, translateProviderLingva, 5000);
case 'deepl': case 'deepl':
return await translateProviderDeepl(text, lang); return await translateProviderDeepl(text, lang);
case 'deeplx': case 'deeplx':
return await chunkedTranslate(text, lang, translateProviderDeepLX, 1500); return await chunkedTranslate(
text, lang, translateProviderDeepLX, 1500);
case 'oneringtranslator': case 'oneringtranslator':
return await translateProviderOneRing(text, lang); return await translateProviderOneRing(text, lang);
case 'bing': case 'bing':
return await chunkedTranslate(text, lang, translateProviderBing, 1000); return await chunkedTranslate(text, lang, translateProviderBing, 1000);
default: default:
console.error('Unknown translation provider', extension_settings.translate.provider); console.error(
'Unknown translation provider',
extension_settings.translate.provider);
return text; return text;
} }
} catch (error) { } catch (error) {
@ -406,7 +421,8 @@ async function translateOutgoingMessage(messageId) {
const originalText = message.mes; const originalText = message.mes;
message.extra.display_text = originalText; message.extra.display_text = originalText;
message.mes = await translate(originalText, extension_settings.translate.internal_language); message.mes = await translate(
originalText, extension_settings.translate.internal_language);
updateMessageBlock(messageId, message); updateMessageBlock(messageId, message);
console.log('translateOutgoingMessage', messageId); console.log('translateOutgoingMessage', messageId);
@ -424,6 +440,13 @@ function createEventHandler(translateFunction, shouldTranslateFunction) {
}; };
} }
async function onTranslateInputMessageClick() {
const ta = document.querySelector('#send_textarea');
toastr.info(`Input Message is translating`, 'Please wait...');
const translatedText =
await translate(ta.value, extension_settings.translate.internal_language);
$('#send_textarea').val(translatedText);
}
// Prevents the chat from being translated in parallel // Prevents the chat from being translated in parallel
let translateChatExecuting = false; let translateChatExecuting = false;
@ -437,7 +460,8 @@ async function onTranslateChatClick() {
const context = getContext(); const context = getContext();
const chat = context.chat; const chat = context.chat;
toastr.info(`${chat.length} message(s) queued for translation.`, 'Please wait...'); toastr.info(
`${chat.length} message(s) queued for translation.`, 'Please wait...');
for (let i = 0; i < chat.length; i++) { for (let i = 0; i < chat.length; i++) {
await translateIncomingMessage(i); await translateIncomingMessage(i);
@ -453,7 +477,9 @@ async function onTranslateChatClick() {
} }
async function onTranslationsClearClick() { async function onTranslationsClearClick() {
const confirm = await callPopup('<h3>Are you sure?</h3>This will remove translated text from all messages in the current chat. This action cannot be undone.', 'confirm'); const confirm = await callPopup(
'<h3>Are you sure?</h3>This will remove translated text from all messages in the current chat. This action cannot be undone.',
'confirm');
if (!confirm) { if (!confirm) {
return; return;
@ -477,11 +503,13 @@ async function translateMessageEdit(messageId) {
const chat = context.chat; const chat = context.chat;
const message = chat[messageId]; const message = chat[messageId];
if (message.is_system || extension_settings.translate.auto_mode == autoModeOptions.NONE) { if (message.is_system ||
extension_settings.translate.auto_mode == autoModeOptions.NONE) {
return; return;
} }
if ((message.is_user && shouldTranslate(outgoingTypes)) || (!message.is_user && shouldTranslate(incomingTypes))) { if ((message.is_user && shouldTranslate(outgoingTypes)) ||
(!message.is_user && shouldTranslate(incomingTypes))) {
await translateIncomingMessage(messageId); await translateIncomingMessage(messageId);
} }
} }
@ -504,9 +532,12 @@ async function onMessageTranslateClick() {
await context.saveChat(); await context.saveChat();
} }
const handleIncomingMessage = createEventHandler(translateIncomingMessage, () => shouldTranslate(incomingTypes)); const handleIncomingMessage = createEventHandler(
const handleOutgoingMessage = createEventHandler(translateOutgoingMessage, () => shouldTranslate(outgoingTypes)); translateIncomingMessage, () => shouldTranslate(incomingTypes));
const handleImpersonateReady = createEventHandler(translateImpersonate, () => shouldTranslate(incomingTypes)); const handleOutgoingMessage = createEventHandler(
translateOutgoingMessage, () => shouldTranslate(outgoingTypes));
const handleImpersonateReady = createEventHandler(
translateImpersonate, () => shouldTranslate(incomingTypes));
const handleMessageEdit = createEventHandler(translateMessageEdit, () => true); const handleMessageEdit = createEventHandler(translateMessageEdit, () => true);
window['translate'] = translate; window['translate'] = translate;
@ -555,14 +586,21 @@ jQuery(() => {
<div id="translate_chat" class="list-group-item flex-container flexGap5"> <div id="translate_chat" class="list-group-item flex-container flexGap5">
<div class="fa-solid fa-language extensionsMenuExtensionButton" /></div> <div class="fa-solid fa-language extensionsMenuExtensionButton" /></div>
Translate Chat Translate Chat
</div>`; </div>
<div id="translate_input_message" class="list-group-item flex-container flexGap5">
<div class="fa-solid fa-language extensionsMenuExtensionButton" /></div>
Translate Input Message
</div>
`;
$('#extensionsMenu').append(buttonHtml); $('#extensionsMenu').append(buttonHtml);
$('#extensions_settings2').append(html); $('#extensions_settings2').append(html);
$('#translate_chat').on('click', onTranslateChatClick); $('#translate_chat').on('click', onTranslateChatClick);
$('#translate_input_message').on('click', onTranslateInputMessageClick);
$('#translation_clear').on('click', onTranslationsClearClick); $('#translation_clear').on('click', onTranslationsClearClick);
for (const [key, value] of Object.entries(languageCodes)) { for (const [key, value] of Object.entries(languageCodes)) {
$('#translation_target_language').append(`<option value="${value}">${key}</option>`); $('#translation_target_language')
.append(`<option value="${value}">${key}</option>`);
} }
$('#translation_auto_mode').on('change', (event) => { $('#translation_auto_mode').on('change', (event) => {
@ -599,7 +637,8 @@ jQuery(() => {
'oneringtranslator': 'http://127.0.0.1:4990/translate', 'oneringtranslator': 'http://127.0.0.1:4990/translate',
'deeplx': 'http://127.0.0.1:1188/translate', 'deeplx': 'http://127.0.0.1:1188/translate',
}; };
const popupText = `<h3>${optionText} API URL</h3><i>Example: <tt>${String(exampleURLs[extension_settings.translate.provider])}</tt></i>`; const popupText = `<h3>${optionText} API URL</h3><i>Example: <tt>${
String(exampleURLs[extension_settings.translate.provider])}</tt></i>`;
const secretKey = extension_settings.translate.provider + '_url'; const secretKey = extension_settings.translate.provider + '_url';
const savedUrl = secret_state[secretKey] ? await findSecret(secretKey) : ''; const savedUrl = secret_state[secretKey] ? await findSecret(secretKey) : '';