Greasy Fork

Greasy Fork is available in English.

[MWI]Auto Message Translator

Automatically translate English messages to your language in Milky Way Idle game

当前为 2025-05-12 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         [MWI]Auto Message Translator
// @name:zh-CN   [银河奶牛]自动消息翻译器(zh-CN)
// @name:ja      [MWI]自動メッセージ翻訳ツール
// @name:ko      [MWI]자동 메시지 번역기
// @namespace    https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-message-translator-zh_cn
// @version      1.0.1
// @description  Automatically translate English messages to your language in Milky Way Idle game
// @description:zh-CN  在银河奶牛游戏中自动将英文消息翻译为中文
// @description:ja  Milky Way Idleゲームで英語のメッセージを日本語に自動翻訳します
// @description:ko  Milky Way Idle 게임에서 영어 메시지를 한국어로 자동 번역합니다
// @author       shenhuanjie
// @license      MIT
// @match        https://www.milkywayidle.com/game*
// @match        https://milkywayidle.com/game*
// @icon         https://www.milkywayidle.com/favicon.svg
// @homepage     https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-message-translator-zh_cn
// @supportURL   https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-message-translator-zh_cn
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @connect      translate.googleapis.com
// @connect      translate.google.com
// @run-at       document-start
// @noframes
//
// @history      1.0.1 - Added support for more languages, improved translation reliability
// @history      1.0.0 - Initial release
// ==/UserScript==

(function() {
    'use strict';

    // ========== 全局配置 ==========
    const CONFIG = {
        enableConsoleLog: false,      // 控制台日志开关
        sourceLanguage: 'en',        // 源语言
        targetLanguage: 'zh-CN',     // 目标语言
        logLevel: 'INFO',            // 日志级别: DEBUG, INFO, WARNING, ERROR
        initialScanDelay: 1000,      // 初始扫描延迟(ms)
        rescanInterval: 5000,        // 重新扫描间隔(ms)
        maxTranslationDepth: 5,      // 最大翻译深度
        translationCacheSize: 100,   // 翻译缓存大小
        messageFormat: /【(.+?)】\s*[::]\s*(.+)/,  // 消息格式正则表达式

        // 类名前缀配置(用于模糊匹配)
        classNamePrefixes: {
            chatMessage: 'ChatMessage_chatMessage__',
            timestamp: 'ChatMessage_timestamp__',
            name: 'ChatMessage_name__',
            chatHistory: 'ChatHistory_chatHistory__',
            systemMessage: 'ChatMessage_systemMessage__'
        },

        // 翻译请求间隔(ms),避免请求过于频繁被封IP
        translationRequestDelay: 300,

        // 翻译选项
        translateSystemMessages: false, // 是否翻译系统消息
        skipEmojiMessages: true,       // 是否跳过包含表情符号的消息
        minTextLength: 2,               // 最小翻译文本长度
        skipPatterns: [                 // 跳过匹配这些模式的消息
            /^[::]$/,                  // 冒号
            /^[@#]\w+$/,                // @用户名或#标签
            /^[^\w\s]{2,}$/,            // 纯特殊字符
            /^[\uD800-\uDBFF][\uDC00-\uDFFF]$/ // 表情符号
        ]
    };
    // =============================

    // 工具函数:根据类名前缀生成选择器
    function getClassSelector(prefix) {
        return `[class^="${prefix}"], [class*=" ${prefix}"]`;
    }

    // 翻译缓存,避免重复翻译相同内容
    const translationCache = new Map();

    // 上次翻译请求的时间戳
    let lastTranslationTime = 0;

    // 延迟函数,返回一个Promise,在指定的毫秒数后解析
    function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    // 工具函数:日志记录
    function log(message, level = 'INFO') {
        if (!CONFIG.enableConsoleLog) return;

        if (!['DEBUG', 'INFO', 'WARNING', 'ERROR'].includes(level)) {
            level = 'INFO';
        }

        const logLevels = {
            'DEBUG': 0,
            'INFO': 1,
            'WARNING': 2,
            'ERROR': 3
        };

        if (logLevels[level] < logLevels[CONFIG.logLevel]) {
            return;
        }

        const logColor = {
            DEBUG: '#888',
            INFO: '#2196F3',
            WARNING: '#FFC107',
            ERROR: '#F44336'
        };

        console.log(`%c[Translator][${level}] ${message}`, `color: ${logColor[level]}`);
    }

    // 使用Google翻译移动版网页接口进行翻译(无需API KEY)
    async function translateText(text, sourceLang = CONFIG.sourceLanguage, targetLang = CONFIG.targetLanguage) {
        // 检查缓存
        if (translationCache.has(text)) {
            log(`使用缓存翻译: ${text.substring(0, 30)}...`, 'DEBUG');
            return translationCache.get(text);
        }

        // 如果文本为空或只包含空白字符,直接返回
        if (!text || text.trim() === '') {
            return text;
        }

        log(`翻译文本: ${text.substring(0, 30)}...`, 'DEBUG');

        try {
            // 实现请求延迟,避免请求过于频繁
            const now = Date.now();
            const timeSinceLastRequest = now - lastTranslationTime;
            if (timeSinceLastRequest < CONFIG.translationRequestDelay) {
                const waitTime = CONFIG.translationRequestDelay - timeSinceLastRequest;
                log(`等待 ${waitTime}ms 后发送翻译请求...`, 'DEBUG');
                await delay(waitTime);
            }

            // 更新最后请求时间
            lastTranslationTime = Date.now();

            // 构建Google翻译移动版网页请求URL
            const encodedText = encodeURIComponent(text);
            const url = `https://translate.google.com/m?sl=${sourceLang}&tl=${targetLang}&q=${encodedText}`;

            // 使用Promise包装GM_xmlhttpRequest
            const response = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    headers: {
                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                    },
                    onload: function(response) {
                        if (response.status >= 200 && response.status < 300) {
                            resolve(response);
                        } else {
                            reject(new Error(`请求失败: ${response.status} ${response.statusText}`));
                        }
                    },
                    onerror: function(error) {
                        reject(error);
                    }
                });
            });

            // 从HTML中提取翻译结果
            const html = response.responseText;

            // 尝试多种可能的正则表达式模式来匹配翻译结果
            let translatedText = null;

            // 模式1: 移动版翻译结果容器
            const regex1 = /<div class="result-container">(.*?)<\/div>/s;
            const match1 = html.match(regex1);
            if (match1 && match1[1]) {
                translatedText = match1[1].trim();
            }

            // 模式2: 另一种可能的结果容器
            if (!translatedText) {
                const regex2 = /<div class="t0">(.*?)<\/div>/s;
                const match2 = html.match(regex2);
                if (match2 && match2[1]) {
                    translatedText = match2[1].trim();
                }
            }

            // 模式3: 另一种可能的结果容器
            if (!translatedText) {
                const regex3 = /<div class="translation">(.*?)<\/div>/s;
                const match3 = html.match(regex3);
                if (match3 && match3[1]) {
                    translatedText = match3[1].trim();
                }
            }

            // 如果所有模式都失败,尝试更通用的方法
            if (!translatedText) {
                // 查找任何可能包含翻译结果的div
                const resultDivRegex = /<div[^>]*>(.*?)<\/div>/gs;
                const allDivs = [...html.matchAll(resultDivRegex)];

                // 查找包含原文长度相似的div(可能是翻译结果)
                for (const divMatch of allDivs) {
                    const divContent = divMatch[1].trim();
                    // 排除太短或太长的内容
                    if (divContent && divContent.length > text.length * 0.5 && divContent.length < text.length * 2) {
                        translatedText = divContent;
                        break;
                    }
                }
            }

            // 如果仍然没有找到翻译结果,返回原文
            if (!translatedText) {
                log('无法从HTML中提取翻译结果', 'WARNING');
                return text;
            }

            // 清理HTML标签
            translatedText = translatedText.replace(/<[^>]*>/g, '');

            // 更新缓存
            if (translationCache.size >= CONFIG.translationCacheSize) {
                // 如果缓存已满,删除最早添加的项
                const firstKey = translationCache.keys().next().value;
                translationCache.delete(firstKey);
            }
            translationCache.set(text, translatedText);

            log(`翻译完成: ${text.substring(0, 20)}... -> ${translatedText.substring(0, 20)}...`, 'INFO');
            return translatedText;
        } catch (error) {
            log(`翻译失败: ${error.message}`, 'ERROR');
            return text; // 翻译失败时返回原文
        }
    }

    // 从聊天消息元素中提取用户名和消息内容
    function extractMessageInfo(chatMessageElement) {
        if (!chatMessageElement) return null;

        try {
            // 检查是否是系统消息
            if (chatMessageElement.classList.contains('ChatMessage_systemMessage__3Jz9e')) {
                // 系统消息通常只有一个span,直接包含在消息元素中
                const systemSpan = chatMessageElement.querySelector('span:not(.ChatMessage_timestamp__1iRZO)');
                if (systemSpan && systemSpan.textContent) {
                    return {
                        isSystemMessage: true,
                        username: 'System',
                        contentSpan: systemSpan,
                        originalContent: systemSpan.textContent
                    };
                }
                return null;
            }

            // 查找用户名元素
            const nameSelector = getClassSelector(CONFIG.classNamePrefixes.name);
            const nameElement = chatMessageElement.querySelector(nameSelector);

            if (!nameElement) {
                log('未找到用户名元素', 'DEBUG');
                return null;
            }

            // 提取用户名 - 用户名不需要翻译,所以只是提取文本
            const username = nameElement.textContent.trim();

            // 查找最后一个span元素,通常是消息内容
            // 这个方法更可靠,因为消息内容总是在最后
            const allSpans = Array.from(chatMessageElement.querySelectorAll('span'));

            // 过滤掉时间戳、用户名相关的span和已翻译的span
            const contentSpans = allSpans.filter(span => {
                // 跳过时间戳
                if (span.classList.toString().includes(CONFIG.classNamePrefixes.timestamp)) {
                    return false;
                }

                // 跳过包含用户名的span或其父元素
                if (span.contains(nameElement) || nameElement.contains(span)) {
                    return false;
                }

                // 跳过冒号span (通常紧跟用户名)
                if (span.textContent.trim() === ':' || span.textContent.trim() === ':') {
                    return false;
                }

                // 跳过已经被标记为翻译过的span
                if (span.hasAttribute('data-translated')) {
                    return false;
                }

                // 跳过包含在链接容器中的span
                const linkContainer = span.closest('.ChatMessage_linkContainer__18Kv3');
                if (linkContainer) {
                    return false;
                }

                // 跳过物品元素
                const itemContainer = span.closest('.Item_itemContainer__x7kH1');
                if (itemContainer) {
                    return false;
                }

                return span.textContent.trim() !== '';
            });

            // 如果找不到合适的内容span,返回null
            if (contentSpans.length === 0) {
                log('未找到合适的消息内容span', 'DEBUG');
                return null;
            }

            // 使用最后一个符合条件的span作为消息内容
            const contentSpan = contentSpans[contentSpans.length - 1];

            return {
                isSystemMessage: false,
                username,  // 用户名不翻译
                contentSpan,  // 只翻译消息内容span
                originalContent: contentSpan.textContent
            };
        } catch (error) {
            log(`提取消息信息时出错: ${error.message}`, 'ERROR');
            return null;
        }
    }

    // 处理单个聊天消息
    async function processChatMessage(chatMessageElement) {
        if (!chatMessageElement) return false;

        try {
            // 在DEBUG级别下显示消息结构
            if (CONFIG.logLevel === 'DEBUG') {
                debugMessageStructure(chatMessageElement);
            }

            const messageInfo = extractMessageInfo(chatMessageElement);
            if (!messageInfo) {
                log('无法提取消息信息', 'DEBUG');
                return false;
            }

            const { isSystemMessage, username, contentSpan, originalContent } = messageInfo;

            // 确保我们找到的是消息内容而不是用户名
            if (!contentSpan || !originalContent) {
                log('未找到有效的消息内容', 'WARNING');
                return false;
            }

            // 检查是否已翻译(添加一个标记属性)
            if (contentSpan.hasAttribute('data-translated')) {
                log(`跳过已翻译的消息: ${originalContent.substring(0, 20)}...`, 'DEBUG');
                return false;
            }

            // 跳过只包含表情符号、特殊字符或非英文内容的消息
            if (!/[a-zA-Z]{2,}/.test(originalContent)) {
                log(`跳过非英文内容: ${originalContent}`, 'DEBUG');
                contentSpan.setAttribute('data-translated', 'non-english');
                return false;
            }

            // 跳过系统消息(可选,根据需要配置)
            if (isSystemMessage && !CONFIG.translateSystemMessages) {
                log(`跳过系统消息: ${originalContent.substring(0, 20)}...`, 'DEBUG');
                contentSpan.setAttribute('data-translated', 'system');
                return false;
            }

            log(`准备翻译 ${username} 的消息内容: ${originalContent.substring(0, 30)}...`, 'DEBUG');

            // 只翻译消息内容,不翻译用户名
            const translatedContent = await translateText(originalContent);

            // 如果翻译结果与原文相同,则不做更改
            if (translatedContent === originalContent) {
                contentSpan.setAttribute('data-translated', 'same');
                log(`翻译结果与原文相同: ${originalContent.substring(0, 20)}...`, 'DEBUG');
                return false;
            }

            // 更新span内容
            contentSpan.textContent = translatedContent;
            contentSpan.setAttribute('data-translated', 'true');
            contentSpan.setAttribute('title', `原文: ${originalContent}`); // 添加原文作为提示

            log(`已翻译 ${username} 的消息内容: ${originalContent.substring(0, 20)}... -> ${translatedContent.substring(0, 20)}...`, 'INFO');
            return true;
        } catch (error) {
            log(`处理聊天消息时出错: ${error.message}`, 'ERROR');
            return false;
        }
    }

    // 扫描并翻译聊天消息
    async function scanAndTranslate() {
        log('开始扫描聊天消息...', 'INFO');
        const startTime = performance.now();

        try {
            // 使用类名前缀查找所有聊天消息元素
            const chatMessageSelector = getClassSelector(CONFIG.classNamePrefixes.chatMessage);
            const chatMessages = document.querySelectorAll(chatMessageSelector);

            log(`找到 ${chatMessages.length} 条聊天消息`, 'INFO');

            let totalTranslations = 0;
            for (const message of chatMessages) {
                const translated = await processChatMessage(message);
                if (translated) {
                    totalTranslations++;
                }
            }

            const elapsedTime = performance.now() - startTime;
            log(`扫描完成: ${totalTranslations} 处翻译,耗时 ${elapsedTime.toFixed(2)}ms`, 'INFO');
            return totalTranslations;
        } catch (error) {
            log(`扫描过程中出错: ${error.message}`, 'ERROR');
            return 0;
        }
    }

    // 处理新添加的节点
    async function processAddedNode(node) {
        if (!node || node.nodeType !== Node.ELEMENT_NODE) return;

        try {
            // 检查节点是否是聊天消息
            const chatMessageSelector = getClassSelector(CONFIG.classNamePrefixes.chatMessage);
            if (node.matches && node.matches(chatMessageSelector)) {
                await processChatMessage(node);
                return;
            }

            // 查找节点内的所有聊天消息
            const chatMessages = node.querySelectorAll(chatMessageSelector);
            for (const message of chatMessages) {
                await processChatMessage(message);
            }
        } catch (error) {
            log(`处理新添加节点时出错: ${error.message}`, 'ERROR');
        }
    }

    // 检查元素是否在聊天历史区域内
    function isInChatHistory(element) {
        if (!element) return false;

        // 向上查找聊天历史容器
        let current = element;
        const chatHistorySelector = getClassSelector(CONFIG.classNamePrefixes.chatHistory);

        while (current && current !== document.body) {
            if (current.matches && current.matches(chatHistorySelector)) {
                return true;
            }
            current = current.parentElement;
        }

        return false;
    }

    // 防抖函数,避免频繁处理
    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            const context = this;
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(context, args), wait);
        };
    }

    // 处理DOM变化的防抖函数
    const debouncedProcessMutations = debounce(async (mutations) => {
        let hasNewMessages = false;

        for (const mutation of mutations) {
            if (mutation.type === 'childList') {
                // 处理新增节点
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === Node.ELEMENT_NODE && isInChatHistory(node)) {
                        await processAddedNode(node);
                        hasNewMessages = true;
                    }
                }
            } else if (mutation.type === 'characterData') {
                // 处理文本内容变更
                const textNode = mutation.target;
                if (textNode && textNode.nodeType === Node.TEXT_NODE && isInChatHistory(textNode)) {
                    // 查找包含此文本节点的聊天消息元素
                    let current = textNode.parentNode;
                    const chatMessageSelector = getClassSelector(CONFIG.classNamePrefixes.chatMessage);

                    while (current && current !== document.body) {
                        if (current.matches && current.matches(chatMessageSelector)) {
                            await processChatMessage(current);
                            hasNewMessages = true;
                            break;
                        }
                        current = current.parentElement;
                    }
                }
            }
        }

        if (hasNewMessages) {
            log('处理了新的聊天消息', 'DEBUG');
        }
    }, 300); // 300ms防抖延迟

    // 初始化函数
    function init() {
        log('翻译器初始化中...', 'INFO');

        try {
            // 初始延迟扫描,等待页面完全加载
            setTimeout(async () => {
                log('执行初始聊天消息扫描...', 'INFO');
                const initialTranslations = await scanAndTranslate();
                log(`初始扫描完成,翻译了 ${initialTranslations} 条消息`, 'INFO');

                // 动态监听DOM变化
                const observer = new MutationObserver(mutations => {
                    debouncedProcessMutations(mutations);
                });

                // 查找聊天历史容器并监听其变化
                const chatHistorySelector = getClassSelector(CONFIG.classNamePrefixes.chatHistory);
                const chatHistoryElements = document.querySelectorAll(chatHistorySelector);

                if (chatHistoryElements.length > 0) {
                    for (const element of chatHistoryElements) {
                        observer.observe(element, {
                            childList: true,
                            subtree: true,
                            characterData: true
                        });
                        log(`开始监听聊天历史容器: ${element.className}`, 'INFO');
                    }
                } else {
                    // 如果找不到聊天历史容器,则监听整个body
                    observer.observe(document.body, {
                        childList: true,
                        subtree: true,
                        characterData: true
                    });
                    log('未找到聊天历史容器,监听整个页面', 'WARNING');
                }

                log('翻译器已启动并监听DOM变化', 'INFO');
            }, CONFIG.initialScanDelay);

            // 定期重新扫描整个DOM
            setInterval(async () => {
                await scanAndTranslate();
            }, CONFIG.rescanInterval);

            log(`翻译器配置: 源语言=${CONFIG.sourceLanguage}, 目标语言=${CONFIG.targetLanguage}, 扫描间隔=${CONFIG.rescanInterval/1000}s`, 'INFO');
        } catch (error) {
            log(`初始化失败: ${error.message}`, 'ERROR');
        }
    }

    // 添加翻译样式
    function addTranslationStyles() {
        const styleElement = document.createElement('style');
        styleElement.textContent = `
            span[data-translated="true"] {
                color: #4CAF50 !important;
                text-decoration: underline dotted #4CAF50;
                position: relative;
            }

            span[data-translated="true"]:hover::after {
                content: attr(title);
                position: absolute;
                bottom: 100%;
                left: 0;
                background: rgba(0, 0, 0, 0.8);
                color: white;
                padding: 4px 8px;
                border-radius: 4px;
                font-size: 12px;
                white-space: pre-wrap;
                max-width: 300px;
                z-index: 1000;
            }

            .translator-status {
                position: fixed;
                bottom: 10px;
                right: 10px;
                background: rgba(33, 150, 243, 0.8);
                color: white;
                padding: 5px 10px;
                border-radius: 4px;
                font-size: 12px;
                z-index: 10000;
                display: none;
                transition: opacity 0.3s;
            }
        `;
        document.head.appendChild(styleElement);
    }

    // 添加状态指示器
    function addStatusIndicator() {
        const statusDiv = document.createElement('div');
        statusDiv.className = 'translator-status';
        statusDiv.textContent = '翻译器已启动';
        document.body.appendChild(statusDiv);

        // 显示状态指示器几秒钟,然后淡出
        setTimeout(() => {
            statusDiv.style.display = 'block';
            setTimeout(() => {
                statusDiv.style.opacity = '0';
                setTimeout(() => {
                    statusDiv.style.display = 'none';
                }, 1000);
            }, 3000);
        }, 1000);

        return statusDiv;
    }

    // 更新状态指示器
    function updateStatus(message, duration = 3000) {
        const statusDiv = document.querySelector('.translator-status') || addStatusIndicator();
        statusDiv.textContent = message;
        statusDiv.style.display = 'block';
        statusDiv.style.opacity = '1';

        setTimeout(() => {
            statusDiv.style.opacity = '0';
            setTimeout(() => {
                statusDiv.style.display = 'none';
            }, 1000);
        }, duration);
    }

    // 启动脚本
    function startScript() {
        // 添加样式
        addTranslationStyles();

        // 初始化翻译器
        init();

        // 添加状态指示器
        addStatusIndicator();

        // 打印初始状态信息
        if (CONFIG.enableConsoleLog) {
            console.log('%c[Translator] 翻译器已加载,控制台日志已开启', 'color: #2196F3');
        } else {
            console.log('%c[Translator] 翻译器已加载,控制台日志已关闭', 'color: #888');
        }
    }

    // 根据页面加载状态启动脚本
    if (document.readyState === 'loading') {
        window.addEventListener('DOMContentLoaded', startScript);
    } else {
        startScript();
    }

    // 调试函数:显示消息结构
    function debugMessageStructure(chatMessageElement) {
        if (!CONFIG.enableConsoleLog || CONFIG.logLevel !== 'DEBUG') return;

        try {
            console.group('消息结构调试');
            console.log('消息元素:', chatMessageElement);

            // 查找用户名元素
            const nameSelector = getClassSelector(CONFIG.classNamePrefixes.name);
            const nameElement = chatMessageElement.querySelector(nameSelector);
            console.log('用户名元素:', nameElement);

            if (nameElement) {
                console.log('用户名文本:', nameElement.textContent.trim());
            }

            // 查找所有span元素
            const spans = chatMessageElement.querySelectorAll('span');
            console.log('所有span元素:', spans);

            // 查找可能的消息内容span
            for (let i = 0; i < spans.length; i++) {
                const span = spans[i];
                console.log(`Span ${i}:`, {
                    element: span,
                    text: span.textContent.trim(),
                    classes: span.className,
                    containsUserName: nameElement && (span.contains(nameElement) || nameElement.contains(span))
                });
            }

            console.groupEnd();
        } catch (error) {
            console.error('调试消息结构时出错:', error);
        }
    }

    // 导出一些函数到全局作用域,方便调试
    window.messageTranslator = {
        translate: translateText,
        scan: scanAndTranslate,
        updateStatus: updateStatus,
        config: CONFIG,
        debug: {
            messageStructure: debugMessageStructure,
            extractMessageInfo: extractMessageInfo
        }
    };
})();