/** * @name GoogleTranslateOption * @author DevilBro * @authorId 278543574059057154 * @version 2.2.6 * @description Allows you to translate Messages and your outgoing Message within Discord * @invite Jx3TjNS * @donate https://www.paypal.me/MircoWittrien * @patreon https://www.patreon.com/MircoWittrien * @website https://mwittrien.github.io/ * @source https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/GoogleTranslateOption/ * @updateUrl https://mwittrien.github.io/BetterDiscordAddons/Plugins/GoogleTranslateOption/GoogleTranslateOption.plugin.js */ module.exports = (_ => { const config = { "info": { "name": "GoogleTranslateOption", "author": "DevilBro", "version": "2.2.6", "description": "Allows you to translate Messages and your outgoing Message within Discord" }, "changeLog": { "added": { "Show Original Message": "Added an option that, similar to send original message, allows you to translate someone elses message and have the plugin display the translation and the original message", "Per Channel Languages": "You can now press the lock icon in the language settings, this will save any further changes for the languages in a separate save, allowing you to set up a separate language config for each channel" } } }; return !window.BDFDB_Global || (!window.BDFDB_Global.loaded && !window.BDFDB_Global.started) ? class { getName () {return config.info.name;} getAuthor () {return config.info.author;} getVersion () {return config.info.version;} getDescription () {return `The Library Plugin needed for ${config.info.name} is missing. Open the Plugin Settings to download it. \n\n${config.info.description}`;} downloadLibrary () { require("request").get("https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js", (e, r, b) => { if (!e && b && r.statusCode == 200) require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0BDFDB.plugin.js"), b, _ => BdApi.showToast("Finished downloading BDFDB Library", {type: "success"})); else BdApi.alert("Error", "Could not download BDFDB Library Plugin. Try again later or download it manually from GitHub: https://mwittrien.github.io/downloader/?library"); }); } load () { if (!window.BDFDB_Global || !Array.isArray(window.BDFDB_Global.pluginQueue)) window.BDFDB_Global = Object.assign({}, window.BDFDB_Global, {pluginQueue: []}); if (!window.BDFDB_Global.downloadModal) { window.BDFDB_Global.downloadModal = true; BdApi.showConfirmationModal("Library Missing", `The Library Plugin needed for ${config.info.name} is missing. Please click "Download Now" to install it.`, { confirmText: "Download Now", cancelText: "Cancel", onCancel: _ => {delete window.BDFDB_Global.downloadModal;}, onConfirm: _ => { delete window.BDFDB_Global.downloadModal; this.downloadLibrary(); } }); } if (!window.BDFDB_Global.pluginQueue.includes(config.info.name)) window.BDFDB_Global.pluginQueue.push(config.info.name); } start () {this.load();} stop () {} getSettingsPanel () { let template = document.createElement("template"); template.innerHTML = `
The Library Plugin needed for ${config.info.name} is missing.\nPlease click Download Now to install it.
`; template.content.firstElementChild.querySelector("a").addEventListener("click", this.downloadLibrary); return template.content.firstElementChild; } } : (([Plugin, BDFDB]) => { var _this; const translateIconGeneral = ``; const translateIconMask = ``; const translateIcon = translateIconGeneral.replace(``, ``).replace(``, ``).replace(` mask="url(#translateIconMask)"`, ``); const translateIconUntranslate = translateIconGeneral.replace(``, ``).replace(``, translateIconMask); const TranslateButtonComponent = class TranslateButton extends BdApi.React.Component { render() { const enabled = _this.isTranslationEnabled(this.props.channelId); return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ChannelTextAreaButton, { className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN._googletranslateoptiontranslatebutton, _this.isTranslationEnabled(this.props.channelId) && BDFDB.disCN._googletranslateoptiontranslating, BDFDB.disCN.textareapickerbutton), isActive: this.props.isActive, iconSVG: translateIcon, nativeClass: true, tooltip: enabled && { text: (_ => `${BDFDB.LanguageUtils.getName(languages[_this.getLanguageChoice(languageTypes.INPUT, messageTypes.SENT, this.props.channelId)])} ➝ ${BDFDB.LanguageUtils.getName(languages[_this.getLanguageChoice(languageTypes.OUTPUT, messageTypes.SENT, this.props.channelId)])}`), tooltipConfig: {style: "max-width: 400px"} }, onClick: _ => { this.props.isActive = true; BDFDB.ReactUtils.forceUpdate(this); BDFDB.ModalUtils.open(_this, { size: "LARGE", header: BDFDB.LanguageUtils.LanguageStrings.SETTINGS, subHeader: "", onClose: _ => { this.props.isActive = false; BDFDB.ReactUtils.forceUpdate(this); }, children: BDFDB.ReactUtils.createElement(TranslateSettingsComponent, { channelId: this.props.channelId }) }); }, onContextMenu: _ => { _this.toggleTranslation(this.props.channelId); BDFDB.ReactUtils.forceUpdate(this); } }); } }; const TranslateSettingsComponent = class TranslateSettings extends BdApi.React.Component { filterLanguages(direction, place) { let isOutput = direction == languageTypes.OUTPUT; return BDFDB.ObjectUtils.toArray(BDFDB.ObjectUtils.map(isOutput ? BDFDB.ObjectUtils.filter(languages, lang => !lang.auto) : languages, (lang, id) => ({ value: id, label: BDFDB.LanguageUtils.getName(lang), backup: this.isOnlyBackup(lang) }))).filter(isOutput && this.isOnlyBackup(languages[_this.getLanguageChoice(languageTypes.INPUT, place, this.props.channelId)]) ? (n => n.backup) : (n => n)); } isOnlyBackup(lang) { return lang.auto && !translationEngines[_this.settings.engines.translator].auto || !lang.auto && !lang.special && !translationEngines[_this.settings.engines.translator].languages.includes(lang.id); } render() { return [ BDFDB.ArrayUtils.is(_this.settings.exceptions.wordStart) && _this.settings.exceptions.wordStart.length && [ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, { className: BDFDB.disCN.marginbottom8, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsLabel, { label: `Words starting with ${_this.settings.exceptions.wordStart.map(n => '"' + n + '"').join(", ")} will be ignored` }) }), BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, { className: BDFDB.disCN.marginbottom8 }) ], Object.keys(_this.defaults.choices).map(place => { const isSpecific = channelLanguages[this.props.channelId] && channelLanguages[this.props.channelId][place]; return Object.keys(_this.defaults.choices[place].value).map(direction => [ BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, { title: `${BDFDB.LibraryModules.StringUtils.upperCaseFirstChar(direction.toLowerCase())} Language in ${place.toLowerCase()} Messages: `, titleChildren: direction == languageTypes.OUTPUT && [{ text: _ => isSpecific ? `${BDFDB.LibraryModules.StringUtils.upperCaseFirstChar(place)} Language Selection will be changed specifically for this Channel` : `${BDFDB.LibraryModules.StringUtils.upperCaseFirstChar(place)} Language Selection will be changed for all Channels`, name: isSpecific ? BDFDB.LibraryComponents.SvgIcon.Names.LOCK_CLOSED : BDFDB.LibraryComponents.SvgIcon.Names.LOCK_OPEN, color: isSpecific && "var(--bdfdb-red)", onClick: _ => { if (channelLanguages[this.props.channelId] && channelLanguages[this.props.channelId][place]) { delete channelLanguages[this.props.channelId][place]; if (BDFDB.ObjectUtils.isEmpty(channelLanguages[this.props.channelId])) delete channelLanguages[this.props.channelId]; } else { if (!channelLanguages[this.props.channelId]) channelLanguages[this.props.channelId] = {}; channelLanguages[this.props.channelId][place] = {}; for (let l in languageTypes) channelLanguages[this.props.channelId][place][languageTypes[l]] = _this.getLanguageChoice(languageTypes[l], place, null); } BDFDB.DataUtils.save(channelLanguages, _this, "channelLanguages"); BDFDB.ReactUtils.forceUpdate(this); } }, { iconSVG: ``, onClick: _ => { let input = _this.getLanguageChoice(languageTypes.INPUT, place, this.props.channelId); let output = _this.getLanguageChoice(languageTypes.OUTPUT, place, this.props.channelId); input = input == "auto" ? "en" : input; _this.saveLanguageChoice(output, languageTypes.INPUT, place, this.props.channelId); _this.saveLanguageChoice(input, languageTypes.OUTPUT, place, this.props.channelId); _this.setLanguages(); BDFDB.ReactUtils.forceUpdate(this); } }].map(data => { const icon = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, { className: "aaa", onClick: data.onClick, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { width: 24, height: 24, color: data.color || "currentColor", name: data.name, iconSVG: data.iconSVG }) }); return data.text ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {text: data.text, children: icon}) : icon; }), className: BDFDB.disCN.marginbottom8, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Select, { value: _this.getLanguageChoice(direction, place, this.props.channelId), options: this.filterLanguages(direction, place), optionRenderer: lang => 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 }), lang.backup && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { text: "Will use Backup Translator", tooltipConfig: { color: "red" }, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { nativeClass: true, width: 20, height: 20, color: "var(--bdfdb-red)", name: BDFDB.LibraryComponents.SvgIcon.Names.WARNING }) }), BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FavButton, { isFavorite: languages[lang.value].fav == 0, onClick: value => { if (value) favorites.push(lang.value); else BDFDB.ArrayUtils.remove(favorites, lang.value, true); BDFDB.DataUtils.save(favorites.sort(), _this, "favorites"); _this.setLanguages(); } }) ] }) : null, onChange: value => _this.saveLanguageChoice(value, direction, place, this.props.channelId) }) }), direction == languageTypes.OUTPUT && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, { className: BDFDB.disCN.marginbottom8 }) ]); }), Object.keys(_this.defaults.engines).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, { title: _this.defaults.engines[key].description, className: BDFDB.disCN.marginbottom8, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Select, { value: _this.settings.engines[key], options: (key == "backup" ? ["----"] : []).concat(Object.keys(translationEngines)).filter(key == "backup" ? (n => n != _this.settings.engines.translator) : (n => n)).map(engineKey => ({value: engineKey, label: translationEngines[engineKey] ? translationEngines[engineKey].name : "----"})), maxVisibleItems: 3, onChange: value => { _this.settings.engines[key] = value; BDFDB.DataUtils.save(_this.settings.engines, _this, "engines"); _this.setLanguages(); BDFDB.ReactUtils.forceUpdate(this); } }) })), Object.keys(_this.defaults.general).filter(key => _this.defaults.general[key].popout).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, { type: "Switch", plugin: _this, keys: ["general", key], label: _this.defaults.general[key].description, tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5, value: _this.settings.general[key] })), BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsItem, { type: "Switch", label: "Translate your Messages before sending", tag: BDFDB.LibraryComponents.FormComponents.FormTitle.Tags.H5, value: _this.isTranslationEnabled(this.props.channelId), onChange: value => { _this.toggleTranslation(this.props.channelId); BDFDB.ReactUtils.forceUpdate(this); } }) ].flat(10).filter(n => n); } }; 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: "Google", auto: true, funcName: "googleApiTranslate", languages: googleLanguages}, deepl: {name: "DeepL", auto: true, funcName: "deepLTranslate", languages: ["bg","cs","da","de","en","el","es","et","fi","fr","hu","it","ja","lt","lv","nl","pl","pt","ro","ru","sk","sl","sv","zh"]}, itranslate: {name: "iTranslate", auto: true, 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", auto: true, 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"]}, papago: {name: "Papago", auto: false, funcName: "papagoTranslate", languages: ["en","es","fr","id","ja","ko","th","vi","zh-CN","zh-TW"]} }; var languages = {}; var favorites = []; var channelLanguages = {}; var translationEnabledStates = [], isTranslating; var translatedMessages = {}, oldMessages = {}; const defaultLanguages = { INPUT: "auto", OUTPUT: "$discord" }; const languageTypes = { INPUT: "input", OUTPUT: "output" }; const messageTypes = { RECEIVED: "received", SENT: "sent", }; return class GoogleTranslateOption extends Plugin { onLoad () { _this = this; this.defaults = { general: { addTranslateButton: {value: true, popout: false, description: "Adds a Translate Button to the Channel Textarea"}, usePerChatTranslation: {value: true, popout: false, description: "Enables/Disables the Translator Button State per Channel and not globally"}, sendOriginalMessage: {value: false, popout: true, description: "Also sends the original Message when translating your own Message"}, showOriginalMessage: {value: false, popout: true, description: "Also shows the original Message when translating someones Message"} }, choices: {}, exceptions: { wordStart: {value: ["!"], max: 1, description: "Words starting with any of these will be ignored"} }, engines: { translator: {value: "googleapi", description: "Translation Engine"}, backup: {value: "----", description: "Backup Engine"} } }; for (let m in messageTypes) this.defaults.choices[messageTypes[m]] = {value: Object.keys(languageTypes).reduce((newObj, l) => (newObj[languageTypes[l]] = defaultLanguages[l], newObj), {})}; this.patchedModules = { before: { ChannelTextAreaForm: "render", ChannelEditorContainer: "render", Embed: "render" }, after: { ChannelTextAreaContainer: "render", Messages: "type", MessageContent: "type", Embed: "render" } }; this.css = ` ${BDFDB.dotCN._googletranslateoptiontranslatebutton + BDFDB.dotCNS._googletranslateoptiontranslating + BDFDB.dotCN.textareaicon} { color: var(--bdfdb-red) !important; } `; } onStart () { // REMOVE 25.05.2021 let loadedFavorites = BDFDB.DataUtils.load(this, "favorites"); if (BDFDB.ObjectUtils.is(loadedFavorites) && Object.keys(loadedFavorites).length) BDFDB.DataUtils.save(Object.keys(loadedFavorites), this, "favorites") this.forceUpdateAll(); } onStop () { this.forceUpdateAll(); } getSettingsPanel (collapseStates = {}) { let settingsPanel; return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, { collapseStates: collapseStates, children: _ => { let settingsItems = []; for (let key in this.defaults.general) settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, { type: "Switch", plugin: this, keys: ["general", key], label: this.defaults.general[key].description, value: this.settings.general[key] })); settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormDivider, { className: BDFDB.disCNS.dividerdefault + BDFDB.disCN.marginbottom8 })); for (let key in this.defaults.exceptions) settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, { title: this.defaults.exceptions[key].description, className: BDFDB.disCN.marginbottom8, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ListInput, { placeholder: "New Exception", maxLength: this.defaults.exceptions[key].max, items: this.settings.exceptions[key], onChange: value => { this.SettingsUpdated = true; BDFDB.DataUtils.save(value, this, "exceptions", key); } }) })); return settingsItems.flat(10); } }); } onSettingsClosed () { if (this.SettingsUpdated) { delete this.SettingsUpdated; this.forceUpdateAll(); } } forceUpdateAll () { favorites = BDFDB.DataUtils.load(this, "favorites"); favorites = !BDFDB.ArrayUtils.is(favorites) ? [] : favorites; channelLanguages = BDFDB.DataUtils.load(this, "channelLanguages"); this.setLanguages(); BDFDB.PatchUtils.forceAllUpdates(this); BDFDB.MessageUtils.rerenderAll(); } onMessageContextMenu (e) { if (e.instance.props.message && e.instance.props.channel) { let translated = translatedMessages[e.instance.props.message.id]; let hint = BDFDB.BDUtils.isPluginEnabled("MessageUtilities") ? BDFDB.BDUtils.getPlugin("MessageUtilities").getActiveShortcutString("__Translate_Message") : null; let [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: ["pin", "unpin"]}); if (index == -1) [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: ["edit", "add-reaction", "quote"]}); children.splice(index > -1 ? index + 1 : 0, 0, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, { label: translated ? this.labels.context_messageuntranslateoption : this.labels.context_messagetranslateoption, id: BDFDB.ContextMenuUtils.createItemId(this.name, translated ? "untranslate-message" : "translate-message"), hint: hint && (_ => { return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.MenuItems.MenuHint, { hint: hint }); }), disabled: !translated && isTranslating, action: _ => this.translateMessage(e.instance.props.message, e.instance.props.channel) })); this.injectSearchItem(e); } } onNativeContextMenu (e) { this.injectSearchItem(e); } onSlateContextMenu (e) { this.injectSearchItem(e); } injectSearchItem (e) { let text = document.getSelection().toString(); if (text) { let translating, foundTranslation, foundInput, foundOutput; let [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: ["devmode-copy-id", "search-google"], group: true}); children.splice(index > -1 ? index + 1 : 0, 0, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, { children: BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, { id: BDFDB.ContextMenuUtils.createItemId(this.name, "search-translation"), disabled: isTranslating, label: this.labels.context_googletranslateoption, persisting: true, action: event => { let item = BDFDB.DOMUtils.getParent(BDFDB.dotCN.menuitem, event.target); if (item) { let createTooltip = _ => { BDFDB.TooltipUtils.create(item, [ `${BDFDB.LanguageUtils.LibraryStrings.from} ${foundInput.name}:`, text, `${BDFDB.LanguageUtils.LibraryStrings.to} ${foundOutput.name}:`, foundTranslation ].map(n => BDFDB.ReactUtils.createElement("div", {children: n})), { type: "right", color: "black", className: "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)); } else createTooltip(); } else if (!translating) { translating = true; this.translateText(text, messageTypes.RECEIVED, (translation, input, output) => { if (translation) { foundTranslation = translation, foundInput = input, foundOutput = output; createTooltip(); } }); } } } }) })); } } onMessageOptionContextMenu (e) { if (e.instance.props.message && e.instance.props.channel) { let translated = !!translatedMessages[e.instance.props.message.id]; let [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: ["pin", "unpin"]}); children.splice(index + 1, 0, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, { label: translated ? this.labels.context_messageuntranslateoption : this.labels.context_messagetranslateoption, disabled: isTranslating, id: BDFDB.ContextMenuUtils.createItemId(this.name, translated ? "untranslate-message" : "translate-message"), icon: _ => { return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.MenuItems.MenuIcon, { icon: translated ? translateIconUntranslate : translateIcon }); }, action: _ => this.translateMessage(e.instance.props.message, e.instance.props.channel) })); } } onMessageOptionToolbar (e) { if (e.instance.props.expanded && e.instance.props.message && e.instance.props.channel) { let translated = !!translatedMessages[e.instance.props.message.id]; e.returnvalue.props.children.unshift(); e.returnvalue.props.children.unshift(BDFDB.ReactUtils.createElement(class extends BdApi.React.Component { render() { return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { key: translated ? "untranslate-message" : "translate-message", text: _ => translated ? _this.labels.context_messageuntranslateoption : _this.labels.context_messagetranslateoption, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Clickable, { className: BDFDB.disCN.messagetoolbarbutton, onClick: _ => { if (!isTranslating) _this.translateMessage(e.instance.props.message, e.instance.props.channel).then(_ => { translated = !!translatedMessages[e.instance.props.message.id]; BDFDB.ReactUtils.forceUpdate(this); }); }, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { className: BDFDB.disCN.messagetoolbaricon, iconSVG: translated ? translateIconUntranslate : translateIcon }) }) }); } })); } } processChannelTextAreaForm (e) { BDFDB.PatchUtils.patch(this, e.instance, "handleSendMessage", {instead: e2 => { if (this.isTranslationEnabled(e.instance.props.channel.id)) { e2.stopOriginalMethodCall(); this.translateText(e2.methodArguments[0], messageTypes.SENT, (translation, input, output) => { translation = !translation ? e2.methodArguments[0] : (this.settings.general.sendOriginalMessage ? (e2.methodArguments[0] + "\n\n" + translation) : translation); e2.originalMethod(translation); }); return Promise.resolve({ shouldClear: true, shouldRefocus: true }); } else return e2.callOriginalMethodAfterwards(); }}, {force: true, noCache: true}); } processChannelEditorContainer (e) { if (this.isTranslationEnabled(e.instance.props.channel.id) && isTranslating) e.instance.props.disabled = true; } processChannelTextAreaContainer (e) { if (this.settings.general.addTranslateButton) { let editor = BDFDB.ReactUtils.findChild(e.returnvalue, {name: "ChannelEditorContainer"}); if (editor && editor.props.type == BDFDB.DiscordConstants.TextareaTypes.NORMAL && !editor.props.disabled) { let [children, index] = BDFDB.ReactUtils.findParent(e.returnvalue, {props: [["className", BDFDB.disCN.textareapickerbuttons]]}); if (index > -1 && children[index].props && children[index].props.children) children[index].props.children.unshift(BDFDB.ReactUtils.createElement(TranslateButtonComponent, { channelId: e.instance.props.channel.id })); } } } processMessages (e) { e.returnvalue.props.children.props.channelStream = [].concat(e.returnvalue.props.children.props.channelStream); for (let i in e.returnvalue.props.children.props.channelStream) { let message = e.returnvalue.props.children.props.channelStream[i].content; if (message) { if (BDFDB.ArrayUtils.is(message.attachments)) this.checkMessage(e.returnvalue.props.children.props.channelStream[i], message); else if (BDFDB.ArrayUtils.is(message)) for (let j in message) { let childMessage = message[j].content; if (childMessage && BDFDB.ArrayUtils.is(childMessage.attachments)) this.checkMessage(message[j], childMessage); } } } } checkMessage (stream, message) { let translation = translatedMessages[message.id]; if (translation) stream.content.content = translation.content; else if (oldMessages[message.id] && Object.keys(message).some(key => !BDFDB.equals(oldMessages[message.id][key], message[key]))) { stream.content.content = oldMessages[message.id].content; delete oldMessages[message.id]; } } processMessageContent (e) { if (e.instance.props.message) { let translation = translatedMessages[e.instance.props.message.id]; if (translation && translation.content) e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { text: `${BDFDB.LanguageUtils.getName(translation.input)} ➝ ${BDFDB.LanguageUtils.LibraryStrings.to} ${BDFDB.LanguageUtils.getName(translation.output)}`, tooltipConfig: {style: "max-width: 400px"}, children: BDFDB.ReactUtils.createElement("span", { className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.messagetimestamp, BDFDB.disCN.messagetimestampinline, BDFDB.disCN._googletranslateoptiontranslated), children: BDFDB.ReactUtils.createElement("span", { className: BDFDB.disCN.messageedited, children: `(${this.labels.translated_watermark})` }) }) })); } } processEmbed (e) { if (e.instance.props.embed && e.instance.props.embed.message_id) { let translation = translatedMessages[e.instance.props.embed.message_id]; if (translation && Object.keys(translation.embeds).length) { 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.findParent(e.returnvalue, {props: [["className", BDFDB.disCN.embeddescription]]}); if (index > -1) children[index].props.children.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, { text: `${BDFDB.LanguageUtils.getName(translation.input)} ➝ ${BDFDB.LanguageUtils.getName(translation.output)}`, tooltipConfig: {style: "max-width: 400px"}, children: BDFDB.ReactUtils.createElement("span", { className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.messagetimestamp, BDFDB.disCN.messagetimestampinline, BDFDB.disCN._googletranslateoptiontranslated), children: BDFDB.ReactUtils.createElement("span", { className: BDFDB.disCN.messageedited, children: `(${this.labels.translated_watermark})` }) }) })); } } 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; } } } toggleTranslation (channelId) { if (!this.isTranslationEnabled(channelId)) translationEnabledStates.push(this.settings.general.usePerChatTranslation ? channelId : "global"); else BDFDB.ArrayUtils.remove(translationEnabledStates, this.settings.general.usePerChatTranslation ? channelId : "global", true); } isTranslationEnabled (channelId) { return translationEnabledStates.includes(this.settings.general.usePerChatTranslation ? channelId : "global"); } setLanguages () { if (this.settings.engines.translator == this.settings.engines.backup) { this.settings.engines.backup = Object.keys(translationEngines).filter(n => n != this.settings.engines.translator)[0]; BDFDB.DataUtils.save(this.settings.engines, this, "engines"); } let engine = translationEngines[this.settings.engines.translator] || {}; let backup = translationEngines[this.settings.engines.backup] || {}; let languageIds = [].concat(engine.languages, backup.languages).flat(10).filter(n => n); languages = BDFDB.ObjectUtils.deepAssign( !engine.auto && !backup.auto ? {} : { auto: { auto: true, name: "Auto", id: "auto" } }, BDFDB.ObjectUtils.filter(BDFDB.LanguageUtils.languages, lang => languageIds.includes(lang.id)), { binary: { special: true, name: "Binary", id: "binary" }, braille: { special: true, name: "Braille 6-dot", id: "braille" }, morse: { special: true, name: "Morse", id: "morse" } } ); for (let id in languages) languages[id].fav = favorites.includes(id) ? 0 : 1; languages = BDFDB.ObjectUtils.sort(languages, "fav"); } getLanguageChoice (direction, place, channelId) { this.setLanguages(); let choice; if (channelLanguages[channelId] && channelLanguages[channelId][place]) choice = channelLanguages[channelId][place][direction]; else choice = this.settings.choices[place] && this.settings.choices[place][direction]; choice = languages[choice] ? choice : Object.keys(languages)[0]; choice = direction == languageTypes.OUTPUT && choice == "auto" ? "en" : choice; return choice; } saveLanguageChoice (choice, direction, place, channelId) { if (channelLanguages[channelId] && channelLanguages[channelId][place]) { channelLanguages[channelId][place][direction] = choice; BDFDB.DataUtils.save(channelLanguages, this, "channelLanguages"); } else { this.settings.choices[place][direction] = choice; BDFDB.DataUtils.save(this.settings.choices, this, "choices"); } } translateMessage (message, channel) { return new Promise(callback => { if (!message) return callback(null); if (translatedMessages[message.id]) { delete translatedMessages[message.id]; BDFDB.MessageUtils.rerenderAll(true); callback(false); } else { let orignalContent = message.content || ""; for (let embed of message.embeds) orignalContent += ("\n__________________ __________________ __________________\n" + embed.rawDescription); this.translateText(orignalContent, messageTypes.RECEIVED, (translation, input, output) => { if (translation) { oldMessages[message.id] = new BDFDB.DiscordObjects.Message(message); let oldStrings = orignalContent.split(/\n{0,1}__________________ __________________ __________________\n{0,1}/); let strings = translation.split(/\n{0,1}__________________ __________________ __________________\n{0,1}/); let oldContent = this.settings.general.showOriginalMessage && (oldStrings.shift() || "").trim(); let content = (strings.shift() || "").trim() + (oldContent ? `\n\n${oldContent}` : ""); let embeds = {}; for (let i in message.embeds) { message.embeds[i].message_id = message.id; let oldEmbedString = this.settings.general.showOriginalMessage && (oldStrings.shift() || "").trim(); embeds[message.embeds[i].id] = (strings.shift() || message.embeds[i].rawDescription).trim() + (oldEmbedString ? `\n\n${oldEmbedString}` : ""); } translatedMessages[message.id] = {content, embeds, input, output}; BDFDB.MessageUtils.rerenderAll(true); } callback(true); }); } }); } translateText (text, place, callback) { let toast = null, toastInterval, finished = false, finishTranslation = translation => { isTranslating = false; if (toast) toast.close(); BDFDB.TimeUtils.clear(toastInterval); if (finished) return; finished = true; if (translation) translation = this.addExceptions(translation, excepts); callback(translation == text ? "" : translation, input, output); }; let [newText, excepts, translate] = this.removeExceptions(text.trim(), place); let channelId = BDFDB.LibraryModules.LastChannelStore.getChannelId(); let input = Object.assign({}, languages[this.getLanguageChoice(languageTypes.INPUT, place, channelId)]); let output = Object.assign({}, languages[this.getLanguageChoice(languageTypes.OUTPUT, place, channelId)]); if (translate && input.id != output.id) { 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.special) { 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 { const startTranslating = engine => { isTranslating = true; if (toast) toast.close(); BDFDB.TimeUtils.clear(toastInterval); let loadingString = `${this.labels.toast_translating} (${translationEngines[engine].name}) - ${BDFDB.LanguageUtils.LibraryStrings.please_wait}`; let currentLoadingString = loadingString; toast = BDFDB.NotificationUtils.toast(loadingString, { timeout: 0, position: "center", onClose: _ => BDFDB.TimeUtils.clear(toastInterval) }); toastInterval = BDFDB.TimeUtils.interval((_, count) => { if (count > 40) { finishTranslation(""); BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed} (${translationEngines[engine].name}) - ${this.labels.toast_translating_tryanother}`, { type: "danger", position: "center" }); } else { currentLoadingString = currentLoadingString.endsWith(".....") ? loadingString : currentLoadingString + "."; toast.update(currentLoadingString); } }, 500); }; if (this.validTranslator(this.settings.engines.translator, input, output, specialCase)) { startTranslating(this.settings.engines.translator); this[translationEngines[this.settings.engines.translator].funcName].apply(this, [{input, output, text: newText, specialCase, engine: translationEngines[this.settings.engines.translator]}, translation => { if (!translation && this.validTranslator(this.settings.engines.backup, input, output, specialCase)) { startTranslating(this.settings.engines.backup); this[translationEngines[this.settings.engines.backup].funcName].apply(this, [{input, output, text: newText, specialCase, engine: translationEngines[this.settings.engines.backup]}, finishTranslation]); } else finishTranslation(translation); }]); } else if (this.validTranslator(this.settings.engines.backup, input, output, specialCase)) { startTranslating(this.settings.engines.backup); this[translationEngines[this.settings.engines.backup].funcName].apply(this, [{input, output, text: newText, specialCase, engine: translationEngines[this.settings.engines.backup]}, finishTranslation]); } else finishTranslation(); } } else finishTranslation(); } validTranslator (key, input, output, specialCase) { return translationEngines[key] && typeof this[translationEngines[key].funcName] == "function" && (specialCase || input.auto && translationEngines[key].auto || translationEngines[key].languages.includes(input.id) && translationEngines[key].languages.includes(output.id)); } 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, body) => { if (!error && body && response.statusCode == 200) { try { body = JSON.parse(body); if (!data.specialCase && body.src && body.src && languages[body.src]) { data.input.name = languages[body.src].name; data.input.ownlang = languages[body.src].ownlang; } callback(body.sentences.map(n => n && n.trans).filter(n => n).join("")); } catch (err) {callback("");} } else { if (response.statusCode == 429) BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Request Limit per Hour reached.`, { type: "danger", position: "center" }); else BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Translation Server might be down.`, { type: "danger", position: "center" }); callback(""); } }); } deepLTranslate (data, callback) { BDFDB.LibraryRequires.request(`https://api-free.deepl.com/v2/translate?auth_key=75cc2f40-fdae-14cd-7242-6a384e2abb9c:fx&text=${encodeURIComponent(data.text)}${data.input.auto ? "" : `&source_lang=${data.input.id}`}&target_lang=${data.output.id}`, (error, response, body) => { if (!error && body && response.statusCode == 200) { try { body = JSON.parse(body); if (!data.specialCase && body.translations[0] && body.translations[0].detected_source_language && languages[body.translations[0].detected_source_language.toLowerCase()]) { data.input.name = languages[body.translations[0].detected_source_language.toLowerCase()].name; data.input.ownlang = languages[body.translations[0].detected_source_language.toLowerCase()].ownlang; } callback(body.translations.map(n => n && n.text).filter(n => n).join("")); } catch (err) {callback("");} } else { console.log(response); if (response.statusCode == 429 || response.statusCode == 456) BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Request Limit reached.`, { type: "danger", position: "center" }); else if (response.statusCode == 403) BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. API-Key outdated.`, { type: "danger", position: "center" }); else BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Translation Server might be down.`, { type: "danger", position: "center" }); callback(""); } }); } iTranslateTranslate (data, callback) { let translate = _ => { BDFDB.LibraryRequires.request.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, body) => { if (!error && response && response.statusCode == 200) { try { body = JSON.parse(body); if (!data.specialCase && body.source && body.source.dialect && languages[body.source.dialect]) { data.input.name = languages[body.source.dialect].name; data.input.ownlang = languages[body.source.dialect].ownlang; } callback(body.target.text); } catch (err) {callback("");} } else { if (response.statusCode == 429) BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Request Limit reached.`, { type: "danger", position: "center" }); else if (response.statusCode == 403) BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. API-Key outdated.`, { type: "danger", position: "center" }); else BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Translation Server might be down.`, { type: "danger", position: "center" }); callback(""); } }); }; if (data.engine.APIkey) translate(); else BDFDB.LibraryRequires.request("https://www.itranslate.com/js/webapp/main.js", {gzip: true}, (error, response, body) => { if (!error && body) { let APIkey = /var API_KEY = "(.+)"/.exec(body); 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.auto ? data.output.id : (data.input.id + "-" + data.output.id)}&options=1`, (error, response, body) => { if (!error && body && response.statusCode == 200) { body = BDFDB.DOMUtils.create(body); let translation = body.querySelector("text"); let detected = body.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(""); } if (body && body.indexOf('code="408"') > -1) { BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Monthly Request Limit reached.`, { type: "danger", position: "center" }); callback(""); } else { BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Translation Server down or API-Key outdated.`, { type: "danger", position: "center" }); callback(""); } }); } papagoTranslate (data, callback) { BDFDB.LibraryRequires.request.post({ url: "https://openapi.naver.com/v1/papago/n2mt", form: { source: data.input.id, target: data.output.id, text: data.text }, headers: { "X-Naver-Client-Id": "kUNGxtAmTJQFbaFehdjk", "X-Naver-Client-Secret": "zC70k3VhpM" } }, (error, response, body) => { if (!error && body && response.statusCode == 200) { try { let message = (JSON.parse(body) || {}).message; if (message && message.body && message.body.translatedText) callback(message.body.translatedText); else callback(""); } catch (err) {callback("");} } else { if (response.statusCode == 429) BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Request Limit per Hour is reached.`, { type: "danger", position: "center" }); else BDFDB.NotificationUtils.toast(`${this.labels.toast_translating_failed}. ${this.labels.toast_translating_tryanother}. Translation Server is down or API-key outdated.`, { type: "danger", position: "center" }); callback(""); } }); } checkForSpecialCase (text, input) { if (input.special) return input; else if (input.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) { let binary = ""; for (let character of string) binary += parseInt(character.charCodeAt(0).toString(2)).toPrecision(8).split(".").reverse().join("").toString() + " "; return binary; } string2braille (string) { let braille = ""; for (let character of string) braille += brailleConverter[character.toLowerCase()] ? brailleConverter[character.toLowerCase()] : character; return braille; } string2morse (string) { string = string.replace(/ /g, "%%%%%%%%%%"); let 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) { let string = ""; binary = binary.replace(/\n/g, "00001010").replace(/\r/g, "00001101").replace(/\t/g, "00001001").replace(/\s/g, ""); if (/^[0-1]*$/.test(binary)) { let eightDigits = ""; let counter = 0; for (let 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: "danger", position: "center" }); return string; } braille2string (braille) { let string = ""; for (let character of braille) string += brailleConverter[character.toLowerCase()] ? brailleConverter[character.toLowerCase()] : character; return string; } morse2string (morse) { let 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, excepts) { for (let count in excepts) { let exception = BDFDB.ArrayUtils.is(this.settings.exceptions.wordStart) && this.settings.exceptions.wordStart.some(n => excepts[count].indexOf(n) == 0) ? excepts[count].slice(1) : excepts[count]; let newString = string.replace(new RegExp(BDFDB.StringUtils.regEscape(`{{${count}}}`)), exception); if (newString == string) string = newString + " " + exception; else string = newString; } return string; } removeExceptions (string, place) { let excepts = {}, newString = [], count = 0; if (place == messageTypes.RECEIVED) { 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}}}`); excepts[count] = text[j]; count++; } else newString.push(text[j]); } } else { let usedExceptions = BDFDB.ArrayUtils.is(this.settings.exceptions.wordStart) ? this.settings.exceptions.wordStart : []; string.split(" ").forEach(word => { if (word.indexOf("<@!") == 0 || word.indexOf("<#") == 0 || word.indexOf(":") == 0 || word.indexOf("<:") == 0 || word.indexOf(" word.indexOf(n) == 0 && word.length > 1)) { newString.push(`{{${count}}}`); excepts[count] = word; count++; } else newString.push(word); }); } return [newString.join(" "), excepts, newString.length-count != 0]; } getGoogleTranslatePageURL (input, output, text) { return `https://translate.google.com/#${BDFDB.LanguageUtils.languages[input] ? input : "auto"}/${output}/${encodeURIComponent(text)}`; } setLabelsByLanguage () { switch (BDFDB.LanguageUtils.getLanguage().id) { case "bg": // Bulgarian return { context_googletranslateoption: "Търсене превод", context_messagetranslateoption: "Превод на съобщението", context_messageuntranslateoption: "Превод на съобщението", popout_translateoption: "Превод", popout_untranslateoption: "Непревод", toast_translating: "Превод", toast_translating_failed: "Преводът не бе успешен", toast_translating_tryanother: "Опитайте друг преводач", translated_watermark: "преведено" }; case "da": // Danish return { context_googletranslateoption: "Søg oversættelse", context_messagetranslateoption: "Oversæt besked", context_messageuntranslateoption: "Ikke-oversat besked", popout_translateoption: "Oversætte", popout_untranslateoption: "Untranslate", toast_translating: "Oversætter", toast_translating_failed: "Kunne ikke oversætte", toast_translating_tryanother: "Prøv en anden oversætter", translated_watermark: "oversat" }; case "de": // German return { context_googletranslateoption: "Übersetzung suchen", context_messagetranslateoption: "Nachricht übersetzen", context_messageuntranslateoption: "Nachricht unübersetzen", popout_translateoption: "Übersetzen", popout_untranslateoption: "Unübersetzen", toast_translating: "Übersetzen", toast_translating_failed: "Übersetzung fehlgeschlagen", toast_translating_tryanother: "Versuch einen anderen Übersetzer", translated_watermark: "übersetzt" }; case "el": // Greek return { context_googletranslateoption: "Αναζήτηση μετάφρασης", context_messagetranslateoption: "Μετάφραση μηνύματος", context_messageuntranslateoption: "Μη μετάφραση μηνύματος", popout_translateoption: "Μεταφράζω", popout_untranslateoption: "Μη μετάφραση", toast_translating: "Μετάφραση", toast_translating_failed: "Αποτυχία μετάφρασης", toast_translating_tryanother: "Δοκιμάστε έναν άλλο Μεταφραστή", translated_watermark: "μεταφρασμένο" }; case "es": // Spanish return { context_googletranslateoption: "Buscar traducción", context_messagetranslateoption: "Traducir mensaje", context_messageuntranslateoption: "Mensaje sin traducir", popout_translateoption: "Traducir", popout_untranslateoption: "No traducir", toast_translating: "Traductorio", toast_translating_failed: "No se pudo traducir", toast_translating_tryanother: "Prueba con otro traductor", translated_watermark: "traducido" }; case "fi": // Finnish return { context_googletranslateoption: "Hae käännöstä", context_messagetranslateoption: "Käännä viesti", context_messageuntranslateoption: "Käännä viesti", popout_translateoption: "Kääntää", popout_untranslateoption: "Käännä", toast_translating: "Kääntäminen", toast_translating_failed: "Käännös epäonnistui", toast_translating_tryanother: "Kokeile toista kääntäjää", translated_watermark: "käännetty" }; case "fr": // French return { context_googletranslateoption: "Recherche de traduction", context_messagetranslateoption: "Traduire le message", context_messageuntranslateoption: "Message non traduit", popout_translateoption: "Traduire", popout_untranslateoption: "Non traduit", toast_translating: "Traduction en cours", toast_translating_failed: "Échec de la traduction", toast_translating_tryanother: "Essayez un autre traducteur", translated_watermark: "traduit" }; case "hr": // Croatian return { context_googletranslateoption: "Pretraži prijevod", context_messagetranslateoption: "Prevedi poruku", context_messageuntranslateoption: "Prevedi poruku", popout_translateoption: "Prevedi", popout_untranslateoption: "Neprevedi", toast_translating: "Prevođenje", toast_translating_failed: "Prijevod nije uspio", toast_translating_tryanother: "Pokušajte s drugim prevoditeljem", translated_watermark: "prevedeno" }; case "hu": // Hungarian return { context_googletranslateoption: "Keresés a fordításban", context_messagetranslateoption: "Üzenet lefordítása", context_messageuntranslateoption: "Az üzenet lefordítása", popout_translateoption: "fordít", popout_untranslateoption: "Fordítás le", toast_translating: "Fordítás", toast_translating_failed: "Nem sikerült lefordítani", toast_translating_tryanother: "Próbálkozzon másik fordítóval", translated_watermark: "lefordított" }; case "it": // Italian return { context_googletranslateoption: "Cerca traduzione", context_messagetranslateoption: "Traduci messaggio", context_messageuntranslateoption: "Annulla traduzione messaggio", popout_translateoption: "Tradurre", popout_untranslateoption: "Non tradurre", toast_translating: "Tradurre", toast_translating_failed: "Impossibile tradurre", toast_translating_tryanother: "Prova un altro traduttore", translated_watermark: "tradotto" }; case "ja": // Japanese return { context_googletranslateoption: "翻訳を検索", context_messagetranslateoption: "メッセージの翻訳", context_messageuntranslateoption: "メッセージの翻訳解除", popout_translateoption: "翻訳する", popout_untranslateoption: "翻訳しない", toast_translating: "翻訳", toast_translating_failed: "翻訳に失敗しました", toast_translating_tryanother: "別の翻訳者を試す", translated_watermark: "翻訳済み" }; case "ko": // Korean return { context_googletranslateoption: "번역 검색", context_messagetranslateoption: "메시지 번역", context_messageuntranslateoption: "메시지 번역 취소", popout_translateoption: "옮기다", popout_untranslateoption: "번역 취소", toast_translating: "번역 중", toast_translating_failed: "번역하지 못했습니다.", toast_translating_tryanother: "다른 번역기 시도", translated_watermark: "번역" }; case "lt": // Lithuanian return { context_googletranslateoption: "Paieškos vertimas", context_messagetranslateoption: "Versti pranešimą", context_messageuntranslateoption: "Išversti pranešimą", popout_translateoption: "Išversti", popout_untranslateoption: "Neišversti", toast_translating: "Vertimas", toast_translating_failed: "Nepavyko išversti", toast_translating_tryanother: "Išbandykite kitą vertėją", translated_watermark: "išverstas" }; case "nl": // Dutch return { context_googletranslateoption: "Zoek vertaling", context_messagetranslateoption: "Bericht vertalen", context_messageuntranslateoption: "Bericht onvertalen", popout_translateoption: "Vertalen", popout_untranslateoption: "Onvertalen", toast_translating: "Vertalen", toast_translating_failed: "Kan niet vertalen", toast_translating_tryanother: "Probeer een andere vertaler", translated_watermark: "vertaald" }; case "no": // Norwegian return { context_googletranslateoption: "Søk i oversettelse", context_messagetranslateoption: "Oversett melding", context_messageuntranslateoption: "Ikke oversett melding", popout_translateoption: "Oversette", popout_untranslateoption: "Ikke oversett", toast_translating: "Oversetter", toast_translating_failed: "Kunne ikke oversette", toast_translating_tryanother: "Prøv en annen oversetter", translated_watermark: "oversatt" }; case "pl": // Polish return { context_googletranslateoption: "Wyszukaj tłumaczenie", context_messagetranslateoption: "Przetłumacz wiadomość", context_messageuntranslateoption: "Nieprzetłumacz wiadomość", popout_translateoption: "Tłumaczyć", popout_untranslateoption: "Nie przetłumacz", toast_translating: "Tłumaczenie", toast_translating_failed: "Nie udało się przetłumaczyć", toast_translating_tryanother: "Wypróbuj innego tłumacza", translated_watermark: "przetłumaczony" }; case "pt-BR": // Portuguese (Brazil) return { context_googletranslateoption: "Tradução de pesquisa", context_messagetranslateoption: "Traduzir mensagem", context_messageuntranslateoption: "Mensagem não traduzida", popout_translateoption: "Traduzir", popout_untranslateoption: "Não traduzido", toast_translating: "Traduzindo", toast_translating_failed: "Falha ao traduzir", toast_translating_tryanother: "Tente outro tradutor", translated_watermark: "traduzido" }; case "ro": // Romanian return { context_googletranslateoption: "Căutare traducere", context_messagetranslateoption: "Traduceți mesajul", context_messageuntranslateoption: "Untraduceți mesajul", popout_translateoption: "Traduceți", popout_untranslateoption: "Netradus", toast_translating: "Traducere", toast_translating_failed: "Nu s-a putut traduce", toast_translating_tryanother: "Încercați un alt traducător", translated_watermark: "tradus" }; case "ru": // Russian return { context_googletranslateoption: "Искать перевод", context_messagetranslateoption: "Перевести сообщение", context_messageuntranslateoption: "Непереведенное сообщение", popout_translateoption: "Переведите", popout_untranslateoption: "Неперевести", toast_translating: "Идет перевод", toast_translating_failed: "Не удалось перевести", toast_translating_tryanother: "Попробуйте другой переводчик", translated_watermark: "переведено" }; case "sv": // Swedish return { context_googletranslateoption: "Sök översättning", context_messagetranslateoption: "Översätt meddelande", context_messageuntranslateoption: "Untranslate meddelande", popout_translateoption: "Översätt", popout_untranslateoption: "Untranslate", toast_translating: "Översätter", toast_translating_failed: "Det gick inte att översätta", toast_translating_tryanother: "Prova en annan översättare", translated_watermark: "översatt" }; case "th": // Thai return { context_googletranslateoption: "ค้นหาคำแปล", context_messagetranslateoption: "แปลข้อความ", context_messageuntranslateoption: "ยกเลิกการแปลข้อความ", popout_translateoption: "แปลภาษา", popout_untranslateoption: "ไม่แปล", toast_translating: "กำลังแปล", toast_translating_failed: "แปลไม่สำเร็จ", toast_translating_tryanother: "ลองใช้นักแปลคนอื่น", translated_watermark: "แปล" }; case "tr": // Turkish return { context_googletranslateoption: "Çeviri ara", context_messagetranslateoption: "Mesajı Çevir", context_messageuntranslateoption: "Çeviriyi Kaldır Mesajı", popout_translateoption: "Çevirmek", popout_untranslateoption: "Çevirmeyi kaldır", toast_translating: "Çeviri", toast_translating_failed: "Tercüme edilemedi", toast_translating_tryanother: "Başka bir Çevirmen deneyin", translated_watermark: "tercüme" }; case "uk": // Ukrainian return { context_googletranslateoption: "Пошук перекладу", context_messagetranslateoption: "Перекласти повідомлення", context_messageuntranslateoption: "Неперекладене повідомлення", popout_translateoption: "Перекласти", popout_untranslateoption: "Неперекласти", toast_translating: "Переклад", toast_translating_failed: "Не вдалося перекласти", toast_translating_tryanother: "Спробуйте іншого перекладача", translated_watermark: "переклав" }; case "vi": // Vietnamese return { context_googletranslateoption: "Tìm kiếm bản dịch", context_messagetranslateoption: "Dịch tin nhắn", context_messageuntranslateoption: "Thư chưa dịch", popout_translateoption: "Phiên dịch", popout_untranslateoption: "Chưa dịch", toast_translating: "Phiên dịch", toast_translating_failed: "Không dịch được", toast_translating_tryanother: "Thử một Trình dịch khác", translated_watermark: "đã dịch" }; case "zh-CN": // Chinese (China) return { context_googletranslateoption: "搜索翻译", context_messagetranslateoption: "翻译消息", context_messageuntranslateoption: "取消翻译消息", popout_translateoption: "翻译", popout_untranslateoption: "取消翻译", toast_translating: "正在翻译", toast_translating_failed: "翻译失败", toast_translating_tryanother: "尝试其他翻译器", translated_watermark: "已翻译" }; case "zh-TW": // Chinese (Taiwan) return { context_googletranslateoption: "搜索翻譯", context_messagetranslateoption: "翻譯訊息", context_messageuntranslateoption: "取消翻譯訊息", popout_translateoption: "翻譯", popout_untranslateoption: "取消翻譯", toast_translating: "正在翻譯", toast_translating_failed: "翻譯失敗", toast_translating_tryanother: "嘗試其他翻譯器", translated_watermark: "已翻譯" }; default: // English return { context_googletranslateoption: "Search Translation", context_messagetranslateoption: "Translate Message", context_messageuntranslateoption: "Untranslate Message", popout_translateoption: "Translate", popout_untranslateoption: "Untranslate", toast_translating: "Translating", toast_translating_failed: "Failed to translate", toast_translating_tryanother: "Try another Translator", translated_watermark: "translated" }; } } }; })(window.BDFDB_Global.PluginUtils.buildPlugin(config)); })();