import { callPopup, eventSource, event_types, getRequestHeaders, reloadCurrentChat, saveSettingsDebounced, substituteParams, updateMessageBlock, } from "../../../script.js"; import { extension_settings, getContext } from "../../extensions.js"; import { secret_state, writeSecret } from "../../secrets.js"; const autoModeOptions = { NONE: 'none', RESPONSES: 'responses', INPUT: 'inputs', BOTH: 'both', }; const incomingTypes = [autoModeOptions.RESPONSES, autoModeOptions.BOTH]; const outgoingTypes = [autoModeOptions.INPUT, autoModeOptions.BOTH]; const defaultSettings = { target_language: 'en', internal_language: 'en', provider: 'google', auto_mode: autoModeOptions.NONE, }; const languageCodes = { 'Afrikaans': 'af', 'Albanian': 'sq', 'Amharic': 'am', 'Arabic': 'ar', 'Armenian': 'hy', 'Azerbaijani': 'az', 'Basque': 'eu', 'Belarusian': 'be', 'Bengali': 'bn', 'Bosnian': 'bs', 'Bulgarian': 'bg', 'Catalan': 'ca', 'Cebuano': 'ceb', 'Chinese (Simplified)': 'zh-CN', 'Chinese (Traditional)': 'zh-TW', 'Corsican': 'co', 'Croatian': 'hr', 'Czech': 'cs', 'Danish': 'da', 'Dutch': 'nl', 'English': 'en', 'Esperanto': 'eo', 'Estonian': 'et', 'Finnish': 'fi', 'French': 'fr', 'Frisian': 'fy', 'Galician': 'gl', 'Georgian': 'ka', 'German': 'de', 'Greek': 'el', 'Gujarati': 'gu', 'Haitian Creole': 'ht', 'Hausa': 'ha', 'Hawaiian': 'haw', 'Hebrew': 'iw', 'Hindi': 'hi', 'Hmong': 'hmn', 'Hungarian': 'hu', 'Icelandic': 'is', 'Igbo': 'ig', 'Indonesian': 'id', 'Irish': 'ga', 'Italian': 'it', 'Japanese': 'ja', 'Javanese': 'jw', 'Kannada': 'kn', 'Kazakh': 'kk', 'Khmer': 'km', 'Korean': 'ko', 'Kurdish': 'ku', 'Kyrgyz': 'ky', 'Lao': 'lo', 'Latin': 'la', 'Latvian': 'lv', 'Lithuanian': 'lt', 'Luxembourgish': 'lb', 'Macedonian': 'mk', 'Malagasy': 'mg', 'Malay': 'ms', 'Malayalam': 'ml', 'Maltese': 'mt', 'Maori': 'mi', 'Marathi': 'mr', 'Mongolian': 'mn', 'Myanmar (Burmese)': 'my', 'Nepali': 'ne', 'Norwegian': 'no', 'Nyanja (Chichewa)': 'ny', 'Pashto': 'ps', 'Persian': 'fa', 'Polish': 'pl', 'Portuguese (Portugal, Brazil)': 'pt', 'Punjabi': 'pa', 'Romanian': 'ro', 'Russian': 'ru', 'Samoan': 'sm', 'Scots Gaelic': 'gd', 'Serbian': 'sr', 'Sesotho': 'st', 'Shona': 'sn', 'Sindhi': 'sd', 'Sinhala (Sinhalese)': 'si', 'Slovak': 'sk', 'Slovenian': 'sl', 'Somali': 'so', 'Spanish': 'es', 'Sundanese': 'su', 'Swahili': 'sw', 'Swedish': 'sv', 'Tagalog (Filipino)': 'tl', 'Tajik': 'tg', 'Tamil': 'ta', 'Telugu': 'te', 'Thai': 'th', 'Turkish': 'tr', 'Ukrainian': 'uk', 'Urdu': 'ur', 'Uzbek': 'uz', 'Vietnamese': 'vi', 'Welsh': 'cy', 'Xhosa': 'xh', 'Yiddish': 'yi', 'Yoruba': 'yo', 'Zulu': 'zu', }; const KEY_REQUIRED = ['deepl','libre']; const LOCAL_URL = ['libre']; function showKeysButton() { const providerRequiresKey = KEY_REQUIRED.includes(extension_settings.translate.provider); const providerOptionalUrl = LOCAL_URL.includes(extension_settings.translate.provider); $("#translate_key_button").toggle(providerRequiresKey); $("#translate_key_button").toggleClass('success', Boolean(secret_state[extension_settings.translate.provider])); $("#translate_url_button").toggle(providerOptionalUrl); } function loadSettings() { for (const key in defaultSettings) { if (!extension_settings.translate.hasOwnProperty(key)) { extension_settings.translate[key] = defaultSettings[key]; } } $(`#translation_provider option[value="${extension_settings.translate.provider}"]`).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(); } async function translateImpersonate(text) { const translatedText = await translate(text, extension_settings.translate.target_language); $("#send_textarea").val(translatedText); } async function translateIncomingMessage(messageId) { const context = getContext(); const message = context.chat[messageId]; if (typeof message.extra !== 'object') { message.extra = {}; } // New swipe is being generated. Don't translate that if ($(`#chat .mes[mesid="${messageId}"] .mes_text`).text() == '...') { return; } const textToTranslate = substituteParams(message.mes, context.name1, message.name); const translation = await translate(textToTranslate, extension_settings.translate.target_language); message.extra.display_text = translation; updateMessageBlock(messageId, message); } async function translateProviderLibre(text, lang) { const response = await fetch('/libre_translate', { method: 'POST', headers: getRequestHeaders(), body: JSON.stringify({ text: text, lang: lang }), }); if (response.ok) { const result = await response.text(); return result; } throw new Error(response.statusText); } async function translateProviderGoogle(text, lang) { const response = await fetch('/google_translate', { method: 'POST', headers: getRequestHeaders(), body: JSON.stringify({ text: text, lang: lang }), }); if (response.ok) { const result = await response.text(); return result; } throw new Error(response.statusText); } async function translateProviderDeepl(text, lang) { if (!secret_state.deepl) { throw new Error('No DeepL API key'); } const response = await fetch('/deepl_translate', { method: 'POST', headers: getRequestHeaders(), body: JSON.stringify({ text: text, lang: lang }), }); if (response.ok) { const result = await response.text(); return result; } throw new Error(response.statusText); } async function translate(text, lang) { try { if (text == '') { return ''; } switch (extension_settings.translate.provider) { case 'libre': return await translateProviderLibre(text, lang); case 'google': return await translateProviderGoogle(text, lang); case 'deepl': return await translateProviderDeepl(text, lang); default: console.error('Unknown translation provider', extension_settings.translate.provider); return text; } } catch (error) { console.log(error); toastr.error(String(error), 'Failed to translate message'); } } async function translateOutgoingMessage(messageId) { const context = getContext(); const message = context.chat[messageId]; if (typeof message.extra !== 'object') { message.extra = {}; } const originalText = message.mes; message.extra.display_text = originalText; message.mes = await translate(originalText, extension_settings.translate.internal_language); updateMessageBlock(messageId, message); console.log('translateOutgoingMessage', messageId); } function shouldTranslate(types) { return types.includes(extension_settings.translate.auto_mode); } function createEventHandler(translateFunction, shouldTranslateFunction) { return async (data) => { if (shouldTranslateFunction()) { await translateFunction(data); } }; } // Prevents the chat from being translated in parallel let translateChatExecuting = false; async function onTranslateChatClick() { if (translateChatExecuting) { return; } try { translateChatExecuting = true; const context = getContext(); const chat = context.chat; toastr.info(`${chat.length} message(s) queued for translation.`, 'Please wait...'); for (let i = 0; i < chat.length; i++) { await translateIncomingMessage(i); } await context.saveChat(); } catch (error) { console.log(error); toastr.error('Failed to translate chat'); } finally { translateChatExecuting = false; } } async function onTranslationsClearClick() { const confirm = await callPopup('