🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

语言切换快捷键|适配大部分在线翻译网站

语言切换快捷键 Ctrl + Shift + S

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name         语言切换快捷键|适配大部分在线翻译网站
// @namespace    https://github.com/CandyTek
// @version      1.1
// @license      MIT
// @description  语言切换快捷键 Ctrl + Shift + S
// @author       CandyTek
// @match        *://translate.yandex.com/?*
// @match        *://zh.pons.com/*
// @match        *://fanyi.so.com/*
// @match        *://fanyi.xfyun.cn/console/trans/*
// @match        *://translate.volcengine.com/*
// @match        *://translation.imtranslator.net/*
// @match        *://www.iciba.com/translate*
// @match        *://fanyi.baidu.com/*
// @match        *://dictionary.cambridge.org/*
// @match        *://www.baidu.com/s?*
// @match        *://fanyi.youdao.com/*
// @match        *://www.deepl.com/*
// @match        *://translation2.paralink.com/*
// @match        *://cn.bing.com/search?*
// @match        *://cn.bing.com/translator?*
// @match        *://fanyi.sogou.com/text?*
// @match        *://www.amz123.com/tools-translate/sougou
// @match        *://fanyi.caiyunapp.com/*
// @match        *://niutrans.com/trans?*
// @match        *://translate.alibaba.com/*
// @match        *://dict.eudic.net/home/translation
// @match        *://www.fanyi1234.com/lang/*
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPjxwYXRoIGQ9Ik02Ljk5IDExTDMgMTVsMy45OSA0di0zSDE0di0ySDYuOTl2LTN6TTIxIDlsLTMuOTktNHYzSDEwdjJoNy4wMXYzTDIxIDl6Ii8+PC9zdmc+
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-end
// ==/UserScript==

// 网站规则列表
const myMatchWebsites = [
	{
		// https://translate.yandex.com/?source_lang=en&target_lang=zh
		match: ["*://translate.yandex.com/?*"],
		button:"[aria-label=\"Switch direction\"]",
	},
	{
		// https://zh.pons.com/%E7%BF%BB%E8%AF%91/
		match: ["*://zh.pons.com/*"],
		button:"[data-e2e=\"switch-language\"]",
	},
	{
		// https://fanyi.so.com/#
		match: ["*://fanyi.so.com/*"],
		button:".exchange",
	},
	{
		// https://fanyi.xfyun.cn/console/trans/text
		match: ["*://fanyi.xfyun.cn/console/trans/*"],
		button:".anticon-swap",
	},
	{
		// https://translate.volcengine.com/
		match: ["*://translate.volcengine.com/*"],
		button:".reverse-img",
	},
	{
		// https://translation.imtranslator.net/
		match: ["*://translation.imtranslator.net/*"],
		button:"[title=\"Change translation direction of a selected language pair\"]",
	},
	{
		// https://www.iciba.com/translate
		match: ["*://www.iciba.com/translate*"],
		button:".select_exchange__AfzH4",
	},
	{
		// fanyi.baidu.com
		match: ["*://fanyi.baidu.com/*"],
		button:"svg.mg3bUrpQ",
	},
	{
		// https://dictionary.cambridge.org/zhs/%E8%AF%8D%E5%85%B8/%E8%8B%B1%E8%AF%AD-%E6%B1%89%E8%AF%AD-%E7%B9%81%E4%BD%93/exchange
		match: ["*://dictionary.cambridge.org/*"],
		button:".i-exchange",
	},
	{
		// https://www.baidu.com/s?tn=68018901_3_dg&ie=UTF-8&wd=%E7%BF%BB%E8%AF%91%E7%BD%91%E7%AB%99
		match: ["*://www.baidu.com/s?*"],
		button:".op_translation_exchange_img",
	},
	{
		// https://fanyi.youdao.com/#/
		match: ["*://fanyi.youdao.com/*"],
		button:".ic_language_exchange",
	},
	{
		// https://fanyi.sogou.com/text?keyword=&transfrom=en&transto=zh-CHS&model=general&exchange=true
		match: ["*://fanyi.sogou.com/*"],
		button:".btn-switch",
	},
	{
		// https://www.deepl.com/zh/translator
		match: ["*://www.deepl.com/*"],
		button:"[data-testid=\"lmt_language_switch\"]",
	},
	{
		// https://translation2.paralink.com/
		match: ["*://translation2.paralink.com/*"],
		button:"#switch",
	},
	{
		// https://cn.bing.com/search?q=%E4%BD%A0%E5%A5%BD%20%E8%8B%B1%E8%AF%AD%E7%BF%BB%E8%AF%91
		match: ["*://cn.bing.com/search?*","*://cn.bing.com/translator?*"],
		button:"#tta_revIcon",
	},
	// {
	// 	// https://www.amz123.com/tools-translate/sougou
	// 	match: ["*://www.amz123.com/tools-translate/sougou"],
	// 	button:".btn-switch",
	// 	iframe:"#translate",
	// },
	{
		// https://fanyi.caiyunapp.com/
		match: ["*://fanyi.caiyunapp.com/*"],
		button:".changeImg",
	},
	{
		// https://niutrans.com/trans?type=text
		match: ["*://niutrans.com/trans?*"],
		button:".nt-icon-qiehuan",
	},
	{
		// https://translate.alibaba.com/
		match: ["*://translate.alibaba.com/*"],
		button:".anticon-swap",
	},
	{
		// https://dict.eudic.net/home/translation
		match: ["*://dict.eudic.net/home/translation"],
		button:".switchBtn",
	},
	{
		// https://www.fanyi1234.com/lang/
		match: ["*://www.fanyi1234.com/lang/*"],
		button:"[alt=\"转换\"]",
	},

];

/** 设置工具类 */
class CandyTekPreferenceUtil {
	/** 是否已向网页添加过设置面板了 */
	isAlreadyAddSettingPanel = false;
	/** 设置面板根元素 */
	rootShadow = null;
	/** 存放设置值的地方。获取 prefValues[key] */
	prefValues;
	/** 源 pref 配置数组 */
	preferenceList;

	constructor(preferenceList) {
		this.preferenceList = preferenceList;
		this.refreshPrefValues();
	}

	/** 刷新设置值 */
	refreshPrefValues() {
		this.prefValues = this.preferenceList.reduce((list, curr) => {
			list[curr.preference] = GM_getValue(curr.preference, curr.defaultValue);
			return list;
		}, {});
	}

	/** 获取设置值 */
	get(key) {
		return this.prefValues.hasOwnProperty(key) ? this.prefValues[key] : GM_getValue(key, "");
	}

	/** 写入设置值,未适配 boolean */
	set(key, value) {
		GM_setValue(key, value);
		this.prefValues[key] = value;
	}

	/** 显示设置面板在网页右上角 */
	show() {
		if (this.isAlreadyAddSettingPanel) {
			this.rootShadow.querySelector(".setting_panel").style.display = "block";
			return;
		}

		if (!document.body.createShadowRoot) {
			console.warn("可能不能创建 ShadowRoot");
			//return;
		}
		// 创建设置面板
		const host = document.createElement('div');
		host.id = "simplify_article_settings_panel";
		document.body.appendChild(host);

		const root = host.attachShadow({ mode: 'open' });
		this.rootShadow = root;
		this.isAlreadyAddSettingPanel = true;
		root.innerHTML = `
	<style>
		.preference_title {
			width: fit-content;
			height: 40px;
			font-size: 20px;
			margin: 0px;
			line-height: 40px;
			padding-left: 16px;
			font-weight: bold;
		}

		.preference_item {
			display: flex;
			padding: 12px 8px;
		}

		.preference_item_title {
			padding: 0px 0px 0px 10px;
			margin: 0px;
			font-size: 15px;
			line-height: 40px;
			letter-spacing: 2px;
			height: 40px;
			width: 140px;
		}

		.preference_item_edittext {
			font-size: 14px;
			margin-left: auto;
			line-height: 36px;
			height: 36px;
			padding: 0px;
			border: 2px solid #c4c7ce;
			border-radius: 6px;
			text-align: center;
			width: 138px;
		}
		.preference_item_textarea {
			text-align: unset;
			line-height: 20px;
		}

		.preference_item_edittext_color {
			width: 100px;
			border-radius: 6px 0px 0px 6px;
			border-right: 0;
		}

		.hoverbutton {
			background: none;
		}

		.hoverbutton:hover {
			background: #CCC;
			background-size: 80% 80%;
			border-radius: 4px;
		}

		.input_select_color {
			width: 40px;
			height: 40px;
			margin: 0px;
			padding:0px 2px 0px 4px;
			box-sizing: border-box;
			background-color:#ffffff;
			border-width: 2px;
			border-radius: 0px 6px 6px 0px;
			border-left: 0px;
			border-color: #c4c7ce;
		}

		.checkbox_input {
			width: 24px;
			height: 40px;
			margin: 0px 0px 0px auto;
		}


		.setting_panel {
			position: fixed;
			right: 20px;
			top: 20px;
			width: fit-content;
			height: fit-content;
			border-radius: 8px;
			background: #FFFFFF;
			padding: 8px;
			box-shadow: 0 10px 20px rgb(0 0 0 / 15%);
			z-index:9999;
		}

		.container {
			background: #F0F0F0;
			border-radius: 8px;
			margin-top: 0px;
			padding-top: 8px;
			padding-right: 8px;
		}
	</style>

	<div class="setting_panel">
		<div class="preference_item" style="padding-top: 0px;">
			<button id="close" title="关闭并保存" class="hoverbutton" type="submit"
				style="width: 40px;height: 40px;display: flex;align-items: center; justify-content: center; border: unset;">
				<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#5f6368"
					viewBox="0 -960 960 960">
					<path
						d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
				</svg>
			</button>
			<p class="preference_title">设置</p>
		</div>
		<div class="container" id="container">

		</div>
	</div>
	`;

		const container = root.querySelector("#container");
		// 动态创建设置项
		for (const index in this.preferenceList) {
			const item = this.preferenceList[index];
			const itemDiv = document.createElement("div");
			itemDiv.className = "preference_item";

			const itemTitle = document.createElement("p");
			itemTitle.className = "preference_item_title";
			itemTitle.innerText = item.text;
			itemDiv.appendChild(itemTitle);

			if (item.type == "number") {
				const input = document.createElement("input");
				input.type = "number";
				input.className = "preference_item_edittext";
				input.id = item.preference;
				input.value = GM_getValue(item.preference, item.defaultValue);
				itemDiv.appendChild(input);
			} else if (item.type == "color") {
				const inputText = document.createElement("input");
				inputText.type = "text";
				inputText.className = "preference_item_edittext preference_item_edittext_color";
				inputText.id = item.preference;
				inputText.value = GM_getValue(item.preference, item.defaultValue);
				inputText.maxLength = 50;
				itemDiv.appendChild(inputText);

				const inputColor = document.createElement("input");
				inputColor.type = "color";
				inputColor.className = "input_select_color";
				if (this.isValidHexColor(inputText.value)) {
					inputColor.value = inputText.value;
				}
				itemDiv.appendChild(inputColor);

				inputText.addEventListener('input', () => this.inputTextAndChangeDisplayColor(inputText, inputColor));
				inputColor.addEventListener('input', () => this.selectColorAndChangeText(inputText, inputColor));
			} else if (item.type == "checkbox") {
				const input = document.createElement("input");
				input.type = "checkbox";
				input.id = item.preference;
				const checkValue = GM_getValue(item.preference, item.defaultValue);
				input.checked = checkValue;
				input.className = "checkbox_input";
				itemDiv.appendChild(input);
			} else if (item.type == "textarea") {
				const input = document.createElement("textarea");
				input.id = item.preference;
				input.value = GM_getValue(item.preference, item.defaultValue);
				input.className = "preference_item_edittext preference_item_textarea";
				itemDiv.appendChild(input);
			}
			container.appendChild(itemDiv);
		}

		root.querySelector("#close").onclick = () => {
			root.querySelector(".setting_panel").style.display = "none";
			// 动态创建设置项
			for (const index in this.preferenceList) {
				const item = this.preferenceList[index];

				if (item.type == "color" || item.type == "textarea") {
					try {
						GM_setValue(item.preference, root.querySelector(`#${item.preference}`).value);
					} catch (error) {
						console.error(`保存配置失败:${item.preference}`);
					}
				} else if (item.type == "number") {
					try {
						GM_setValue(item.preference, parseFloat(root.querySelector(`#${item.preference}`).value));
					} catch (error) {
						console.error(`保存配置失败:${item.preference}`);
					}
				} else if (item.type == "checkbox") {
					try {
						GM_setValue(item.preference, root.querySelector(`#${item.preference}`).checked);
					} catch (error) {
						console.error(`保存配置失败:${item.preference}`);
					}
				}
			}
			this.refreshPrefValues();
		};
	}

	/** input 颜色选择器更改颜色时,同时更改文本框 */
	selectColorAndChangeText(inputText, inputColor) {
		inputText.value = inputColor.value;
	};
	/** 文本框更改值时,同时更改颜色显示 */
	inputTextAndChangeDisplayColor(inputText, inputColor) {
		const color = inputText.value;
		if (this.isValidHexColor(color)) {
			inputColor.value = color;
		}
	};

	/** 用于校验 6 位的十六进制颜色值 */
	isValidHexColor(hex) {
		try {
			const hexPattern = /^#?([a-fA-F0-9]{6})$/;
			return hexPattern.test(hex);
		} catch (error) {
			return false;
		}
	}

}

(() => {
	let p;
	// 开始匹配网站
	for (const website of myMatchWebsites) {
		let hit = false;
		hit = Array.isArray(website.match) ? website.match.some((s) => matchRule(window.location.href, s)) : matchRule(window.location.href, website.match);

		if (hit) {
			//p = new CandyTekPreferenceUtil(myPreferenceList);
			// 添加设置菜单
			// GM_registerMenuCommand("快捷键设置", () => {
			// p.show();
			// });
			// 添加语音调转快捷键
			document.addEventListener("keydown", function(event) {
				if (event.ctrlKey && event.shiftKey && !event.altKey && event.key === "S") {
					let el = document.querySelector(website.button);
					if(website.iframe){
						const iframe = document.querySelector(website.iframe);
						const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
						el=iframeDocument.querySelector(website.button);
					}
					customClick(el);
					if(website.secondButton){
						let el2 = document.querySelector(website.secondButton);
						customClick(el2);
					}
				}
			});

			console.info(`匹配成功 ${website.match}`);
			break;
		}
	};

	function customClick(el){
		if(el){
			try{
				el.click();
			}catch{
				el.dispatchEvent(new MouseEvent('click', {bubbles: true,cancelable: true,}));
			}
		}
	}

	/** match匹配方法 */
	function matchRule(str, rule) {
		const escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
		return new RegExp(`^${rule.split("*").map(escapeRegex).join(".*")}$`).test(str);
	}


	// todo:https://www.reverso.net/text-translation
	// todo:google
	// node:http://www.sowang.com/sogou/fanyi.htm
	// todo:https://www.amz123.com/tools-translate/tenxunjiaohu
	// todo:https://fanyi.pdf365.cn/free
	// todo:https://www.fanyi1234.com/
	// todo:https://dict.cnki.net/
	// todo:https://www.ichacha.net/
	// todo:https://www.tangpafanyi.com/text.html
	// todo:https://tran.httpcn.com/FanyiWeb/
	// todo:https://fanyi.zou.la/
	// todo:https://fanyi.dict.cn/
	// todo:https://www.99yee.cn/
	// todo:https://transmart.qq.com/zh-CN/index
	// todo:https://fanyi.atman360.com/index
	// todo:https://fanyi.qq.com/
	// todo:https://www.medsci.cn/sci/translation.do
	// todo:https://www.worldlingo.com/en/products/text_translator.html

})();