翻译小工具 | 一键翻译任何语言 | 划选翻译 | 可展开原译文对照 | 简便有效

任何语言翻译及快捷一键翻译想要语言 | 页面翻译 | 选中文字( 按Ctrl )| 提供快捷方式,只需按一个键即可快速翻译想要语言 |英文学习 | 翻译文可设置,支持全球多数通用语言 | 有什么问题都可以反馈

目前为 2024-05-25 提交的版本。查看 最新版本

// ==UserScript==
// @name         翻译小工具 | 一键翻译任何语言 | 划选翻译 | 可展开原译文对照 | 简便有效
// @namespace    http://tampermonkey.net/
// @version      1.1.3
// @description  任何语言翻译及快捷一键翻译想要语言 | 页面翻译 | 选中文字( 按Ctrl )| 提供快捷方式,只需按一个键即可快速翻译想要语言 |英文学习 | 翻译文可设置,支持全球多数通用语言 | 有什么问题都可以反馈
// @author       [email protected]
// @match        *://*/*
// @license      GPLv3
// @icon         https://s21.ax1x.com/2024/05/17/pkuVzUH.png
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// @grant        GM_xmlhttpRequest
// ==/UserScript==

var cssContent = `
.fy_btn_box{
    position: fixed;
    top: 50px;
    right: 50px;
    z-index: 9999;
    background-color: rgba(255, 255, 255, 0.5);
    border-radius: 10px;
    padding: 10px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
    color: #333;
}
#fy_transContainer{
    display: none;
    position: fixed;
    top: 50px;
    left: 50px;
    max-width: 500px;
    min-width: 300px;
    padding: 10px;
    padding-top: 24px;
    border-radius: 4px;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    box-shadow: 0 3px 10px 1px rgba(0, 0, 0, 0.1);
    background-color: rgba(255,255,255,0.7);
    backdrop-filter: saturate(420%) blur(50px);
    -webkit-backdrop-filter: saturate(420%) blur(50px);
    z-index: 9999;
    font-size: 14px;
    border-bottom-right-radius: 0;
    overflow: hidden;
}
#fy_dragBar{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 20px;
    cursor: grab;
    // background-color: #F9E79F;
    background-color: rgba(0,0,0,0.1);
    backdrop-filter: saturate(420%) blur(50px);
    -webkit-backdrop-filter: saturate(420%) blur(50px);
}
#fy_Scale_rb, #fy_Scale_lb{
    position:absolute;
    bottom:0;
    width:9px;
    height:9px;
    // background: #ddd;
    // clip-path: polygon(100% 0%, 100% 100%, 0% 100%);
}
#fy_Scale_rb{
    right:0;
    cursor: nw-resize;
}
#fy_Scale_lb{
    left:0;
    cursor: ne-resize;
}

#fy_contentBox{
    width: 100%;
    overflow: auto;
    line-height: 1.3em;
    letter-spacing: 0.5px;
}
#fy_contentBox .textRight{
    text-align: right;
}
.transText_node{
    width: 100%;
    padding: 7px;
    box-sizing: border-box;
}
.transText_node:hover{
    background-color: rgba(0,0,0,0.04);
    border-radius: 6px;
}
.transText_node_to{
    transition: all 0.2s;
}
.transText_node_from{
    height: 0;
    overflow: hidden;
    transition: all 0.2s;
}



#fy_contentBox .fy_node_expand{
    background-color: rgba(0,0,0,0.04);
    border-radius: 6px;
    margin: 5px 0;
}

.fy_node_expand .transText_node_from, .fy_node_expand .transText_node_to{
    padding: 6px 8px;
}
.fy_node_expand .transText_node_to{
    background-color: rgb(209, 255, 240);
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
}
.fy_node_expand .transText_node_from{
    background-color: rgb(254, 234, 242);
    height: auto;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
}

.copy_icon{
    cursor: pointer;
    opacity: 0;
    transition: all 0.3s ease;
}
.transText_node_from:hover .copy_icon{
    opacity: 1;
}
.transText_node_to:hover .copy_icon{
    opacity: 1;
}

#fy_ctrl_ber{
    position: absolute;
    top: 2px;
    right: 0;
}
#fy_select, #fy_api_select{
    position: relative;
    background-color: transparent;
    border: none; /* 去除默认边框 */
    box-shadow: none; /* 去除默认的阴影 */
    outline: none; /* 去除可能的轮廓线 */
    font: inherit;
    line-height: 1.2em;
    height: auto;
    text-align: center;
    width: auto;
    min-width: auto;
    min-height: auto;
}
#fy_api_select{
}

#fy_loading{
    display: none;
    width: 100%;
    padding-top: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
}
#fy_loading svg {
    width: 2.25em;
    transform-origin: center;
    animation: rotate4 2s linear infinite;
    }

#fy_loading circle {
    fill: none;
    stroke: hsl(214, 97%, 59%);
    stroke-width: 2;
    stroke-dasharray: 1, 200;
    stroke-dashoffset: 0;
    stroke-linecap: round;
    animation: dash4 1.5s ease-in-out infinite;
    }

@keyframes rotate4 {
    100% {
        transform: rotate(360deg);
    }
}

@keyframes dash4 {
    0% {
        stroke-dasharray: 1, 200;
        stroke-dashoffset: 0;
    }

    50% {
        stroke-dasharray: 90, 200;
        stroke-dashoffset: -35px;
    }

    100% {
        stroke-dashoffset: -125px;
    }
}



/* 滚动条整体样式 */
::-webkit-scrollbar {
    width: 6px;  /* 宽度 */
    height: 6px; /* 高度(对于垂直滚动条) */
}

/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
    background: #aaa;
    border-radius: 6px;
}

/* 滚动条滑块:hover状态样式 */
::-webkit-scrollbar-thumb:hover {
    background: #888;
}

/* 滚动条轨道 */
::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 6px;
}

/* 滚动条轨道:hover状态样式 */
::-webkit-scrollbar-track:hover {
    background: #ddd;
}

/* 滚动条轨道:active状态样式 */
::-webkit-scrollbar-track-piece:active {
    background: #eee;
}

/* 滚动条:角落样式(即两个滚动条交汇处) */
::-webkit-scrollbar-corner {
    background: #535353;
}


`


// ------------全局----------------
// 显示总容器
var transContainerDOM = null
// 翻译内容容器
var fyContentDOM = null
// 拖动条
var fyDragBarDOM = null
// 待翻译文本
var fromTransTextArray = [];
// 翻译结果对象
var transRes = {}
// 时间戳
var salt = Date.now()
// 当前翻译对象数组 
var currTransToObjs = []
// --------------------------
// 语言选择类型
var selectTypes = [
    { type: 'zh', valueName: '中', name: '中文' },
    { type: 'en', valueName: '英', name: '英文' },
    { type: 'jp', valueName: '日', name: '日文' },
    { type: 'kor', valueName: '韩', name: '韩文' },
    { type: 'fra', valueName: '法', name: '法文' },
    { type: 'spa', valueName: '西', name: '西班牙文' },
    { type: 'ru', valueName: '俄', name: '俄文' },
    { type: 'de', valueName: '德', name: '德文' },
    { type: 'it', valueName: '意', name: '意大利文' },
    { type: 'th', valueName: '泰', name: '泰文' },
    { type: 'vie', valueName: '越', name: '越南文' },
    { type: 'pt', valueName: '葡', name: '葡萄牙文' },
    { type: 'ara', valueName: '阿', name: '阿拉伯文' },
    { type: 'cht', valueName: '中(繁)', name: '中文繁体'},
    { type: 'yue', valueName: '中(粤)', name: '粤语' },
]
// 翻译api列表
var apiMap = [
    { name: 'Baidu', descript: '百度翻译' },
    { name: 'Google', descript: '谷歌翻译(需要外网)' },
]
// 默认百度翻译
var currFromApi = 'Baidu'
// 默认翻译语言
var fyToType = '中'


// 初始加载样式
const loadStyle = () => {
    var style = document.createElement('style');
    if (style.styleSheet) {
        // 对于老版本的IE浏览器
        style.styleSheet.cssText = cssContent;
    } else {
        style.appendChild(document.createTextNode(cssContent));
    }
    var head = document.head || document.getElementsByTagName('head')[0];
    head.appendChild(style);
}
/**
 * 传入配置信息创建元素并返回DOM对象
*/
const myCreateEle = (option, mountE) => {
    let e = document.createElement(option.el || 'div')
    option.className && e.classList.add(option.className)
    for (let p in (option.props || {})) {
        e.setAttribute(p, option.props[p])
    }
    e.textContent = option.text || ''
    e.style.cssText = option.style || ''
    mountE && mountE.appendChild(e)
    return e
}

// 初始生成元素
const initLoadElement = () => {
    transContainerDOM = myCreateEle({
        props: { id: 'fy_transContainer' }
    }, document.body)
    // 内容容器
    fyContentDOM = myCreateEle({ props: { id: 'fy_contentBox' } }, transContainerDOM)

    // loading
    $(transContainerDOM).append(`
    <div id="fy_loading">
        <svg viewBox="25 25 50 50">
            <circle r="20" cy="50" cx="50"></circle>
        </svg>
    </div>
    `)

    // 拖动条
    fyDragBarDOM = myCreateEle({
        props: { id: 'fy_dragBar' }
    }, transContainerDOM)

    // 缩放点
    myCreateEle({
        props: { id: 'fy_Scale_rb' }
    }, transContainerDOM)
    myCreateEle({
        props: { id: 'fy_Scale_lb' }
    }, transContainerDOM)

    // 控制条
    let fyCtrlBarDom = myCreateEle({props: { id: 'fy_ctrl_ber' }
    }, transContainerDOM)
    // 选择翻译api源
    let fyApiSelectDom = myCreateEle({ el:'select',
        props: { id: 'fy_api_select' }
    }, fyCtrlBarDom)
    apiMap.forEach(item => {
        $(fyApiSelectDom).append(`
            <option value="${item.name}" title="${item.descript}">
                <span>${item.name}</span>
            </option>
        `)
    })
    // 语言选择
    let fyLangSelectDom = myCreateEle({ el:'select',
        props: { id: 'fy_select' }
    }, fyCtrlBarDom)
    selectTypes.forEach(item => {
        $(fyLangSelectDom).append(`
        <option value="${item.valueName}" title="${item.name}">
            <span>${item.valueName}</span>
        </option>
        `)
    })

    // 语言选择
    // let optionsStr = ''
    // selectTypes.forEach(item => {
    //     optionsStr += `
    //     <option value="${item.valueName}" title="${item.name}">
    //         <span>${item.valueName}</span>
    //     </option>
    //     `
    // })
    // $(transContainerDOM).append(`
    //     <select id="fy_select">
    //     ${optionsStr}
    //     </select>
    // `)


    // ❌➖📌💡🎯📝✔️❓❗️📅🚫🔄✅📖📘
    // filter: grayscale(100%); 置灰
}


// 生成MD5值
const calculateMD5 = (input) => {
    return CryptoJS.MD5(input).toString();
}

// 百度翻译
const baiduTranslate = async (fromTransText, reUpdate = false) => {
    let targetLang = (baiduOptions.find(item => item.valueName == fyToType)).type
    let appid = '20240513002050392';
    let sign = calculateMD5(appid + fromTransText + salt + 'evAKKTnaxMEpHrnCxwDC');
    let param = `?q=${fromTransText}&from=auto&to=${targetLang}&appid=${appid}&salt=${salt}&sign=${sign}`


    console.log('查看百度请求 ',param)

    await GM_xmlhttpRequest({
        url: "https://fanyi-api.baidu.com/api/trans/vip/translate" + param,
        method: "GET",
        onload: function (response) {
            if (response.status === 200) {
                let res = JSON.parse((response.responseText || ''))
                if (!(res.trans_result && res.trans_result.length > 0)) return;
                transRes = {
                    apiType: 'baidu',
                    formText: res.trans_result[0].src,
                    toText: res.trans_result[0].dst,
                }
                currTransToObjs.push({ ...transRes })
                renderRes(res.trans_result[0].src, res.trans_result[0].dst)
                $('#fy_loading').hide()
                !reUpdate && computedContainer()
                // 复制翻译后文字
                copyText(res.trans_result[0].dst)
            } else {
                console.error("百度翻译请求失败,状态码: " + response.status);
            }
        },
        onerror: function (e) {
            handleError(e)
        },
    });
}

// google翻译
const googleTranslate = async (texts, reUpdate = false) => {
    const params = new URLSearchParams();
    texts.forEach(text => params.append('q', text));
    let targetLang = (googleOptions.find(item => item.valueName == fyToType)).type
    console.log('查看请求 target ',targetLang)
    const apiKey = 'AIzaSyC1-wRtiswc3Y4qtPIC6ojlRMM-2Aw8lQw';
    const url = `https://translation.googleapis.com/language/translate/v2?target=${targetLang}&key=${apiKey}&${params.toString()}`;
    await GM_xmlhttpRequest({
        url: url,
        method: "GET",
        onload: function (response) {
            if (response.status === 200) {
                let { data = {} } = JSON.parse((response.responseText || ''))
                // console.log('查看序列后 ', data)
                if (!(data.translations && data.translations.length > 0)) return;
                transRes = {
                    apiType: 'google',
                    formTextArray: texts,
                    toTextsArray: data.translations.map(item => item.translatedText),
                }
                data.translations.forEach((item, i) => {
                    renderRes(texts[i], item.translatedText)
                })
                $('#fy_loading').hide()
                !reUpdate && computedContainer()
                // 复制翻译后文字
                copyText(transRes.toTextsArray[0])
            } else {
                console.error("google翻译请求失败,状态码: " + response.status);
            }
        },
        onerror: function (e) {
            handleError(e)
        },
    });
}


const renderRes = (formText, toText)=>{
    $(fyContentDOM).append(`
        <div class="transText_node">
            <div class="transText_node_to" title="${formText}">${toText} <span class="copy_icon" title="复制" value="${toText}">📝</span></div>
            <div class="transText_node_from">${formText} <span class="copy_icon" title="复制" value="${formText}">📝</span></div>
        </div>
    `)
}
const handleError = (e)=>{
    // <div>或是否在用VPN代理❗️(百度的api😅)</div>
    $(fyContentDOM).append(`
        <div class="transText_node" style="text-align: center;">
            <div>请求失败❗️☹️</div>
            <div>请检查网络连接❗️</div>
        </div>
    `)
    $('#fy_loading').hide()
}


var currX = 0; // 当前鼠标位置
var currY = 0; // 当前鼠标位置
var isContainer = false // 容器是否出现
var isCtrl = false; // 是否处于可翻译状态
// 绑定事件
const bingEvents = () => {
    // // transContainerDOM 翻译容器

    // 绑定Ctrl+右键点击翻译
    // bindCtrlRightClick()
    // 绑定翻译键
    bingTransKeys()

    // 点击页面
    document.body.onclick = function (event) {
        if (isContainer) {
            clearTransContainer()
        }
    };
    // 清除翻译容器
    const clearTransContainer = () => {
        isContainer = false
        transContainerDOM.style.display = 'none'
        transContainerDOM.style.maxWidth = '500px';
        transContainerDOM.style.minWidth = '300px'
        transContainerDOM.style.width = 'auto'
        transContainerDOM.style.height = 'auto'
        fyContentDOM.textContent = ''
        fromTransTextArray = []
    }

    transContainerDOM.onclick = function (e) {
        e.stopPropagation(); // 阻止事件冒泡
    }
    // 上下文菜单
    document.addEventListener("contextmenu", function (event) {
        if (isCtrl) {
            // 取消默认行为(阻止上下文菜单出现)
            event.preventDefault();
        }
    });
    bindHandleDrag() // 绑定拖动模块事件
    bindHandleScale() // 绑定缩放模块事件
    bindHandleSelectLang() // 绑定切换翻译事件
    bindHandleSelectApi() // 绑定切换翻译请求的api事件
    bindTextClick() // 点击翻译文本事件


    // 鼠标按键抬起事件
    document.addEventListener("mouseup", function (event) {
        if(isContainer){
            const rect = transContainerDOM.getBoundingClientRect();
            currX = rect.left
            currY = rect.top
        }else{
            currX = event.clientX;
            currY = event.clientY;
        }
    })

}

// Ctrl + 鼠标右键点击事件
// const bindCtrlRightClick = () => {
//     document.addEventListener('keydown', (e) => {
//         if (e.key === 'Control') {
//             isCtrl = true;
//         }
//     });
//     document.addEventListener('keyup', (e) => {
//         if (e.key === 'Control') {
//             isCtrl = false;
//         }
//     });
//     // 鼠标按下事件
//     document.addEventListener("mousedown", function (event) {
//         currX = event.clientX;
//         currY = event.clientY;
//         if (isCtrl && event.button === 2) {
//             // 获取Selection对象,选中的文本
//             let textAll = window.getSelection().toString();
//             if (!textAll) return
//             startTrans()
//         }
//     })
// }

// 绑定翻译事件按键
const bingTransKeys = () => {
    var inOnlyKeyVal = ''
    document.addEventListener('keydown', function (event) {
        inOnlyKeyVal = event.key
    });
    document.addEventListener('keyup', function (event) {
        let textAll = window.getSelection().toString();
        if (!inOnlyKeyVal) return;
        if (textAll) {
            if ([event.key, inOnlyKeyVal].every(key => key === 'Control')) {
                startTrans()
            }
            if ([event.key, inOnlyKeyVal].every(key => key === '`')) {
                startTrans()
            }
        }
        if (textAll || fromTransTextArray.length) {
            // 按键必须为数字键,且都为正整数
            if ([event.key, inOnlyKeyVal].every(key => !isNaN(parseInt(key)))) {
                let selectedValue = selectTypes[parseInt(event.key) - 1].valueName || ''
                if (!selectedValue) return
                $('#fy_select').val(selectedValue)
                fyToType = selectedValue
                startTrans()
            }
        }
        inOnlyKeyVal = ''
    });
}

// 防止重复请求锁
var isLockTrans = false
// 开始执行翻译请求
const startTrans = () => {
    if (isLockTrans) {
        showMessage({ message: '操作频繁,请稍后~', type: 'warning', time: 1000 })
        return
    };
    isLockTrans = true
    let textAll = window.getSelection().toString();
    let arrFroms = formatTrans(textAll)
    if(arrFroms && arrFroms.length){
        fromTransTextArray = arrFroms
    }
    // 判断是否有选中翻译的文本
    isContainer = true // 更改容器状态
    transContainerDOM.style.display = 'flex'
    $('#fy_loading').show()
    fyContentDOM.textContent = ''
    computedContainer() // 计算容器位置

    const methodMap = {
        Baidu: (texts)=>{
            texts.filter(text => text).forEach(text => {
                baiduTranslate(text)
            })
        },
        Google: googleTranslate,
    }
    methodMap[currFromApi](fromTransTextArray)
    setTimeout(() => {
        isLockTrans = false
    }, 500);
}


// 切换翻译语言
const bindHandleSelectLang = () => {
    document.getElementById('fy_select').onchange = function (e) {
        fyToType = this.value
        fyContentDOM.textContent = ''
        $('#fy_loading').show()
        startTrans()
        // googleTranslate(fromTransTextArray, true)
    }
}
// 切换api
const bindHandleSelectApi = () => {
    document.getElementById('fy_api_select').onchange = function (e) {
        currFromApi = this.value
        fyContentDOM.textContent = ''
        $('#fy_loading').show()
        startTrans()
        // googleTranslate(fromTransTextArray, true)
    }
}


// 窗口拖动事件
const bindHandleDrag = () => {
    let isMove = false
    let mouseToEleX;
    let mouseToEleY;
    // 拖动处理
    fyDragBarDOM.addEventListener("mousedown", function (e) {
        if (!isCtrl) {
            isMove = true
            fyDragBarDOM.style.cursor = 'grabbing'
            // 获取鼠标相对于元素的位置
            mouseToEleX = e.clientX - transContainerDOM.getBoundingClientRect().left;
            mouseToEleY = e.clientY - transContainerDOM.getBoundingClientRect().top;
        }
    });
    // 当鼠标移动时
    window.addEventListener('mousemove', (e) => {
        if (!isMove) return
        // 防止默认的拖动选择文本行为
        e.preventDefault();
        let t = (e.clientY - mouseToEleY) < 0 ? 0 : e.clientY - mouseToEleY;
        // 更新元素的位置
        transContainerDOM.style.left = (e.clientX - mouseToEleX) + 'px';
        transContainerDOM.style.top = t + 'px';
    })
    // 当鼠标松开时
    window.addEventListener('mouseup', () => {
        isMove = false;
        fyDragBarDOM.style.cursor = 'grab'
    });
}
// 改变窗口大小事件
const bindHandleScale = () => {
    let mainCurrWidth;
    let mainCurrHeight;
    let cX, cY;
    let isScale = false;
    let scaleType = ''

    const scaleFun = (e, type) => {
        isScale = true
        mainCurrWidth = transContainerDOM.offsetWidth
        mainCurrHeight = transContainerDOM.offsetHeight
        cX = e.clientX;
        cY = e.clientY;
        scaleType = type
        let rect = transContainerDOM.getBoundingClientRect()
        if (scaleType == 'rb') {
            transContainerDOM.style.left = rect.left + 'px'
            transContainerDOM.style.right = 'auto'
        }
        if (scaleType == 'lb') {
            let scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
            transContainerDOM.style.right = (window.innerWidth - rect.right - scrollBarWidth) + 'px'
            transContainerDOM.style.left = 'auto'
        }
    }
    fy_Scale_rb.addEventListener('mousedown', (e) => {
        scaleFun(e, 'rb')
    });
    fy_Scale_lb.addEventListener('mousedown', (e) => {
        scaleFun(e, 'lb')
    });
    // 当鼠标移动时
    window.addEventListener('mousemove', (e) => {
        if (!isScale) return
        // 防止默认的拖动选择文本行为
        e.preventDefault();
        transContainerDOM.style.maxWidth = 'none'
        let newHeight = mainCurrHeight + (e.clientY - cY)
        let newWidth = mainCurrWidth;

        if (scaleType == 'rb') {
            newWidth = mainCurrWidth + (e.clientX - cX)
        }
        if (scaleType == 'lb') {
            newWidth = mainCurrWidth + (cX - e.clientX)
        }
        // 更新元素的位置
        transContainerDOM.style.width = Math.max(10, newWidth) + 'px';
        transContainerDOM.style.height = Math.max(10, newHeight) + 'px';
    })
    // 当鼠标松开时
    window.addEventListener('mouseup', () => {
        isScale = false;
    });
}

// 点击译文事件
var isClickLock = true
const bindTextClick = () => {
    fyContentDOM.addEventListener('click', function (event) {
        if (!isClickLock) return;
        isClickLock = false
        setTimeout(() => {
            isClickLock = true;
        }, 300); // 双击事件的间隔时间通常是300毫秒左右
        let textAll = window.getSelection().toString();
        if (textAll) return;
        let targetEle = event.target
        if (!targetEle.classList.contains('transText_node')) {
            targetEle = targetEle.parentNode
        }
        if (!targetEle.classList.contains('transText_node')) return;

        if (targetEle.classList.contains('fy_node_expand')) {
            targetEle.classList.remove('fy_node_expand');
        } else {
            targetEle.classList.add('fy_node_expand')
        }

        var rect = transContainerDOM.getBoundingClientRect();
        // 获取视口的高度
        var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
        // 计算元素底部到视口底部的距离
        if ((viewportHeight - rect.bottom) < 30) {
            transContainerDOM.style.height = (viewportHeight - rect.top - 50) + 'px'
        }
    });
    $(fyContentDOM).on('click', '.copy_icon', function () {
        copyText(this.getAttribute('value'))
        showMessage({
            message: '复制成功',
            time: 800,
        })
    })

}

// 计算渲染容器高度位置
const computedContainer = () => {
    let scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
    let distance_right = (window.innerWidth - transContainerDOM.getBoundingClientRect().right - scrollBarWidth)
    if (distance_right < 5) {
        transContainerDOM.style.left = 'auto'
        transContainerDOM.style.right = '5px'
    } else {
        transContainerDOM.style.right = 'auto'
        transContainerDOM.style.left = currX + 'px'
    }
    transContainerDOM.style.top = currY + 'px'

    let topToBotton = window.innerHeight - currY
    if (transContainerDOM.offsetHeight > topToBotton) {
        transContainerDOM.style.height = topToBotton + 'px'
    }
}

// 语音播放文本
const playText = (text) => {
    // 检查浏览器是否支持语音合成
    if ('speechSynthesis' in window) {
        // 创建语音合成实例
        var synthesis = window.speechSynthesis;
        var textToSpeak = text;
        // 创建语音合成的配置
        var utterance = new SpeechSynthesisUtterance(textToSpeak);
        // 使用默认语音
        utterance.voice = speechSynthesis.getVoices()[0];
        // 播放文本
        synthesis.speak(utterance);
    } else {
        console.log("抱歉,您的浏览器不支持语音合成功能。");
    }
}

const init = (e) => {
    initLoadElement();
    bingEvents();
}

(function () {
    if (window.self !== window.top) return;
    console.log('S translate ~')
    loadStyle();
    this.setTimeout(() => {
        init()
    }, 200);
    // window.addEventListener("load", function () {
    // })
})();



// 百度语言标识符列表
var baiduOptions = [
    { type: 'zh', valueName: '中' },
    { type: 'en', valueName: '英' },
    { type: 'jp', valueName: '日' },
    { type: 'kor', valueName: '韩' },
    { type: 'fra', valueName: '法' },
    { type: 'spa', valueName: '西' },
    { type: 'ru', valueName: '俄' },
    { type: 'de', valueName: '德' },
    { type: 'it', valueName: '意'},
    { type: 'th', valueName: '泰' },
    { type: 'vie', valueName: '越' },
    { type: 'pt', valueName: '葡' },
    { type: 'ara', valueName: '阿' },
    { type: 'cht', valueName: '中(繁)'},
    { type: 'yue', valueName: '中(粤)'},
]
// google语言标识符列表
var googleOptions = [
    { type: 'zh-CN', valueName: '中' },
    { type: 'en', valueName: '英' },
    { type: 'ja', valueName: '日' },
    { type: 'ko', valueName: '韩' },
    { type: 'fr', valueName: '法' },
    { type: 'es', valueName: '西' },
    { type: 'ru', valueName: '俄' },
    { type: 'de', valueName: '德' },
    { type: 'it', valueName: '意'},
    { type: 'th', valueName: '泰' },
    { type: 'vi', valueName: '越' },
    { type: 'pt', valueName: '葡' },
    { type: 'ar', valueName: '阿' },
    { type: 'zh-TW', valueName: '中(繁)'},
    { type: 'zh-TW', valueName: '中(粤)'},
]













// 唤起提示
const showMessage = ({ type = 'success', message, time }) => {
    let tipsDOM = myCreateEle({ text: message, type: type ?? 'success', style: 'position: absolute; top: 30px; left: 50%; transform: translate(-50%, 0%); padding: 2px 6px; border-radius: 2px; color:#fff; background-color: #67c23a; font-size: 10px;' }, transContainerDOM)
    const colorMap = {
        success: '#67c23a',
        warning: '#e6a23c',
        error: '#f56c6c',
        info: '#909399',
    }
    tipsDOM.style.backgroundColor = colorMap[type]
    setTimeout(() => {
        tipsDOM.remove();
    }, time ?? 2000);
}


// 格式化页面划选的文本,拆分成数组
const formatTrans = (texts = '') => {
    return (texts.split(/[\n\t]+/) || []).filter(text => text)
}

// copy 文本
const copyText = (text) => {
    navigator.clipboard.writeText(text)
        .then(() => {
            // console.log('文本已成功复制到剪贴板');

        })
        .catch(err => {
            // 某些浏览器可能不支持或需要用户交互  
            // console.error('无法复制文本: ', err);
        });
}





// -------------------------------------
cssContent += `
// .copy_icon{
//     position: absolute;
//     bottom: 10xp;
//     right: 10px;
// }
// #fy_top_tools{
//     width: auto;
//     position: relative;
//     display: inline-flex;
//     align-items: center;
//     box-sizing: border-box;
//     padding: 0 6px;
//     height: 100%;
//     cursor: initial;
// }
// .fy_top_toolItem{
//     padding: 3px 5px;
//     cursor: pointer;
//     display: flex;
//     align-items: center;
//     justify-content: center;
// }
// .tool_icon{
//     display: inline-block;
//     width: 10px;
//     height: 10px;
//     line-height: 9px;
//     border-radius: 10px;
//     font-size: 10px;
//     text-align: center;
//     background-color: red;
//     color: red;
// }
// .fy_top_toolItem:hover .tool_icon{
//     color:#fff;
// }

`




QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址