你真的认为Google翻译不影响"前端"页面功能吗?

前端阳光

共 8036字,需浏览 17分钟

 ·

2021-12-11 15:21

点击上方 前端阳光,关注公众号

回复加群,加入技术交流群交流群


背景

又是一个时光飞逝的工作日, QA同学突然提出bug, 文本输入框的"计数"功能失效, 经过大家多方查找最后发现竟然是因为测试同学开启了"谷歌翻译"造成的无法"计数", 这就引起了我的浓厚兴趣, 到底这个看起来"人畜无害"的翻译功能是如何影响了我的输入框? 魏蜀吴争霸从此揭开序幕,啊~额(中箭)。

这篇文章是按我的探究过程编写的, 所以并不是一下就找到正确的方向, 跟着我当时的思路一起探索吧。

一、dom结构的改变 (以react代码为例)

想要知道为什么"翻译功能"会影响页面, 要从观察每次"翻译"功能生效时页面结构的变化开始, 最为简单的一段代码:

return ( <div>你好</div> )

翻译前:

翻译后:

查询一下font标签的特性, 竟然已经不建议使用了:

尝试一下是否可以获取到这个font标签元素, 将代码改装一下

 function handleShowDom({
    console.log(document.getElementById("box").children);
  }

 return
   <div id="box" onClick={handleShowDom}>
    你好
   </div>

`` )

可以获取到这个元素就有意思了, 如果某些情况下代码里面存在通过获取子元素进行操作的逻辑, 那么理论上就会出问题啊。

难道是由于dom结构的改变, 导致的文本输入框的计数功能会消失吗?

二、对比vue与react各类ui库

通过查看element ui的源码没有发现特殊的代码逻辑, 那究竟是怎么回事? 满心的疑问让我萌生一种想法, 难道跟vue与react框架本身的原理有关? 那么接下来我会分别实验4种ui框架的效果:

1: react -> antd (无bug

2: react -> arco (有bug)

3: vue -> element(有bug)      这个不用演示了, 开局就是靠它。

4: vue -> iview(有bug)

上述只有 antd是完美解决了这个bug (必须: respect), 那必须要研究一下它是如何"力压群芳"的呢?

三、深究textarea

既然是这个输入框元素发生的问题, 那么我们就从它开始吧, 一起看一下element-ui与antd分别产生的textarea元素长什么样子:

区别还是比较明显的, element-ui的内容信息不是放在标签内部的, 因为我们在dom结构的视角无法查看到内容, 但是antd恰恰相反, 这也许可以成为一个突破口。

那我们把"火力"集中在<textarea>身上, 看看它到底有多少种赋值写法:

  return (
    <div className="App">
      <textarea rows="5" cols="40">
        内容1
      </textarea>
      <br />
      <textarea rows="5" cols="40" value="内容2"></textarea>
      <br />
      <textarea rows="5" cols="40" defaultValue="内容3"></textarea>
   </div>

很奇妙, 上述三种写法都会产生同样的dom结构, 那么就比较好奇是什么样的写法可以隐藏标签内的文字? 我就尝试了一下js指定value的方式:

  useEffect(() => {
    document.getElementById("wrap").value = "你好世界";
  }, []);

  return (
    <div className="App">
      <textarea id="wrap" rows="5" cols="40">
        内容4
      </textarea>
   </div>

 )

果然问题出在这里, dom结构内保存的居然是初始值, 外界显示的是正确的值, 除了这些还有什么区别那? 我们把相关的值都打印出来吧:

vs🌩

真是不研究不知道, 原来同样的组件效果不同的库实现出来差这么多, 感觉属性值差的还挺多的。

但是通过实验发现, 这些属性并不是这个bug的直接原因, 我们还需要深入源码进行探究, 此时我意识到这个问题可能不是elemnet的责任。

四、谷歌翻译是否影响vue

经过多次尝试连自己都不敢相信, 竟然vue的计算属性等双向绑定的数据会在翻译后失效:

  <template>
  <div id="app">
    <button @click="handleClick">你好: {{n}}</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods:{
    handleClick(){
      this.n += 1;
      console.log(this.n)
    }
  },
  data(){
    return {
      n:1
    }
  },
}
</script>

上面可以看出vue的变量已经变化, 只是由于dom结构的改变导致了页面无法被正确更新。

这是一个很危险的隐患, 因为有的用户可能会选择"总是翻译", 这就导致页面功能完全乱掉了啊!

五、谷歌翻译是否影响react

既然react的arco框架会出问题, 那么react框架也一定会被影响, 让我们尝试一下什么情况下会出bug, 翻译之后点击:

  import { useState } from "react";
import "./App.css";

function App() {
  const [n, setN] = useState(0);

  function handleClick() {
    setN(n + 1);
    console.log(n + 1);
  }
  return <div onClick={handleClick}>你好:{n}</div>;
}

export default App;

如果改成则不会受影响:

<div onClick={handleClick}>{n}</div>;

六、受影响范围 (以react的写法为例)

通过观察antd与element-ui实现的不同我发现, antd是使用after伪类类做的, 难道此事与伪类有关? 这也证明了某些情况是不受影响的, 让我们实验一下谷歌翻译都影响哪些写法:

1: 拼接文本

不受影响的写法, 甚至这种写法每次n发生变化, 会移除font标签的dom结构。

<div>{n}</div>;

受影响的写法主要是拼接的文本。

<div>你好 {n}</div>;

<div>{n} {n}</div>;

2: 伪元素(不会被翻译) 空空的dom

<div className={"box"}>.</div>

定义伪元素

.box {
  height: 10px;
  width: 10px;
  border: 1px solid red;
}
.box::after {
  content: 'hello';
  display: inline-block;
}

所以antd很可能是因为伪元素才没有出bug, 如果antd不是无意之举那就太细节了。

3: 属性(非常特殊)

之所以说它非常特殊, 是因为属性是否被翻译需要分两种情况讨论, 是否有文本元素, 并且这个文本不能是input内部的文本。

第一种情况: 页面只有一个输入框

<div>
  <span>
    <input
      type="Please enter content"
      value={value}
      onChange={change}
      placeholder={"Please enter content"}
    />
  </span>
</div>

显而易见, 这个输入框没有被翻译:

第二种情况: 随意添加一个字符串

<div>
  <span>你好</span>
  <span>
    <input
      type="Please enter content"
      value={value}
      onChange={change}
      placeholder={"Please enter content"}
    />
  </span>
</div>

成功进行了翻译并且像placeholder这种可能会展示给用户的属性也被翻译了, 其余功能性的属性并不会被翻译。

<span xxx={"xxxxxxxxx hello"}>你好</span>

七、如何屏蔽翻译

为防止因谷歌翻译引起不良体验, 只需要在html标签上添加translate="no"属性即可屏蔽掉翻译功能:

八、检测被翻译成了什么

其实存在一部分用户默认就开启谷歌翻译功能的, 更有甚者你做的是国际化项目, 不同语言的人使用你的网站更有可能使用默认的谷歌翻译, 那么不编直接屏蔽的情况下, 我们要至少要监听一下用户都将我们的网站翻译成了什么语言? 这样也方便我们日后对网站的i18n进行优化。

比如我们网站自己没有韩语的翻译, 但是经常被用过翻译成韩语, 那么我们可以考虑增加韩语呢?

再比如我们虽然提供了韩语的翻译, 但是用户仍然主动将网站翻译成韩语, 那就要考虑是不是我们可以将已有的翻译功能, 更明确的提示给用户使用? 毕竟我们自带的翻译更准确体验也更好。

1: MutationObserver 简介

可以监控dom元素本身的所有改变, MutationObserver实例化后要传入一个配置项, 这个配置项可以自定义需要监听dom的哪些变化, 下面列出主要的几个属性:

attributes 布尔值, 监测dom的属性变化

attributeFilter 数组, 监测dom的具体某个属性的变化, 填写这个参数就不用每次判断哪个属性变化了

childList布尔值, 子元素的变化

characterData布尔值, 监视指定目标节点或子节点树中节点所包含的字符数据的变化

需要注意的是: childList,attributes 或者 characterData 三个属性之中,至少有一个必须为 true,否则会抛出 TypeError 异常。

2: 监控翻译的思路

由于每次谷歌翻译都会修改我们的<html lang="en">标签的lang属性, 所以我们就监控这个属性的变化, 并且由于哪怕当前是lang="en"谷歌翻译成英语还是lang="en", 也是会被检测到的 , 所以这个技术方案也是可行的。

新建一个listenerI18n.js文件, 已插件的形式引入即可:

(function ({
  const oHtml = document.getElementsByTagName("html")[0];
  const observer = new window.MutationObserver((mutations) => {
    console.log(`上报: 翻译为  ${oHtml.getAttribute("lang")}`);
  });
  observer.observe(oHtml, { attributestrueattributeFilter: ["lang"] });
})();

比较幸运的是就算你设置了

<html lang="en" translate="no">

禁止使用翻译功能, 上述方法仍能检测到用户想要用谷歌翻译翻译成什么语言。

九、总结

看似"人畜无害"的翻译功能, 背后竟然隐藏着各种深浅不一的"坑井", 假设在某些"金额结算页"用户使用了谷歌翻译, 那么是否会造成不小的问题, 所以最好的办法还是提供完善的"语言切换"功能才是王道啊。

end

这次就是这样, 希望与你一起进步。

原文:https://segmentfault.com/a/1190000041067692 作者:lulu_up

技术交流群


我组建了技术交流群,里面有很多 大佬,欢迎进来交流、学习、共建。回复加群即可。





   “分享、点赞在看” 支持一波👍


浏览 154
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报