//META{"name":"GoogleTranslateOption","authorId":"278543574059057154","invite":"Jx3TjNS","donate":"https://www.paypal.me/MircoWittrien","patreon":"https://www.patreon.com/MircoWittrien","website":"https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/GoogleTranslateOption","source":"https://raw.githubusercontent.com/mwittrien/BetterDiscordAddons/master/Plugins/GoogleTranslateOption/GoogleTranslateOption.plugin.js"}*// var GoogleTranslateOption = (_ => { const translateIconGeneral = ``; const translateIconMask = ``; const translateIcon = translateIconGeneral.replace(``, ``).replace(``, ``).replace(` mask="url(#translateIconMask)"`, ``); const translateIconUntranslate = translateIconGeneral.replace(``, ``).replace(``, translateIconMask); const brailleConverter = { "0":"⠴", "1":"⠂", "2":"⠆", "3":"⠒", "4":"⠲", "5":"⠢", "6":"⠖", "7":"⠶", "8":"⠦", "9":"⠔", "!":"⠮", "\"":"⠐", "#":"⠼", "$":"⠫", "%":"⠩", "&":"⠯", "'":"⠄", "(":"⠷", ")":"⠾", "*":"⠡", "+":"⠬", ",":"⠠", "-":"⠤", ".":"⠨", "/":"⠌", ":":"⠱", ";":"⠰", "<":"⠣", "=":"⠿", ">":"⠜", "?":"⠹", "@":"⠈", "a":"⠁", "b":"⠃", "c":"⠉", "d":"⠙", "e":"⠑", "f":"⠋", "g":"⠛", "h":"⠓", "i":"⠊", "j":"⠚", "k":"⠅", "l":"⠇", "m":"⠍", "n":"⠝", "o":"⠕", "p":"⠏", "q":"⠟", "r":"⠗", "s":"⠎", "t":"⠞", "u":"⠥", "v":"⠧", "w":"⠺", "x":"⠭", "y":"⠽", "z":"⠵", "[":"⠪", "\\":"⠳", "]":"⠻", "^":"⠘", "⠁":"a", "⠂":"1", "⠃":"b", "⠄":"'", "⠅":"k", "⠆":"2", "⠇":"l", "⠈":"@", "⠉":"c", "⠊":"i", "⠋":"f", "⠌":"/", "⠍":"m", "⠎":"s", "⠏":"p", "⠐":"\"", "⠑":"e", "⠒":"3", "⠓":"h", "⠔":"9", "⠕":"o", "⠖":"6", "⠗":"r", "⠘":"^", "⠙":"d", "⠚":"j", "⠛":"g", "⠜":">", "⠝":"n", "⠞":"t", "⠟":"q", "⠠":", ", "⠡":"*", "⠢":"5", "⠣":"<", "⠤":"-", "⠥":"u", "⠦":"8", "⠧":"v", "⠨":".", "⠩":"%", "⠪":"[", "⠫":"$", "⠬":"+", "⠭":"x", "⠮":"!", "⠯":"&", "⠰":";", "⠱":":", "⠲":"4", "⠳":"\\", "⠴":"0", "⠵":"z", "⠶":"7", "⠷":"(", "⠸":"_", "⠹":"?", "⠺":"w", "⠻":"]", "⠼":"#", "⠽":"y", "⠾":")", "⠿":"=", "_":"⠸" }; const morseConverter = { "0":"−−−−−", "1":"·−−−−", "2":"··−−−", "3":"···−−", "4":"····−", "5":"·····", "6":"−····", "7":"−−···", "8":"−−−··", "9":"−−−−·", "!":"−·−·−−", "\"":"·−··−·", "$":"···−··−", "&":"·−···", "'":"·−−−−·", "(":"−·−−·", ")":"−·−−·−", "+":"·−·−·", ",":"−−··−−", "-":"−····−", ".":"·−·−·−", "/":"−··−·", ":":"−−−···", ";":"−·−·−·", "=":"−···−", "?":"··−−··", "@":"·−−·−·", "a":"·−", "b":"−···", "c":"−·−·", "d":"−··", "e":"·", "f":"··−·", "g":"−−·", "h":"····", "i":"··", "j":"·−−−", "k":"−·−", "l":"·−··", "m":"−−", "n":"−·", "o":"−−−", "p":"·−−·", "q":"−−·−", "r":"·−·", "s":"···", "t":"−", "u":"··−", "v":"···−", "w":"·−−", "x":"−··−", "y":"−·−−", "z":"−−··", "·":"e", "··":"i", "···":"s", "····":"h", "·····":"5", "····−":"4", "···−":"v", "···−··−":"$", "···−−":"3", "··−":"u", "··−·":"f", "··−−··":"?", "··−−·−":"_", "··−−−":"2", "·−":"a", "·−·":"r", "·−··":"l", "·−···":"&", "·−··−·":"\"", "·−·−·":"+", "·−·−·−":".", "·−−":"w", "·−−·":"p", "·−−·−·":"@", "·−−−":"j", "·−−−−":"1", "·−−−−·":"'", "−":"t", "−·":"n", "−··":"d", "−···":"b", "−····":"6", "−····−":"-", "−···−":"=", "−··−":"x", "−··−·":"/", "−·−":"k", "−·−·":"c", "−·−·−·":";", "−·−·−−":"!", "−·−−":"y", "−·−−·":"(", "−·−−·−":")", "−−":"m", "−−·":"g", "−−··":"z", "−−···":"7", "−−··−−":",", "−−·−":"q", "−−−":"o", "−−−··":"8", "−−−···":":", "−−−−·":"9", "−−−−−":"0", "_":"··−−·−" }; const googleLanguages = ["af","am","ar","az","be","bg","bn","bs","ca","ceb","co","cs","cy","da","de","el","en","eo","es","et","eu","fa","fi","fr","fy","ga","gd","gl","gu","ha","haw","hi","hmn","hr","ht","hu","hy","id","ig","is","it","iw","ja","jw","ka","kk","km","kn","ko","ku","ky","la","lb","lo","lt","lv","mg","mi","mk","ml","mn","mr","ms","mt","my","ne","nl","no","ny","or","pa","pl","ps","pt","ro","ru","rw","sd","si","sk","sl","sm","sn","so","sq","sr","st","su","sv","sw","ta","te","tg","th","tk","tl","tr","tt","ug","uk","ur","uz","vi","xh","yi","yo","zh-CN","zu"]; const translationEngines = { googleapi: {name:"GoogleApi", funcName:"googleApiTranslate", languages: googleLanguages}, google: {name:"Google", funcName:"googleTranslate", languages: googleLanguages}, itranslate: {name:"iTranslate", funcName:"iTranslateTranslate", languages: [...new Set(["af","ar","az","be","bg","bn","bs","ca","ceb","cs","cy","da","de","el","en","eo","es","et","eu","fa","fi","fil","fr","ga","gl","gu","ha","he","hi","hmn","hr","ht","hu","hy","id","ig","is","it","ja","jw","ka","kk","km","kn","ko","la","lo","lt","lv","mg","mi","mk","ml","mn","mr","ms","mt","my","ne","nl","no","ny","pa","pl","pt-BR","pt-PT","ro","ru","si","sk","sl","so","sq","sr","st","su","sv","sw","ta","te","tg","th","tr","uk","ur","uz","vi","we","yi","yo","zh-CN","zh-TW","zu"].concat(googleLanguages))].sort()}, yandex: {name:"Yandex", funcName:"yandexTranslate", languages: ["af","am","ar","az","ba","be","bg","bn","bs","ca","ceb","cs","cy","da","de","el","en","eo","es","et","eu","fa","fi","fr","ga","gd","gl","gu","he","hi","hr","ht","hu","hy","id","is","it","ja","jv","ka","kk","km","kn","ko","ky","la","lb","lo","lt","lv","mg","mhr","mi","mk","ml","mn","mr","ms","mt","my","ne","nl","no","pa","pap","pl","pt","ro","ru","si","sk","sl","sq","sr","su","sv","sw","ta","te","tg","th","tl","tr","tt","udm","uk","ur","uz","vi","xh","yi","zh"]} }; var languages, translating, isTranslating, translatedMessages; return class GoogleTranslateOption { getName () {return "GoogleTranslateOption";} getVersion () {return "1.9.5";} getAuthor () {return "DevilBro";} getDescription () {return "Adds a Google Translate option to your context menu, which shows a preview of the translated text and on click will open the selected text in Google Translate. Also adds a translation button to your textareas, which will automatically translate the text for you before it is being send.";} constructor () { this.changelog = { "added":[["GoogleApi","Added a new way faster Google Api that uses a translation API provided by Google, which does not rely on using an invisible brower window to translate the text, this API is limited to 100 requests per hour, so you might get rate limited quickly"]], "improved":[["New Library Structure & React","Restructured my Library and switched to React rendering instead of DOM manipulation"]] }; this.patchedModules = { before: { ChannelTextAreaForm: "render", ChannelEditorContainer: "render", Embed: "render" }, after: { ChannelTextAreaContainer: "render", Messages: "render", MessageContent: "type", Embed: "render" } }; } initConstructor () { this.defaults = { settings: { useChromium: {value:false, description:"Use an inbuilt browser window instead of opening your default browser"}, addTranslateButton: {value:true, description:"Adds an translate button to the chatbar"}, sendOriginalMessage: {value:false, description:"Send the original message together with the translation"} }, choices: { inputContext: {value:"auto", direction:"input", place:"Context", description:"Input Language in received Messages:"}, outputContext: {value:"$discord", direction:"output", place:"Context", description:"Output Language in received Messages:"}, inputMessage: {value:"auto", direction:"input", place:"Message", description:"Input Language in sent Messages:"}, outputMessage: {value:"$discord", direction:"output", place:"Message", description:"Output Language in sent Messages:"} }, engines: { translator: {value:"googleapi", description:"Translation Engine:"} } }; this.css = ` ${BDFDB.dotCN._googletranslateoptiontranslatebutton + BDFDB.dotCNS._googletranslateoptiontranslating + BDFDB.dotCN.textareaicon} { color: #F04747 !important; } ${BDFDB.dotCN._googletranslateoptionreversebutton} { opacity: 0.5; margin-right: 5px; transition: all 200ms ease; } ${BDFDB.dotCN._googletranslateoptionreversebutton}:hover { opacity: 1; }`; } getSettingsPanel () { if (!window.BDFDB || typeof BDFDB != "object" || !BDFDB.loaded || !this.started) return; let settings = BDFDB.DataUtils.get(this, "settings"); let settingspanel, settingsitems = []; settingsitems = settingsitems.concat(this.createSelects(false)); for (let key in settings) settingsitems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, { className: BDFDB.disCN.marginbottom8, type: "Switch", plugin: this, keys: ["settings", key], label: this.defaults.settings[key].description, value: settings[key] })); return settingspanel = BDFDB.PluginUtils.createSettingsPanel(this, settingsitems); } //legacy load () {} start () { if (!window.BDFDB) window.BDFDB = {myPlugins:{}}; if (window.BDFDB && window.BDFDB.myPlugins && typeof window.BDFDB.myPlugins == "object") window.BDFDB.myPlugins[this.getName()] = this; let libraryScript = document.querySelector("head script#BDFDBLibraryScript"); if (!libraryScript || (performance.now() - libraryScript.getAttribute("date")) > 600000) { if (libraryScript) libraryScript.remove(); libraryScript = document.createElement("script"); libraryScript.setAttribute("id", "BDFDBLibraryScript"); libraryScript.setAttribute("type", "text/javascript"); libraryScript.setAttribute("src", "https://mwittrien.github.io/BetterDiscordAddons/Plugins/BDFDB.min.js"); libraryScript.setAttribute("date", performance.now()); libraryScript.addEventListener("load", _ => {this.initialize();}); document.head.appendChild(libraryScript); } else if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) this.initialize(); this.startTimeout = setTimeout(_ => { try {return this.initialize();} catch (err) {console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not initiate plugin! " + err);} }, 30000); } initialize () { if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) { if (this.started) return; BDFDB.PluginUtils.init(this); languages = {}; translating = false; isTranslating = false; translatedMessages = {}; this.setLanguages(); BDFDB.ModuleUtils.forceAllUpdates(this); } else console.error(`%c[${this.getName()}]%c`, "color: #3a71c1; font-weight: 700;", "", "Fatal Error: Could not load BD functions!"); } stop () { if (window.BDFDB && typeof BDFDB === "object" && BDFDB.loaded) { this.stopping = true; BDFDB.ModuleUtils.forceAllUpdates(this); BDFDB.PluginUtils.clear(this); } } // begin of own functions onSettingsClosed (instance, wrapper, returnvalue) { if (this.SettingsUpdated) { delete this.SettingsUpdated; this.setLanguages(); BDFDB.ModuleUtils.forceAllUpdates(this, ["ChannelTextAreaForm", "ChannelTextAreaContainer"]); } } onMessageContextMenu (e) { if (e.instance.props.message && e.instance.props.channel) { let translated = translatedMessages[e.instance.props.message.id]; let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name:"MessagePinItem"}); if (index > -1) children.splice(index > -1 ? index : children.length, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ContextMenuItems.Item, { label: translated ? this.labels.context_messageuntranslateoption_text : this.labels.context_messagetranslateoption_text, hint: BDFDB.BDUtils.isPluginEnabled("MessageUtilities") ? BDFDB.BDUtils.getPlugin("MessageUtilities").getActiveShortcutString("__Translate_Message") : null, disabled: !translated && isTranslating, action: _ => { BDFDB.ContextMenuUtils.close(e.instance); this.translateMessage(e.instance.props.message, e.instance.props.channel); } })); let text = document.getSelection().toString(); if (text) { let translating, foundtranslation, foundinput, foundoutput; let [children2, index2] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name:"SearchWithGoogle"}); if (index2 > -1) children2.splice(index2 > -1 ? index2 : children2.length, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ContextMenuItems.Item, { label: this.labels.context_googletranslateoption_text, disabled: isTranslating, action: event => { let item = BDFDB.DOMUtils.getParent(BDFDB.dotCN.contextmenuitem, event.target); if (item) { let createTooltip = _ => { BDFDB.TooltipUtils.create(item, `From ${foundinput.name}:\n${text}\n\nTo ${foundoutput.name}:\n${foundtranslation}`, {type:"right", selector:"googletranslate-tooltip"}); }; if (foundtranslation && foundinput && foundoutput) { if (document.querySelector(".googletranslate-tooltip")) { BDFDB.ContextMenuUtils.close(e.instance); BDFDB.DiscordUtils.openLink(this.getGoogleTranslatePageURL(foundinput.id, foundoutput.id, text), BDFDB.DataUtils.get(this, "settings", "useChromium")); } else createTooltip(); } else if (!translating) { translating = true; this.translateText(text, "context", (translation, input, output) => { if (translation) { foundtranslation = translation, foundinput = input, foundoutput = output; createTooltip(); } }); } } } })); } } } onMessageOptionToolbar (e) { if (!e.instance.props.hasMorePopout && e.instance.props.message && e.instance.props.channel) { let translated = !!translatedMessages[e.instance.props.message.id]; let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {key: ["pin", "unpin"]}); children.splice(index + 1, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { key: "translate", text: translated ? this.labels.context_messageuntranslateoption_text : this.labels.context_messagetranslateoption_text, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, { className: BDFDB.disCNS.messagetoolbarbutton, disabled: isTranslating, onClick: event => { this.translateMessage(e.instance.props.message, e.instance.props.channel); let buttonIns = BDFDB.ReactUtils.getInstance(BDFDB.DOMUtils.getParent(BDFDB.dotCN.messagetoolbarbutton, event.target)); if (buttonIns) { let isTranslated = !!translatedMessages[e.instance.props.message.id]; let svgInstance = BDFDB.ReactUtils.findOwner(buttonIns, {props: "iconSVG"}); let tooltipInstance = BDFDB.ReactUtils.findOwner(buttonIns, {key: "translate", up:true}); if (svgInstance) { svgInstance.props.iconSVG = isTranslated ? translateIconUntranslate : translateIcon; BDFDB.ReactUtils.forceUpdate(svgInstance); } if (tooltipInstance) { tooltipInstance.props.text = isTranslated ? this.labels.context_messageuntranslateoption_text : this.labels.context_messagetranslateoption_text; BDFDB.ReactUtils.forceUpdate(tooltipInstance); } } }, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { className: BDFDB.disCNS.messagetoolbaricon, nativeClass: true, iconSVG: translated ? translateIconUntranslate : translateIcon }) }) })); } } onMessageOptionContextMenu (e) { if (e.instance.props.message && e.instance.props.channel) { let translated = !!translatedMessages[e.instance.props.message.id]; let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {key: ["pin", "unpin"]}); children.splice(index + 1, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ContextMenuItems.Item, { label: translated ? this.labels.context_messageuntranslateoption_text : this.labels.context_messagetranslateoption_text, disabled: isTranslating, hint: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { className: BDFDB.disCNS.messagetoolbaricon, nativeClass: true, iconSVG: translated ? translateIconUntranslate : translateIcon }), action: _ => { e.instance.props.onClose(); this.translateMessage(e.instance.props.message, e.instance.props.channel); } })); } } processChannelTextAreaForm (e) { if (!BDFDB.ModuleUtils.isPatched(this, e.instance, "handleSendMessage")) BDFDB.ModuleUtils.patch(this, e.instance, "handleSendMessage", {instead: e2 => { if (translating) { e2.stopOriginalMethodCall(); this.translateText(e2.methodArguments[0], "message", (translation, input, output) => { translation = !translation ? e2.methodArguments[0] : (BDFDB.DataUtils.get(this, "settings", "sendOriginalMessage") ? (e2.methodArguments[0] + "\n\n" + translation) : translation); e2.originalMethod(translation); }); return Promise.resolve({ shouldClear: true, shouldRefocus: true }); } else return e2.callOriginalMethodAfterwards(); }}, true); } processChannelEditorContainer (e) { if (translating && isTranslating) e.instance.props.disabled = true; } processChannelTextAreaContainer (e) { if (BDFDB.DataUtils.get(this, "settings", "addTranslateButton")) { let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {name: "ChannelEditorContainer"}); if (index > -1 && children[index].props.type == BDFDB.DiscordConstants.TextareaTypes.NORMAL && !children[index].props.disabled) { let [children2, index2] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props:[["className", BDFDB.disCN.textareapickerbuttons]]}); if (index2 > -1 && children2[index2].props && children2[index2].props.children) children2[index2].props.children.unshift(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.PopoutContainer, { children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ChannelTextAreaButton, { key: "translate-button", className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN._googletranslateoptiontranslatebutton, translating && BDFDB.disCN._googletranslateoptiontranslating, BDFDB.disCN.textareapickerbutton), iconClassName: BDFDB.disCN.textareaicon, iconSVG: translateIconGeneral }), width: 400, padding: 10, animation: BDFDB.LibraryComponents.PopoutContainer.Animation.SCALE, position: BDFDB.LibraryComponents.PopoutContainer.Positions.TOP, align: BDFDB.LibraryComponents.PopoutContainer.Align.RIGHT, onClose: instance => { let channelTextareaButtonIns = BDFDB.ReactUtils.findOwner(instance, {key: "translate-button"}); if (channelTextareaButtonIns) { channelTextareaButtonIns.props.isActive = false; BDFDB.ReactUtils.forceUpdate(channelTextareaButtonIns); } }, onContextMenu: (e, instance) => { translating = !translating; let channelTextareaButtonIns = BDFDB.ReactUtils.findOwner(instance, {key: "translate-button"}); if (channelTextareaButtonIns) { channelTextareaButtonIns.props.className = BDFDB.DOMUtils.formatClassName(BDFDB.disCN._googletranslateoptiontranslatebutton, translating && BDFDB.disCN._googletranslateoptiontranslating, BDFDB.disCN.textareapickerbutton); BDFDB.ReactUtils.forceUpdate(channelTextareaButtonIns); instance.close(); } }, renderPopout: instance => { let channelTextareaButtonIns = BDFDB.ReactUtils.findOwner(instance, {key: "translate-button"}); if (channelTextareaButtonIns) { channelTextareaButtonIns.props.isActive = true; BDFDB.ReactUtils.forceUpdate(channelTextareaButtonIns); } let popoutelements = []; popoutelements.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { className: BDFDB.disCN.marginbottom8, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsLabel, { label: `Words starting with "!" will be ignored` }) })); popoutelements.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, { className: BDFDB.disCN.marginbottom8 })), popoutelements = popoutelements.concat(this.createSelects(true)); popoutelements.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, { type: "Switch", className: BDFDB.disCN.marginbottom8, label: "Translate your Messages before sending", tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5, value: translating, onChange: value => { translating = value; if (channelTextareaButtonIns) { channelTextareaButtonIns.props.className = BDFDB.DOMUtils.formatClassName(BDFDB.disCN._googletranslateoptiontranslatebutton, translating && BDFDB.disCN._googletranslateoptiontranslating, BDFDB.disCN.textareapickerbutton); BDFDB.ReactUtils.forceUpdate(channelTextareaButtonIns); } } })); return popoutelements; } })); } } } processMessages (e) { let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props: ["message", "channel"]}); if (index > -1) for (let ele of children) if (ele && ele.props && ele.props.message) { let translation = translatedMessages[ele.props.message.id]; if (translation) ele.props.message = new BDFDB.DiscordObjects.Message(Object.assign({}, ele.props.message, {content: translation.content})); } } processMessageContent (e) { if (e.instance.props.message) { let translation = translatedMessages[e.instance.props.message.id]; if (translation) e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { text: `From: ${this.getLanguageName(translation.input)}\nTo: ${this.getLanguageName(translation.output)}`, tooltipConfig: {style: "max-width: 400px"}, children: BDFDB.ReactUtils.createElement("time", { className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.messageedited, BDFDB.disCN._googletranslateoptiontranslated), children: `(${this.labels.translated_watermark_text})` }) })); } } processEmbed (e) { if (e.instance.props.embed && e.instance.props.embed.messageId) { let translation = translatedMessages[e.instance.props.embed.messageId]; if (translation) { if (!e.returnvalue) e.instance.props.embed = Object.assign({}, e.instance.props.embed, { rawDescription: translation.embeds[e.instance.props.embed.id], originalDescription: e.instance.props.embed.originalDescription || e.instance.props.embed.rawDescription }); else { let [children, index] = BDFDB.ReactUtils.findChildren(e.returnvalue, {props: [["className", BDFDB.disCN.embeddescription]]}); if (index > -1) children[index].props.children.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { text: `From: ${this.getLanguageName(translation.input)}\nTo: ${this.getLanguageName(translation.output)}`, tooltipConfig: {style: "max-width: 400px"}, children: BDFDB.ReactUtils.createElement("time", { className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.messageedited, BDFDB.disCN._googletranslateoptiontranslated), children: `(${this.labels.translated_watermark_text})` }) })); } } else if (!e.returnvalue && e.instance.props.embed.originalDescription) { e.instance.props.embed = Object.assign({}, e.instance.props.embed, {rawDescription: e.instance.props.embed.originalDescription}); delete e.instance.props.embed.originalDescription; } } } createSelects (inPopout) { let selects = []; for (let key in this.defaults.choices) { let isOutput = this.defaults.choices[key].direction == "output"; selects.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, { title: this.defaults.choices[key].description, titlechildren: isOutput ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Button, { look: BDFDB.LibraryComponents.Button.Looks.BLANK, size: BDFDB.LibraryComponents.Button.Sizes.NONE, onClick: e => { let place = this.defaults.choices[key].place; let input = this.getLanguageChoice("input", place); let output = this.getLanguageChoice("output", place); input = input == "auto" ? "en" : input; BDFDB.DataUtils.save(output, this, "choices", "input" + place); BDFDB.DataUtils.save(input, this, "choices", "output" + place); for (let selectIns of BDFDB.ReactUtils.findOwner(BDFDB.ReactUtils.findOwner(e._targetInst, {name:["BDFDB_Popout", "BDFDB_SettingsPanel"], up:true}), {name:"BDFDB_Select", all:true, noCopies:true})) if (selectIns && selectIns.props && selectIns.props.id && this.defaults.choices[selectIns.props.id] && this.defaults.choices[selectIns.props.id].place == place) { selectIns.props.value = this.defaults.choices[selectIns.props.id].direction == "input" ? output : input; BDFDB.ReactUtils.forceUpdate(selectIns); } this.setLanguages(); }, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { className: BDFDB.disCN._googletranslateoptionreversebutton, iconSVG: `` }) }) : null, className: BDFDB.disCN.marginbottom8, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Select, { menuPlacement: inPopout ? BDFDB.LibraryComponents.Select.MenuPlacements.TOP : BDFDB.LibraryComponents.Select.MenuPlacements.BOTTOM, value: this.getLanguageChoice(key), id: key, options: BDFDB.ObjectUtils.toArray(BDFDB.ObjectUtils.map(isOutput ? BDFDB.ObjectUtils.filter(languages, lang => lang.id != "auto") : languages, (lang, id) => {return {value:id, label:this.getLanguageName(lang)}})), searchable: true, optionRenderer: lang => { return languages[lang.value] ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { align: BDFDB.LibraryComponents.Flex.Align.CENTER, children: [ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, { grow: 1, children: lang.label }), inPopout ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FavButton, { isFavorite: languages[lang.value].fav == 0, onClick: value => { if (value) BDFDB.DataUtils.save(true, this, "favorites", lang.value); else BDFDB.DataUtils.remove(this, "favorites", lang.value); this.setLanguages(); } }) : null ] }) : null; }, onChange: lang => { BDFDB.DataUtils.save(lang.value, this, "choices", key); } }) })); if (isOutput) selects.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, { className: BDFDB.disCN.marginbottom8 })); } let engines = BDFDB.DataUtils.get(this, "engines"); for (let key in engines) { selects.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, { title: this.defaults.engines[key].description, className: BDFDB.disCN.marginbottom8, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Select, { menuPlacement: inPopout ? BDFDB.LibraryComponents.Select.MenuPlacements.TOP : BDFDB.LibraryComponents.Select.MenuPlacements.BOTTOM, value: engines[key], id: key, options: Object.keys(translationEngines).map(engineKey => {return {value:engineKey, label:translationEngines[engineKey].name}}), searchable: true, onChange: (engine, instance) => { BDFDB.DataUtils.save(engine.value, this, "engines", key); this.setLanguages(); let popoutInstance = BDFDB.ReactUtils.findOwner(instance, {name: "BDFDB_PopoutContainer", up:true}); if (popoutInstance) { popoutInstance.close(); popoutInstance.handleClick(); } } }) })); } return selects; } setLanguages () { let languageIds = (translationEngines[BDFDB.DataUtils.get(this, "engines", "translator")] || {}).languages || []; languages = Object.assign( {"auto": {name:"Auto", id:"auto", integrated:false, dic:false}}, BDFDB.ObjectUtils.filter(BDFDB.LanguageUtils.languages, lang => languageIds.includes(lang.id)), {"binary": {name:"Binary", id:"binary", integrated:false, dic:false}}, {"braille": {name:"Braille 6-dot", id:"braille", integrated:false, dic:false}}, {"morse": {name:"Morse", id:"morse", integrated:false, dic:false}} ); let favorites = BDFDB.DataUtils.load(this, "favorites"); for (let id in languages) languages[id].fav = favorites[id] != undefined ? 0 : 1; languages = BDFDB.ObjectUtils.sort(languages, "fav"); } getLanguageChoice (direction, place) { this.setLanguages(); let type = place === undefined ? direction : direction.toLowerCase() + place.charAt(0).toUpperCase() + place.slice(1).toLowerCase(); let choice = BDFDB.DataUtils.get(this, "choices", type); choice = languages[choice] ? choice : Object.keys(languages)[0]; choice = type.indexOf("output") > -1 && choice == "auto" ? "en" : choice; return choice; } translateMessage (message, channel) { if (!message) return; if (translatedMessages[message.id]) { delete translatedMessages[message.id]; BDFDB.ModuleUtils.forceAllUpdates(this, ["Messages", "Embed"]); } else { let content = message.content || ""; for (let embed of message.embeds) content += ("\n__________________ __________________ __________________\n" + embed.rawDescription); this.translateText(content, "context", (translation, input, output) => { if (translation) { let strings = translation.split("\n__________________ __________________ __________________\n"); let content = strings.shift().trim(), embeds = {}; for (let i in message.embeds) { message.embeds[i].messageId = message.id; embeds[message.embeds[i].id] = (strings.shift() || message.embeds[i].rawDescription).trim(); } translatedMessages[message.id] = {content, embeds, message, input, output}; BDFDB.ModuleUtils.forceAllUpdates(this, ["Messages", "Embed"]); } }); } } translateText (text, type, callback) { let toast = null, finishTranslation = translation => { isTranslating = false; if (translation) translation = this.addExceptions(translation, exceptions); if (toast) { BDFDB.TimeUtils.clear(toast.interval); toast.close(); } callback(translation == text ? "" : translation, input, output); }; let [newtext, exceptions, translate] = this.removeExceptions(text.trim(), type); let input = Object.assign({}, languages[this.getLanguageChoice("input", type)]); let output = Object.assign({}, languages[this.getLanguageChoice("output", type)]); if (translate) { let timer = 0; toast = BDFDB.NotificationUtils.toast("Translating. Please wait", {timeout:0}); toast.interval = BDFDB.TimeUtils.interval(_ => { if (timer++ > 40) { finishTranslation(""); BDFDB.NotificationUtils.toast("Failed to translate text. Try another Translate Engine.", {type:"error"}); } else toast.textContent = toast.textContent.indexOf(".....") > -1 ? "Translating. Please wait" : toast.textContent + "."; }, 500); let specialcase = this.checkForSpecialCase(newtext, input); if (specialcase) { input.name = specialcase.name; switch (specialcase.id) { case "binary": newtext = this.binary2string(newtext); break; case "braille": newtext = this.braille2string(newtext); break; case "morse": newtext = this.morse2string(newtext); break; } } if (output.id == "binary" || output.id == "braille" || output.id == "morse") { switch (output.id) { case "binary": newtext = this.string2binary(newtext); break; case "braille": newtext = this.string2braille(newtext); break; case "morse": newtext = this.string2morse(newtext); break; } finishTranslation(newtext); } else { let translator = BDFDB.DataUtils.get(this, "engines", "translator"); if (translationEngines[translator] && typeof this[translationEngines[translator].funcName] == "function") { isTranslating = true; this[translationEngines[translator].funcName].apply(this, [{input, output, text:newtext, specialcase, engine:translationEngines[translator]}, finishTranslation]); } else finishTranslation(""); } } else finishTranslation(text); } googleTranslate (data, callback) { let googleTranslateWindow = BDFDB.WindowUtils.open(this, this.getGoogleTranslatePageURL(data.input.id, data.output.id, data.text)); googleTranslateWindow.webContents.on("did-finish-load", _ => { googleTranslateWindow.webContents.executeJavaScript(`require("electron").ipcRenderer.sendTo(${BDFDB.LibraryRequires.electron.remote.getCurrentWindow().webContents.id}, "GTO-translation", [(document.querySelector(".translation") || {}).innerText, [(new RegExp("{code:'([^']*)',name:'" + [(new RegExp((window.source_language_detected || "").replace("%1$s", "([A-z]{2,})"), "g")).exec(document.body.innerHTML)].flat()[1] +"'}", "g")).exec(document.body.innerHTML)].flat()[1]]);`); }); BDFDB.WindowUtils.addListener(this, "GTO-translation", (event, messagedata) => { BDFDB.WindowUtils.close(googleTranslateWindow); BDFDB.WindowUtils.removeListener(this, "GTO-translation"); if (!data.specialcase && messagedata[1] && languages[messagedata[1]]) { data.input.name = languages[messagedata[1]].name; data.input.ownlang = languages[messagedata[1]].ownlang; } callback(messagedata[0]); }); } googleApiTranslate (data, callback) { BDFDB.LibraryRequires.request(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=${data.input.id}&tl=${data.output.id}&dt=t&dj=1&source=input&q=${encodeURIComponent(data.text)}`, (error, response, result) => { if (!error && result && response.statusCode == 200) { try { result = JSON.parse(result); if (!data.specialcase && result.src && result.src && languages[result.src]) { data.input.name = languages[result.src].name; data.input.ownlang = languages[result.src].ownlang; } callback(result.sentences[0].trans); } catch (err) {callback("");} } else { BDFDB.NotificationUtils.toast("Failed to translate text. Translation Server is down or Request Limit per Hour is reached. Try another Translate Engine.", {type:"error"}); callback(""); } }); } iTranslateTranslate (data, callback) { let translate = _ => { BDFDB.LibraryRequires.request({ method: "POST", url: "https://web-api.itranslateapp.com/v3/texts/translate", headers: { "API-KEY": data.engine.APIkey }, body: JSON.stringify({ source: { dialect: data.input.id, text: data.text }, target: { dialect: data.output.id } }) }, (error, response, result) => { if (!error && response && response.statusCode == 200) { try { result = JSON.parse(result); if (!data.specialcase && result.source && result.source.dialect && languages[result.source.dialect]) { data.input.name = languages[result.source.dialect].name; data.input.ownlang = languages[result.source.dialect].ownlang; } callback(result.target.text); } catch (err) {callback("");} } else { BDFDB.NotificationUtils.toast("Failed to translate text. Translation Server is down or API-key outdated. Try another Translate Engine.", {type:"error"}); callback(""); } }); }; if (data.engine.APIkey) translate(); else BDFDB.LibraryRequires.request("https://www.itranslate.com/js/webapp/main.js", {gzip: true}, (error, response, result) => { if (!error && result) { let APIkey = /var API_KEY = "(.+)"/.exec(result); if (APIkey) { data.engine.APIkey = APIkey[1]; translate(); } else callback(""); } else callback(""); }); } yandexTranslate (data, callback) { BDFDB.LibraryRequires.request(`https://translate.yandex.net/api/v1.5/tr/translate?key=trnsl.1.1.20191206T223907Z.52bd512eca953a5b.1ec123ce4dcab3ae859f312d27cdc8609ab280de&text=${encodeURIComponent(data.text)}&lang=${data.specialcase || data.input.id == "auto" ? data.output.id : (data.input.id + "-" + data.output.id)}&options=1`, (error, response, result) => { if (!error && result && response.statusCode == 200) { result = BDFDB.DOMUtils.create(result); let translation = result.querySelector("text"); let detected = result.querySelector("detected"); if (translation && detected) { let detectedlang = detected.getAttribute("lang"); if (!data.specialcase && detectedlang && languages[detectedlang]) { data.input.name = languages[detectedlang].name; data.input.ownlang = languages[detectedlang].ownlang; } callback(translation.innerText); } else callback(""); } else { BDFDB.NotificationUtils.toast("Failed to translate text. Translation Server is down or API-key outdated. Try another Translate Engine.", {type:"error"}); callback(""); } }); } checkForSpecialCase (text, input) { if (input.id == "binary" || input.id == "braille" || input.id == "morse") return input; else if (input.id == "auto") { if (/^[0-1]*$/.test(text.replace(/\s/g, ""))) { return {id: "binary", name: "Binary"}; } else if (/^[⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿]*$/.test(text.replace(/\s/g, ""))) { return {id: "braille", name: "Braille 6-dot"}; } else if (/^[/|·−._-]*$/.test(text.replace(/\s/g, ""))) { return {id: "morse", name: "Morse"}; } } return null; } string2binary (string) { var binary = ""; for (let character of string) binary += parseInt(character.charCodeAt(0).toString(2)).toPrecision(8).split(".").reverse().join("").toString() + " "; return binary; } string2braille (string) { var braille = ""; for (let character of string) braille += brailleConverter[character.toLowerCase()] ? brailleConverter[character.toLowerCase()] : character; return braille; } string2morse (string) { string = string.replace(/ /g, "%%%%%%%%%%"); var morse = ""; for (let character of string) morse += (morseConverter[character.toLowerCase()] ? morseConverter[character.toLowerCase()] : character) + " "; morse = morse.split("\n"); for (let i in morse) morse[i] = morse[i].trim(); return morse.join("\n").replace(/% % % % % % % % % % /g, "/ "); } binary2string (binary) { var string = ""; binary = binary.replace(/\n/g, "00001010").replace(/\r/g, "00001101").replace(/\t/g, "00001001").replace(/\s/g, ""); if (/^[0-1]*$/.test(binary)) { var eightdigits = ""; var counter = 0; for (var digit of binary) { eightdigits += digit; counter++; if (counter > 7) { string += String.fromCharCode(parseInt(eightdigits,2).toString(10)); eightdigits = ""; counter = 0; } } } else BDFDB.NotificationUtils.toast("Invalid binary format. Only use 0s and 1s.", {type:"error"}); return string; } braille2string (braille) { var string = ""; for (let character of braille) string += brailleConverter[character.toLowerCase()] ? brailleConverter[character.toLowerCase()] : character; return string; } morse2string (morse) { var string = ""; for (let word of morse.replace(/[_-]/g, "−").replace(/\./g, "·").replace(/\r|\t/g, "").split(/\/|\||\n/g)) { for (let characterstr of word.trim().split(" ")) string += morseConverter[characterstr] ? morseConverter[characterstr] : characterstr; string += " "; } return string.trim(); } addExceptions (string, exceptions) { for (let count in exceptions) { let exception = exceptions[count].indexOf("!") == 0 ? exceptions[count].slice(1) : exceptions[count]; let newstring = string.replace(new RegExp(`\[/////[ ]*${count}\]`), exception); if (newstring == string) string = newstring + " " + exception; else string = newstring; } return string; } removeExceptions (string, type) { var exceptions = {}, newString = [], count = 0; if (type == "context") { let text = [], i = 0; string.split("").forEach(chara => { if (chara == "<" && text[i]) i++; text[i] = text[i] ? text[i] + chara : chara; if (chara == ">") i++; }); for (let j in text) { if (text[j].indexOf("<") == 0) { newString.push(`[/////${count}]`); exceptions[count] = text[j]; count++; } else newString.push(text[j]); } } else { string.split(" ").forEach(word => { if (word.indexOf("<@!") == 0 || word.indexOf(":") == 0 || word.indexOf("@") == 0 || word.indexOf("#") == 0 || (word.indexOf("!") == 0 && word.length > 1)) { newString.push(`[/////${count}]`); exceptions[count] = word; count++; } else newString.push(word); }); } return [newString.join(" "), exceptions, newString.length-count != 0]; } getGoogleTranslatePageURL (input, output, text) { input = BDFDB.LanguageUtils.languages[input] ? input : "auto"; return "https://translate.google.com/#" + input + "/" + output + "/" + encodeURIComponent(text); } getLanguageName (language) { if (language.name.startsWith("Discord")) return language.name.slice(0, -1) + (language.ownlang && languages[language.id].name != language.ownlang ? ` / ${language.ownlang}` : "") + ")"; else return language.name + (language.ownlang && language.name != language.ownlang ? ` / ${language.ownlang}` : ""); } setLabelsByLanguage () { switch (BDFDB.LanguageUtils.getLanguage().id) { case "hr": //croatian return { context_messagetranslateoption_text: "Prijevod poruke", context_messageuntranslateoption_text: "Prijenos poruke", context_googletranslateoption_text: "Traži prijevod", popout_translateoption_text: "Prevesti", popout_untranslateoption_text: "Prevesti natrag", translated_watermark_text: "preveo" }; case "da": //danish return { context_messagetranslateoption_text: "Oversæt Besked", context_messageuntranslateoption_text: "Oversæt Besked tilbage", context_googletranslateoption_text: "Søg oversættelse", popout_translateoption_text: "Oversætte", popout_untranslateoption_text: "Oversæt tilbage", translated_watermark_text: "oversat" }; case "de": //german return { context_messagetranslateoption_text: "Nachricht übersetzen", context_messageuntranslateoption_text: "Nachricht unübersetzen", context_googletranslateoption_text: "Suche Übersetzung", popout_translateoption_text: "Übersetzen", popout_untranslateoption_text: "Unübersetzen", translated_watermark_text: "übersetzt" }; case "es": //spanish return { context_messagetranslateoption_text: "Traducir mensaje", context_messageuntranslateoption_text: "Traducir mensaje de vuelta", context_googletranslateoption_text: "Buscar traducción", popout_translateoption_text: "Traducir", popout_untranslateoption_text: "Traducir de vuelta", translated_watermark_text: "traducido" }; case "fr": //french return { context_messagetranslateoption_text: "Traduire le message", context_messageuntranslateoption_text: "Traduire le message en retour", context_googletranslateoption_text: "Rechercher une traduction", popout_translateoption_text: "Traduire", popout_untranslateoption_text: "Traduire en arrière", translated_watermark_text: "traduit" }; case "it": //italian return { context_messagetranslateoption_text: "Tradurre il messaggio", context_messageuntranslateoption_text: "Tradurre il messaggio indietro", context_googletranslateoption_text: "Cerca la traduzione", popout_translateoption_text: "Traduci", popout_untranslateoption_text: "Traduci indietro", translated_watermark_text: "tradotto" }; case "nl": //dutch return { context_messagetranslateoption_text: "Vertaal bericht", context_messageuntranslateoption_text: "Vertaal bericht terug", context_googletranslateoption_text: "Zoek vertaling", popout_translateoption_text: "Vertaal", popout_untranslateoption_text: "Vertaal terug", translated_watermark_text: "vertaalde" }; case "no": //norwegian return { context_messagetranslateoption_text: "Oversett melding", context_messageuntranslateoption_text: "Oversett melding tilbake", context_googletranslateoption_text: "Søk oversettelse", popout_translateoption_text: "Oversett", popout_untranslateoption_text: "Oversett tilbake", translated_watermark_text: "oversatt" }; case "pl": //polish return { context_messagetranslateoption_text: "Przetłumacz wiadomość", context_messageuntranslateoption_text: "Przetłumacz wiadomość z powrotem", context_googletranslateoption_text: "Wyszukaj tłumaczenie", popout_translateoption_text: "Przetłumacz", popout_untranslateoption_text: "Przetłumacz ponownie", translated_watermark_text: "przetłumaczony" }; case "pt-BR": //portuguese (brazil) return { context_messagetranslateoption_text: "Traduzir mensagem", context_messageuntranslateoption_text: "Traduzir mensagem de volta", context_googletranslateoption_text: "Pesquisar tradução", popout_translateoption_text: "Traduzir", popout_untranslateoption_text: "Traduzir de volta", translated_watermark_text: "traduzido" }; case "fi": //finnish return { context_messagetranslateoption_text: "Käännä viesti", context_messageuntranslateoption_text: "Käännä viesti takaisin", context_googletranslateoption_text: "Etsi käännös", popout_translateoption_text: "Kääntää", popout_untranslateoption_text: "Käännä takaisin", translated_watermark_text: "käännetty" }; case "sv": //swedish return { context_messagetranslateoption_text: "Översätt meddelande", context_messageuntranslateoption_text: "Översätt meddelandet tillbaka", context_googletranslateoption_text: "Sök översättning", popout_translateoption_text: "Översätt", popout_untranslateoption_text: "Översätt tillbaka", translated_watermark_text: "översatt" }; case "tr": //turkish return { context_messagetranslateoption_text: "Mesajı çevir", context_messageuntranslateoption_text: "İletiyi geri çevir", context_googletranslateoption_text: "Arama tercümesi", popout_translateoption_text: "Çevirmek", popout_untranslateoption_text: "Geri çevir", translated_watermark_text: "tercüme" }; case "cs": //czech return { context_messagetranslateoption_text: "Přeposlat zprávu", context_messageuntranslateoption_text: "Přeposlat zprávu zpátky", context_googletranslateoption_text: "Hledat překlad", popout_translateoption_text: "Přeposlat", popout_untranslateoption_text: "Přeposlat zpět", translated_watermark_text: "přeloženo" }; case "bg": //bulgarian return { context_messagetranslateoption_text: "Преведете на съобщението", context_messageuntranslateoption_text: "Преведете съобщението обратно", context_googletranslateoption_text: "Търсене на превод", popout_translateoption_text: "Превод", popout_untranslateoption_text: "Превод обратно", translated_watermark_text: "преведена" }; case "ru": //russian return { context_messagetranslateoption_text: "Перевести сообщение", context_messageuntranslateoption_text: "Перевести сообщение обратно", context_googletranslateoption_text: "Поиск перевода", popout_translateoption_text: "Перевести", popout_untranslateoption_text: "Перевести обратно", translated_watermark_text: "переведенный" }; case "uk": //ukrainian return { context_messagetranslateoption_text: "Перекласти повідомлення", context_messageuntranslateoption_text: "Перекласти повідомлення назад", context_googletranslateoption_text: "Пошук перекладу", popout_translateoption_text: "Перекласти", popout_untranslateoption_text: "Перекласти назад", translated_watermark_text: "перекладений" }; case "ja": //japanese return { context_messagetranslateoption_text: "メッセージを翻訳する", context_messageuntranslateoption_text: "メッセージを翻訳する", context_googletranslateoption_text: "翻訳の検索", popout_translateoption_text: "翻訳", popout_untranslateoption_text: "翻訳する", translated_watermark_text: "翻訳された" }; case "zh-TW": //chinese (traditional) return { context_messagetranslateoption_text: "翻譯消息", context_messageuntranslateoption_text: "翻譯消息", context_googletranslateoption_text: "搜索翻譯", popout_translateoption_text: "翻譯", popout_untranslateoption_text: "翻譯回來", translated_watermark_text: "翻譯" }; case "ko": //korean return { context_messagetranslateoption_text: "메시지 번역", context_messageuntranslateoption_text: "메시지 번역 뒤로", context_googletranslateoption_text: "검색 번역", popout_translateoption_text: "다시", popout_untranslateoption_text: "다시 번역", translated_watermark_text: "번역 된" }; default: //default: english return { context_messagetranslateoption_text: "Translate Message", context_messageuntranslateoption_text: "Untranslate Message", context_googletranslateoption_text: "Search translation", popout_translateoption_text: "Translate", popout_untranslateoption_text: "Untranslate", translated_watermark_text: "translated" }; } } } })();