Last active
May 24, 2026 04:13
-
-
Save Backsoon0/6d9ea3def62b61e5796f10efde6528d1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name OpenCC 繁简转换器 | |
| // @name:en OpenCC Chinese Converter | |
| // @namespace https://github.com/Backsoon0 | |
| // @version 1.1 | |
| // @description 多向繁简转换工具:支持繁→简 / 简→繁 / 短语转换,可通过菜单随时切换,偏好自动保存。基于 opencc-js 1.3.1。 | |
| // @description:en Multi-directional Chinese converter: Traditional↔Simplified with phrase support. Switch modes via menu, preferences auto-saved. Powered by opencc-js 1.3.1. | |
| // @author Backsoon0 | |
| // @license MIT | |
| // @match *://*/* | |
| // @grant GM_registerMenuCommand | |
| // @grant GM_getValue | |
| // @grant GM_setValue | |
| // @grant GM_addStyle | |
| // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/full.js | |
| // @downloadURL https://gist.github.com/Backsoon0/6d9ea3def62b61e5796f10efde6528d1/raw/OpenCC%20%E7%B9%81%E7%AE%80%E8%BD%AC%E6%8D%A2%E5%99%A8.user.js | |
| // @updateURL https://gist.github.com/Backsoon0/6d9ea3def62b61e5796f10efde6528d1/raw/OpenCC%20%E7%B9%81%E7%AE%80%E8%BD%AC%E6%8D%A2%E5%99%A8.user.js | |
| // @run-at document-idle | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| GM_addStyle('#opencc-overlay{all:initial;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.4);z-index:2147483647;display:flex;align-items:center;justify-content:center;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}#opencc-dialog{background:#fff;border-radius:12px;padding:24px;min-width:280px;box-shadow:0 8px 32px rgba(0,0,0,0.2);color:#333}.opencc-title{font-size:18px;font-weight:600;margin-bottom:16px;text-align:center;user-select:none}.opencc-option{padding:10px 16px;margin:4px 0;border-radius:8px;cursor:pointer;font-size:15px;color:#555;user-select:none;transition:background 0.15s}.opencc-option:hover{background:#f0f4ff}.opencc-option.opencc-active{background:#e8f0fe;color:#1a73e8;font-weight:600}.opencc-close{display:block;margin:16px auto 0;padding:8px 32px;border:1px solid #ddd;border-radius:6px;background:#f8f8f8;cursor:pointer;font-size:14px;color:#666}.opencc-close:hover{background:#eee}'); | |
| // 检查 OpenCC 是否成功加载 | |
| if (typeof OpenCC === 'undefined') { | |
| console.error('OpenCC-js 加载失败。'); | |
| return; | |
| } | |
| // 支持的语言对 | |
| const langOptions = [ | |
| { label: '繁 (台湾)→简', from: 'tw', to: 'cn' }, | |
| { label: '繁 (香港)→简', from: 'hk', to: 'cn' }, | |
| { label: '简→繁 (台湾)', from: 'cn', to: 'tw' }, | |
| { label: '简→繁 (香港)', from: 'cn', to: 'hk' }, | |
| { label: '繁 (台湾+短语)→简', from: 'twp', to: 'cn' }, | |
| ]; | |
| let currentFrom, currentTo, converter; | |
| function initConverter(from, to) { | |
| currentFrom = from; | |
| currentTo = to; | |
| converter = OpenCC.Converter({ from, to }); | |
| } | |
| initConverter(GM_getValue('opencc_from', 'tw'), GM_getValue('opencc_to', 'cn')); | |
| // 定义需要忽略的 HTML 标签,防止破坏网页原有功能或修改代码 | |
| const ignoreTags = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'CODE', 'PRE']); | |
| // 检查节点的父元素和祖先链是否需要跳过转换 | |
| function isNodeSkippable(node) { | |
| let el = node.nodeType === Node.TEXT_NODE ? node.parentNode : node; | |
| while (el && el.nodeType === Node.ELEMENT_NODE) { | |
| if (ignoreTags.has(el.tagName.toUpperCase())) return true; | |
| if (el.isContentEditable) return true; | |
| if (el.classList && el.classList.contains('ignore-opencc')) return true; | |
| if (el.hasAttribute('data-opencc-converted')) return true; | |
| el = el.parentNode; | |
| } | |
| return false; | |
| } | |
| // 核心转换函数:遍历 DOM 树 | |
| function convertNode(node) { | |
| if (node.nodeType === Node.TEXT_NODE) { | |
| if (isNodeSkippable(node)) return; | |
| const text = node.nodeValue; | |
| // 简单正则判断:如果包含非 ASCII 字符再进行转换,避免无意义的性能损耗 | |
| if (/[^\x00-\xff]/.test(text)) { | |
| node.nodeValue = converter(text); | |
| if (node.parentNode) node.parentNode.setAttribute('data-opencc-converted', ''); | |
| } | |
| } else if (node.nodeType === Node.ELEMENT_NODE) { | |
| if (ignoreTags.has(node.tagName.toUpperCase())) { | |
| return; // 跳过输入框、代码块等特定标签 | |
| } | |
| // 跳过 contenteditable 元素(富文本编辑器),避免干扰用户输入 | |
| if (node.isContentEditable) { | |
| return; | |
| } | |
| // 支持 opencc-js 的 ignore-opencc CSS 类,允许网页主动标记不转换的区域 | |
| if (node.classList && node.classList.contains('ignore-opencc')) { | |
| return; | |
| } | |
| // 转换 HTML 属性中的占位符和悬浮提示 | |
| if (node.hasAttribute('placeholder')) { | |
| node.setAttribute('placeholder', converter(node.getAttribute('placeholder'))); | |
| } | |
| if (node.hasAttribute('title')) { | |
| node.setAttribute('title', converter(node.getAttribute('title'))); | |
| } | |
| // 递归遍历所有子节点 | |
| node.childNodes.forEach(convertNode); | |
| } | |
| } | |
| // 1. 网页首次加载时,转换现有的内容和网页标题 | |
| convertNode(document.body); | |
| if (document.title) { | |
| document.title = converter(document.title); | |
| } | |
| // 3. 获取当前模式标签 | |
| function currentLabel() { | |
| const opt = langOptions.find(o => o.from === currentFrom && o.to === currentTo); | |
| return opt ? opt.label : currentFrom + '→' + currentTo; | |
| } | |
| // 4. 切换转换模式后全页重新转换 | |
| function applyLanguage(from, to) { | |
| GM_setValue('opencc_from', from); | |
| GM_setValue('opencc_to', to); | |
| initConverter(from, to); | |
| observer.disconnect(); | |
| convertNode(document.body); | |
| if (document.title) document.title = converter(document.title); | |
| startObserving(); | |
| } | |
| // 5. 创建转换模式选择弹窗 | |
| function showSettingsDialog() { | |
| const existing = document.getElementById('opencc-overlay'); | |
| if (existing) existing.remove(); | |
| const overlay = document.createElement('div'); | |
| overlay.id = 'opencc-overlay'; | |
| overlay.addEventListener('click', function(e) { | |
| if (e.target === overlay) overlay.remove(); | |
| }); | |
| const dialog = document.createElement('div'); | |
| dialog.id = 'opencc-dialog'; | |
| dialog.innerHTML = '<div class="opencc-title">选择转换模式</div>'; | |
| langOptions.forEach(function(opt) { | |
| const isActive = currentFrom === opt.from && currentTo === opt.to; | |
| const item = document.createElement('div'); | |
| item.className = 'opencc-option' + (isActive ? ' opencc-active' : ''); | |
| item.textContent = (isActive ? '● ' : ' ') + opt.label; | |
| item.addEventListener('click', function() { | |
| applyLanguage(opt.from, opt.to); | |
| const opts = dialog.querySelectorAll('.opencc-option'); | |
| langOptions.forEach(function(o, i) { | |
| const act = currentFrom === o.from && currentTo === o.to; | |
| opts[i].className = 'opencc-option' + (act ? ' opencc-active' : ''); | |
| opts[i].textContent = (act ? '● ' : ' ') + o.label; | |
| }); | |
| }); | |
| dialog.appendChild(item); | |
| }); | |
| const closeBtn = document.createElement('button'); | |
| closeBtn.className = 'opencc-close'; | |
| closeBtn.textContent = '关闭'; | |
| closeBtn.addEventListener('click', function() { overlay.remove(); }); | |
| dialog.appendChild(closeBtn); | |
| overlay.appendChild(dialog); | |
| document.body.appendChild(overlay); | |
| } | |
| // 6. 注册原生菜单 | |
| GM_registerMenuCommand('切换转换模式...', showSettingsDialog); | |
| // 4. 使用 MutationObserver 监听动态加载的新内容 | |
| const observer = new MutationObserver((mutations) => { | |
| // 暂时断开监听,防止我们在修改 DOM 时触发死循环 | |
| observer.disconnect(); | |
| mutations.forEach((mutation) => { | |
| if (mutation.type === 'childList') { | |
| mutation.addedNodes.forEach((node) => { | |
| convertNode(node); | |
| }); | |
| } else if (mutation.type === 'characterData') { | |
| if (isNodeSkippable(mutation.target)) return; | |
| const text = mutation.target.nodeValue; | |
| if (/[^\x00-\xff]/.test(text)) { | |
| mutation.target.nodeValue = converter(text); | |
| if (mutation.target.parentNode) mutation.target.parentNode.setAttribute('data-opencc-converted', ''); | |
| } | |
| } | |
| }); | |
| // 转换完毕,恢复监听 | |
| startObserving(); | |
| }); | |
| function startObserving() { | |
| observer.observe(document.body, { | |
| childList: true, // 监听子节点的变动 | |
| subtree: true, // 监听所有后代节点 | |
| characterData: true // 监听文本节点内容的变动 | |
| }); | |
| } | |
| startObserving(); | |
| })(); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
OpenCC 繁简转换器
基于 网页繁体转简体 (OpenCC)(作者 mayunqing1230)的二改增强版。
与原版的对比
继承的原版特性
<script>、<style>、<input>、<textarea>、<code>、<pre>标签增强特性
ignore-openccCSS 类标记跳过区域支持的转换模式
使用方式
许可证
MIT,与原始项目保持一致。