网页翻译-扩展版本

给非中文的网页右下角添加一个google翻译图标对网页进行翻译,该版本主要适配手机浏览器: X浏览器、via浏览器等不支持油猴扩展的浏览器

// ==UserScript==
// @name         网页翻译-扩展版本
// @author       Kaiter-Plus
// @namespace    https://gitee.com/Kaiter-Plus/TampermonkeyScript/tree/master/Translate/Translate_ext.js
// @description  给非中文的网页右下角添加一个google翻译图标对网页进行翻译,该版本主要适配手机浏览器: X浏览器、via浏览器等不支持油猴扩展的浏览器
// @version      1.49
// @license      BSD-3-Clause
// @icon         https://www.google.cn/favicon.ico
// @include      *://*
// @run-at       document-end
// @noframes
// @note         2021/03/18 网页整页翻译功能,稍微调整了布局
// @note         2021/03/20 添加了排除网站的功能
// @note         2021/12/14 直接使用 https 获取谷歌翻译接口(防止有可能火狐浏览器无法用于翻译本地文件的bug)@古海沉舟
// @note         2022/10/05 由于谷歌关闭了国内的翻译接口,所以只能使用国际版的接口,现在使用脚本必须配合梯子
// ==/UserScript==

;(function () {
  'use strict'
  // 此处使用正则表达式排除不用进行翻译的网站
  const noTranslateDomain = [
    /^([0-9]+\.){3}[0-9]+/,
    /.*duyaoss\.com/,
    /.*lanzous\.com/,
    /.*w3school.*cn/,
    /.*iqiyi\.com/,
    /.*baidu.*/,
    /.*cnblogs\.com/,
    /.*csdn\.net/,
    /.*zhku\.edu\.cn/,
    /.*zhihuishu\.com/,
    /.*aliyuncs\.com/,
    /.*chaoxing\.com/,
    /.*youku\.com/,
    /.*examcoo\.com/,
    /.*mooc\.com/,
    /.*bilibili\.com/,
    /.*qq\.com/,
    /.*yy\.com/,
    /.*huya\.com/,
    /localhost/,
    /.*acfun\.cn/
  ]
  // 获取 head
  const head = document.head
  // 获取body
  const body = document.body
  // 获取当前页面的语言
  const lang = document.documentElement.lang.toLowerCase().substr(0, 2)
  // 获取网页使用的主要语言
  const mainLang = document.characterSet.toLowerCase().substr(0, 2)
  // 设置是否排除网站的标识
  let noTranslateDomainflag = false
  // 排除一些网站的翻译
  noTranslateDomain.every(reg => {
    if (reg.test(document.domain)) {
      noTranslateDomainflag = true
      return false
    } else {
      return true
    }
  })

  // 判断是不是中文,如果是则直接return,否则执行
  if (lang === 'zh' || mainLang === 'gb' || noTranslateDomainflag) {
    return
  } else {
    // 创建网页元素方法
    function createElement(html, nodeText, attr, parent) {
      const element = document.createElement(nodeText)
      if (attr) {
        element[attr] = html
      } else {
        element.innerHTML = html
      }
      parent.appendChild(element)
    }

    // 自定义样式,隐藏顶部栏
    createElement(
      `
      html,body{
        top: 0!important;
      }
      #google_translate_element {
        position: fixed;
        bottom: 60px;
        height: 21px;
        border-radius: 11px;
        left: 0px;
        transform: translateX(-75%);
        z-index: 10000000;
        overflow: hidden;
        box-shadow: 1px 1px 3px 0 #888;
        opacity: .5;
        transition: all .3s;
      }
      #google_translate_element .goog-te-gadget-simple {
        border: 0;
      }
      #google_translate_element .goog-te-gadget-simple span {
        margin-right: 0;
        border-radius: 11px;
      }
      #lb {
        display: inline-block;
      }
      .recoverPage {
        width: 4em;
        background-color: #fff;
        position: fixed;
        z-index: 10000000;
        bottom: 60px;
        right: 0px;
        transform: translateX(78%);
        user-select: none;
        text-align: center;
        font-size: small;
        line-height: 2em;
        border-radius: 1em;
        box-shadow: 1px 1px 3px 0 #888;
        opacity: .5;
        transition: all .3s;
      }
      #google_translate_element:hover, .recoverPage:hover {
        opacity: 1;
        transform: translateX(0);
      }
      .recoverPage:active {
        box-shadow: 1px 1px 3px 0 #888 inset;
      }
      #google_translate_element .goog-te-gadget-simple {
        width: 100%;
      }
      /* 隐藏移动端顶部栏 */
      [id=":1.container"].skiptranslate {
        display: none;
      }
      /* 隐藏 PC 端顶部栏 */
      [id=":2.container"].skiptranslate {
        display: none;
      }
      @media handheld, only screen and (max-width: 768px) {
        #google_translate_element {
          width: 104px;
        }
        #google_translate_element .goog-te-gadget>div:first-child {
          margin: 2px;
        }
        #google_translate_element .goog-te-combo {
          margin: 0;
          padding-top: 2px;
          border: none;
        }
        #goog-gt- {
          visibility: hidden!important;
          display: none!important;
        }
        .goog-text-highlight {
          background-color: inherit!important;
          box-shadow: 0 0 0 0 transparent!important;
        }
        .recoverPage {
          width: 2.8em;
          line-height: 2.8em;
          border-radius: 50%;
          opacity: .3;
          transform: translateX(0);
        }
        .recoverPage:hover {
          opacity: .3;
        }
      }
    `,
      'style',
      '',
      head
    )

    // 创建容器
    createElement('google_translate_element', 'div', 'id', body)

    // 初始化
    createElement(
      `function googleTranslateElementInit() {
        let google_translate_element = document.getElementById('google_translate_element')
        let timer = setInterval(function () {
          google_translate_element = document.getElementById('google_translate_element')
          if (google_translate_element) {
            clearInterval(timer)
            new google.translate.TranslateElement(
              {
                pageLanguage: 'auto',
                //包括的语言,中文简体,中文繁体,英语,日语,俄语
                includedLanguages: 'zh-CN,zh-TW,en,ja,ru',
                layout: /mobile/i.test(navigator.userAgent) ? 0 : 2,
              },
              'google_translate_element'
            )
            // 清除图片的请求,加快访问速度
            let img = [].slice.call(document.querySelectorAll('#goog-gt- img,#google_translate_element img'));
            img.forEach(function(v) {
              const a = v
              a.src = ''
              let b = a.outerHTML.replace(/<img(.*?)>/, () => {
                return '<span id="lb"' + RegExp.$1 +'></span>'
              })
              const c = document.createElement('div')
              c.innerHTML = b
              a.parentNode.insertBefore(c.children[0], a.parentNode.children[0])
              a.remove()
            });
            const recoverPage = document.createElement('div')
            recoverPage.setAttribute('class', 'notranslate recoverPage')
            recoverPage.innerText = '原'
            document.body.appendChild(recoverPage)
            // 点击恢复原网页
            recoverPage.onclick = () => {
              const phoneRecoverIframe = document.getElementById(':1.container') // 移动端
              const PCRecoverIframe = document.getElementById(':2.container') // PC端
              if (phoneRecoverIframe) {
                const recoverDocument = phoneRecoverIframe.contentWindow.document
                recoverDocument.getElementById(':1.restore').click()
              } else if (PCRecoverIframe) {
                const recoverDocument = PCRecoverIframe.contentWindow.document
                recoverDocument.getElementById(':2.restore').click()
              }
            }
          }
        }, 300)
      }`,
      'script',
      '',
      head
    )

    // 导入翻译接口
    createElement(
      'https://translate.google.com/translate_a/element.js?&cb=googleTranslateElementInit',
      'script',
      'src',
      head
    )

    // 排除一些代码的翻译
    const noTranslateArray = ['.bbCodeCode', 'tt', 'pre[translate="no"]']
    noTranslateArray.forEach(selectorName => {
      ;[...document.querySelectorAll(selectorName)].forEach(node => {
        if (node.className.indexOf('notranslate') === -1) {
          node.classList.add('notranslate')
        }
      })
    })
  }
})()