Greasy Fork

来自缓存

Greasy Fork is available in English.

翻译机增强版 - AI翻译支持

基于原版翻译机的增强版本,新增AI翻译功能(支持LM Studio/OpenAI等),可视化配置界面。支持翻译Twitter/X、YouTube、Facebook、Reddit等主流社交网站。

这些是此脚本变更过代码的版本。 显示所有版本。

  • v1.0.1 2026-01-06
  • v1.0.0 2026-01-05
  • v0.04 2026-01-05
  • v0.0.3 2026-01-05

    # 更新日志

    ## v0.0.3 (2026-01-05)

    ### 🐛 修复Facebook展开内容闪烁和翻译问题

    #### 问题1: 展开内容后页面闪烁不停
    - **问题描述**: 点击Facebook帖子"展开"按钮后,翻译内容不断闪烁出现和消失,形成无限循环
    - **原因分析**:
    1. 在非替换模式(append模式)下,`baseTextSetter` 没有设置 `spanNode.title`,导致原始文本无法保存
    2. `baseSelector` 检测到 `title` 为空时认为是需要清理的旧翻译
    3. 清理后触发重新翻译,但新翻译仍然不设置 `title`
    4. 形成"检测→清理→翻译→检测"的无限循环
    - **修复方案**:
    1. 在 `baseTextSetter` 的非替换模式分支添加 `spanNode.title = rawText`,确保原始文本被保存
    2. 优化 `baseSelector` 的清理逻辑,只有当有保存的原始文本且文本长度变化超过10%时才触发清理
    - **修复文件**:
    - `baseTextSetter`: 第902行,添加title设置
    - `baseSelector`: 第820-825行,优化清理判断条件
    - **测试验证**: ✅ 通过MCP浏览器15秒稳定性测试,翻译保持稳定,无闪烁

    #### 问题2: "展开"按钮被错误翻译
    - **问题描述**: Facebook帖子中的"展开"按钮文本被包含在翻译内容中
    - **原因分析**: 虽然 `customFilter` 已经过滤了展开按钮不被翻译,但由于DOM结构原因,按钮可能在翻译后才插入或位置发生变化
    - **验证结果**: ✅ 经MCP测试确认展开按钮未被翻译(`expandInText: false`)

    ### 🔧 技术细节

    **修复的核心代码:**

    **1. baseTextSetter (第895-906行) - 添加title保存**
    ```javascript
    } else {
    const spanNode = document.createElement('span');
    spanNode.style.whiteSpace = "pre-wrap";
    spanNode.innerText = `\n\n${currentShowInfo ? "-----------" + translatorName + "-----------\n\n" : ""}` + text;
    spanNode.dataset.translate = "processed";
    spanNode.title = rawText; // 🔥 新增:保存原始文本,用于检测内容变化
    spanNode.class = "translate-processed-node"
    element.appendChild(spanNode);
    element.style.cssText += "-webkit-line-clamp: unset;max-height: unset";
    return spanNode;
    }
    ```

    **2. baseSelector (第818-828行) - 优化清理逻辑**
    ```javascript
    // 判断内容是否变化:
    // 只有当有保存的原始文本,且文本确实发生显著变化时才清理
    // 避免因title为空导致的无限循环
    const shouldClean = savedRawText &&
    currentRawText !== savedRawText &&
    currentRawText.length > 0 &&
    // 文本长度变化超过10%才认为是真正的变化(如展开)
    Math.abs(currentRawText.length - savedRawText.length) > savedRawText.length * 0.1;

    if (shouldClean) {
    ```

    ### 🛠️ MCP调试验证

    本次修复使用 **Playwright MCP** 进行了全面的实时测试:

    **测试1: 闪烁问题检测(修复前)**
    ```javascript
    // 10秒监控显示翻译状态不断变化:
    // hasTranslation: true → false → false → true → false ...
    // textLength在216和353之间反复跳动
    ```

    **测试2: 稳定性验证(修复后)**
    ```javascript
    // 15秒连续监控,所有指标稳定:
    {
    hasTranslation: true, // ✅ 翻译始终存在
    hasTitle: true, // ✅ title正确保存
    titleLength: 216, // ✅ 原始文本长度稳定
    textLength: 329, // ✅ 总长度稳定(原文+翻译)
    expandInText: false // ✅ 展开按钮未包含在文本中
    }
    ```

    **测试3: 多帖子验证**
    - ✅ Kojima帖子:完整展开翻译,无闪烁
    - ✅ UnderGuild帖子:正常翻译
    - ✅ Taipei Game Show帖子:保留展开按钮,翻译正常
    - ✅ Hollow Knight帖子:保留展开按钮,翻译正常

    ### ✅ 验证完成

    - [x] 展开后内容完整翻译
    - [x] 无页面闪烁现象
    - [x] 翻译内容稳定(15秒测试)
    - [x] title属性正确保存原始文本
    - [x] 展开按钮不被翻译
    - [x] 多个帖子类型测试通过

    ### 📊 性能影响

    - 添加 `title` 属性:可忽略的内存开销
    - 优化清理条件(10%阈值):减少不必要的重新翻译
    - 整体性能:✅ 无负面影响,反而减少了无效循环

  • v0.0.2 2026-01-05
  • v1.0.0 2026-01-05

    # 更新日志

    ## v0.0.2 (2026-01-05)

    ### 🐛 Facebook翻译功能修复,优化显示效果

    #### 问题1: Facebook首页无法翻译
    - **问题描述**: Facebook首页 `https://www.facebook.com/` 无法触发翻译规则
    - **原因分析**: 正则表达式 `/https:\/\/www.facebook.com\/.+/` 要求路径部分至少有一个字符,导致首页(路径为空)无法匹配
    - **修复方案**: 将正则修改为 `/https:\/\/www.facebook.com(\/.*)?/`,使路径部分变为可选
    - **修复文件**: 第500行左右
    - **测试验证**: ✅ 通过MCP浏览器测试确认首页可正常翻译

    #### 问题2: 展开后的内容不会重新翻译
    - **问题描述**: 点击Facebook帖子的"查看更多"/"展开"按钮后,新展开的内容不会自动翻译
    - **原因分析**: 元素被标记为 `data-translate="processed"` 后,即使内容改变也不会重新检测
    - **修复方案**: 在 `baseSelector` 函数中添加内容变化检测逻辑
    - 比对当前 `innerText` 与翻译span的 `title` 属性(保存的原始文本)
    - 如果不同且不包含"AI翻译"标识,说明内容已改变
    - 清除旧文本的缓存(sessionStorage)
    - 移除 `data-translate` 标记,允许重新翻译
    - **修复文件**: 第835-875行
    - **测试状态**: ⏳ 待用户验证

    #### 问题3: 重复翻译导致排版混乱
    - **问题描述**: 同一段文本出现多次翻译,页面排版难看
    - **原因分析**: Facebook使用嵌套div结构,selector同时匹配了父容器和子元素
    ```html

    ← 父容器被翻译
    Happy New Year!
    ← 子元素也被翻译
    2026 is the year...
    ← 子元素也被翻译

    ```
    - **修复方案**: 在customFilter中添加父容器排除逻辑
    - 检查当前元素是否包含其他待翻译元素
    - 如果是父容器,则跳过翻译(只翻译叶子节点)
    - 同时排除纯容器元素(无直接文本内容)
    - **修复文件**: 第508-545行
    - **测试状态**: ⏳ 待用户验证

    #### 问题4: "展开"按钮被翻译
    - **问题描述**: Facebook帖子中的"展开"按钮本身被翻译成其他语言
    - **原因分析**: "展开"按钮 `

    展开

    ` 匹配了 `div[dir=auto]` 选择器
    - **修复方案**: 在customFilter中添加两层过滤
    1. 排除 `role="button"` 的元素
    2. 排除只包含"展开"文本的元素
    - **修复文件**: 第508-545行
    - **MCP调试**: 通过Playwright精确定位按钮结构
    - **测试状态**: ⏳ 待用户验证

    #### 问题5: 翻译结果中显示"展开"文字
    - **问题描述**: 虽然按钮本身不翻译了,但翻译文本中仍包含"… 展开"
    - **原因分析**: `baseTextGetter` 使用 `innerText` 时会获取到按钮的文本内容
    - **第一版方案**: ❌ 在 `baseTextSetter` 中用正则过滤 `/[…\.]{1,3}\s*展开/g`
    - **问题**: 如果原文本身包含"展开"一词会被误删(如"这个功能可以展开视图")
    - **最终方案**: ✅ 在 `baseTextGetter` 中排除 `role="button"` 元素
    - 克隆节点
    - 删除所有 `[role="button"]` 子元素
    - 再获取 `innerText`
    - 这样按钮文本根本不会进入翻译流程
    - **修复文件**: 第876-881行
    - **优势**: 不会误删正常内容中的"展开"一词

    ### 🔧 技术细节

    #### 修改的核心函数

    **1. baseSelector (第835-875行)**
    ```javascript
    // 添加内容变化检测
    if (item.dataset.translate === "processed") {
    const translationNode = item.querySelector('span[data-translate="processed"]');
    if (translationNode) {
    const currentText = item.innerText;
    const oldText = translationNode.title || '';
    if (oldText && currentText !== oldText && !currentText.includes('AI翻译')) {
    // 清除缓存并允许重新翻译
    sessionStorage.removeItem(choice + '-' + oldText);
    delete item.dataset.translate;
    return true;
    }
    }
    return false;
    }
    ```

    **2. Facebook规则customFilter (第508-545行)**
    ```javascript
    return items.filter(item => {
    // 过滤1: 排除展开按钮
    if (item.getAttribute('role') === 'button') return false;
    if (item.innerText.trim() === '展开') return false;

    // 过滤2: 必须有文本内容
    const hasText = item.innerText && item.innerText.trim().length > 0;
    if (!hasText) return false;

    // 过滤3: 排除父容器(只翻译叶子节点)
    const hasChildInItems = items.some(otherItem =>
    otherItem !== item && item.contains(otherItem)
    );
    if (hasChildInItems) return false;

    // 过滤4: 排除纯容器元素
    const directText = Array.from(item.childNodes)
    .filter(node => node.nodeType === Node.TEXT_NODE)
    .map(node => node.textContent.trim())
    .filter(t => t)
    .join('');

    // 过滤5: 必须有直接文本或有意义的内容
    return directText.length > 0 || item.querySelector('img[alt], a, br');
    });
    ```

    **3. baseTextGetter (第876-881行)**
    ```javascript
    function baseTextGetter(e) {
    // 获取文本时,排除掉role=button的子元素(如展开按钮)
    const clone = e.cloneNode(true);
    const buttons = clone.querySelectorAll('[role="button"]');
    buttons.forEach(btn => btn.remove());
    return clone.innerText;
    }
    ```

    ### 🛠️ 调试工具使用

    本次修复大量使用了 **Playwright MCP** 进行实时调试:
    - `mcp_microsoft_pla_browser_navigate` - 导航到Facebook页面
    - `mcp_microsoft_pla_browser_snapshot` - 获取页面可访问性快照
    - `mcp_microsoft_pla_browser_run_code` - 执行自定义代码检查DOM结构
    - 实时查看元素属性、文本内容、嵌套关系
    - 精确定位"展开"按钮的HTML结构

    ### ✅ 测试清单

    - [x] Facebook首页能够正常翻译
    - [x] 通过MCP验证翻译span存在
    - [x] 代码审查确认所有修复逻辑正确
    - [ ] 用户测试:展开后内容重新翻译
    - [ ] 用户测试:无重复翻译
    - [ ] 用户测试:展开按钮不被翻译
    - [ ] 用户测试:正常内容中的"展开"一词不受影响

    ### 📋 后续改进建议

    1. **性能优化**
    - 多层过滤可能影响性能,考虑优化算法
    - `items.some()` 循环复杂度为O(n²),可考虑memoization

    2. **功能增强**
    - 支持折叠检测(展开的逆操作)
    - 添加调试模式,便于用户排查问题
    - 考虑使用MutationObserver替代轮询

    3. **通用性提升**
    - 将展开按钮排除逻辑扩展到其他平台(Twitter、Instagram等)
    - 创建可复用的过滤函数

    ---

    ## v1.0.0 (2026-01-05)

    ### ✨ 新增功能
    - ✨ 新增AI翻译引擎支持
    - ✨ 新增LM Studio本地AI集成
    - ✨ 新增OpenAI API支持
    - ✨ 新增可视化配置界面
    - ✨ 新增API密钥密码框保护
    - ✨ 新增配置实时保存功能
    - ✨ 新增右键菜单快速配置

    ### 🔧 优化改进
    - 🔧 优化控制面板布局
    - 🔧 添加配置提示和示例
    - 📝 完善使用文档

    ### 🎯 支持的网站
    - Twitter/X
    - YouTube
    - Facebook
    - Reddit
    - Discord
    - Telegram
    - Instagram
    - TikTok
    - GitHub
    - Bluesky
    - Threads
    - Quora
    - 5ch

    ### 🔄 支持的翻译引擎
    **传统引擎**: 谷歌翻译、百度翻译、腾讯翻译、有道翻译、必应翻译、彩云小译、Papago翻译、阿里翻译、爱词霸翻译、Deepl翻译

    **AI引擎**: LM Studio、OpenAI及兼容API

    ---

    **维护者**: huasuiyue
    **原作者**: HolynnChen
    **许可证**: MIT

  • v0.0.1 2026-01-05
  • v1.0.1 2026-01-05
  • v1.0.0 2026-01-05