You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
<!DOCTYPEhtml><htmllang="en"><head><metacharset="utf-8"/><title>谷歌翻译服务Demo</title><metaname="referrer"content="no-referrer"/><metaname="viewport"content="width=device-width, initial-scale=1"/><style></style></head><body><divid="root"><divid="繁体">庫存</div><divid="简体">库存</div><divid="英语">in stock</div><divid="日本语">在庫あり</div><divid="韩语">재고</div><divid="google_translate_element"></div></div><script>
function googleTranslateElementInit() {// pageLanguage指定页面语言,如果指定为 auto,则告诉谷歌自动检测文字语言类型newgoogle.translate.TranslateElement({pageLanguage: 'auto'},"google_translate_element");}
(function () {vargtConstEvalStartTime=newDate();varh=this||self,l=/^[\w+/_-]+[=]{0,2}$/,m=null;functionn(a){return(a=a.querySelector&&a.querySelector("script[nonce]"))&&(a=a.nonce||a.getAttribute("nonce"))&&l.test(a)
? a
: "";}functionp(a,b){functionc(){}c.prototype=b.prototype;a.i=b.prototype;a.prototype=newc();a.prototype.constructor=a;a.h=function(g,f,k){for(vare=Array(arguments.length-2),d=2;d<arguments.length;d++)e[d-2]=arguments[d];returnb.prototype[f].apply(g,e);};}functionq(a){returna;}
function r(a) {if(Error.captureStackTrace)Error.captureStackTrace(this,r);else{varb=Error().stack;b&&(this.stack=b);}
a && (this.message = String(a));
}p(r,Error);r.prototype.name= "CustomError";functionu(a,b){a=a.split("%s");for(varc="",g=a.length-1,f=0;f<g;f++)c+=a[f]+(f<b.length ? b[f] : "%s");r.call(this,c+a[g]);}
p(u, r);
u.prototype.name = "AssertionError";
function v(a, b) {thrownewu("Failure"+(a ? ": "+a : ""),Array.prototype.slice.call(arguments,1));}
var w;
function x(a, b) {this.g=b===y ? a : "";}
x.prototype.toString = function () {returnthis.g+"";};
var y = {};
function z(a) {varb=document.getElementsByTagName("head")[0];b||(b=document.body.parentNode.appendChild(document.createElement("head")));b.appendChild(a);}
function _loadJs(a) {varb=document;varc="SCRIPT";"application/xhtml+xml"===b.contentType&&(c=c.toLowerCase());c=b.createElement(c);c.type="text/javascript";c.charset="UTF-8";if(void0===w){
b =null;varg=h.trustedTypes;if(g&&g.createPolicy){try{
b =g.createPolicy("goog#html",{createHTML: q,createScript: q,createScriptURL: q,});}catch(t){h.console&&h.console.error(t.message);}
w =b;}elsew=b;}a=(b=w) ? b.createScriptURL(a) : a;a=newx(a,y);a: {try{varf=c&&c.ownerDocument,k=f&&(f.defaultView||f.parentWindow);k=k||h;if(k.Element&&k.Location){
var e=k;breaka;}} catch (t) {}
e = null;
}if(e&&
"undefined" !=typeofe.HTMLScriptElement&&(!c||(!(cinstanceofe.HTMLScriptElement)&&(cinstanceofe.Location||cinstanceofe.Element)))){e=typeofc;if(("object"==e&&null!=c)||"function"==e)try{vard=c.constructor.displayName||c.constructor.name||Object.prototype.toString.call(c);} catch (t) {d="<object could not be stringified>";}
else
d = void 0 === c ? "undefined" : null === c ? "null" : typeof c;
v(
"Argument is not a %s (or a non-Element, non-Location mock); got: %s",
"HTMLScriptElement",
d
);
}ainstanceofx&&a.constructor===x
? (d=a.g)
: ((d=typeofa),v("expected object of type TrustedResourceUrl, got '"+a+"' of type "+("object"!=d
? d
: a
? Array.isArray(a)
? "array"
: d
: "null")),(d="type_error:TrustedResourceUrl"));c.src=d;(d=c.ownerDocument&&c.ownerDocument.defaultView)&&d!=h
? (d=n(d.document))
: (null===m&&(m=n(h.document)),(d=m));d&&c.setAttribute("nonce",d);z(c);}function_loadCss(a){varb=document.createElement("link");b.type="text/css";b.rel="stylesheet";b.charset="UTF-8";b.href=a;z(b);}function_isNS(a){a=a.split(".");for(varb=window,c=0;c<a.length;++c)if(!(b=b[a[c]]))return!1;return!0;}function_setupNS(a){a=a.split(".");for(varb=window,c=0;c<a.length;++c)b.hasOwnProperty
? b.hasOwnProperty(a[c])
? (b=b[a[c]])
: (b=b[a[c]]={})
: (b=b[a[c]]||(b[a[c]]={}));returnb;}window.addEventListener&&"undefined"==typeofdocument.readyState&&window.addEventListener("DOMContentLoaded",function(){document.readyState="complete";},!1);if(_isNS("google.translate.Element")){return;}(function(){varc=_setupNS("google.translate._const");c._cest=gtConstEvalStartTime;gtConstEvalStartTime=undefined;c._cl="en";c._cuc="googleTranslateElementInit";c._cac="";c._cam="";c._ctkk="448204.2198466445";varh="translate.googleapis.com";vars=(true
? "https"
: window.location.protocol=="https:"
? "https"
: "http")+"://";varb=s+h;c._pah=h;c._pas=s;c._pbi=b+"/translate_static/img/te_bk.gif";c._pci=b+"/translate_static/img/te_ctrl3.gif";c._pli=b+"/translate_static/img/loading.gif";c._plla=h+"/translate_a/l";c._pmi=b+"/translate_static/img/mini_google.png";c._ps=b+"/translate_static/css/translateelement.css";c._puh="translate.google.com";_loadCss(c._ps);_loadJs(b+"/translate_static/js/element/main_zh-CN.js");})();})();</script></body></html>
前言
本篇文章介绍如何白嫖谷歌翻译服务翻译浏览器网页,以及如何从压缩混淆后一万多行代码中探索 bug 的真相。压缩混淆后的源码调试面临以下挑战:
业务背景
在我们的业务场景中,有些文案是商家自己输入的,此时我们无法针对这些输入做多语言的转换,因此只能另辟蹊径,借助谷歌翻译服务。具体接入方式如下:
今天接到一个 bug,如果后端返回的页面中,文案是繁体字时,此时切换语言选择器,切换成简体中文,发现这部分文案没有被翻译
可以发现,当切换语言为简体中文时,只有繁体的字没有被翻译,其余的都被翻译成简体中文了。
如何从一万多行的压缩混淆的源码中探索真相
1. 首先在 DOM 上打个断点
谷歌在翻译我们的网页时,会自动在文本节点上插入
font
节点借助这一点,我们可以在有问题的 dom 上打个断点,看看谷歌是如何更新我们的 dom 节点的
然后切换语言,这里可以选择英语
谷歌在翻译时,会先移除节点再插入新的节点,因此这里我们可以直接跳过
一直跳过,直到函数执行到
cu
调用栈,此时观察浏览器页面会发现新的翻译节点in stock
已经插入进来因此有理由相信这个
cu
函数就是插入节点的函数。在这个函数入口处打个断点:此时将选择器切换成简体中文,会发现由于
a.K
为true
导致if
语句块没有执行,节点没有被翻译:那为什么 庫存 节点的
a.K
为true
,其他节点的就是false
呢?顺着调用栈往上找:
很明显,当断点执行到
v.jg
时,库存节点的K
还是false
,为啥函数执行完成,K
就变成了true
?这个方法肯定是做了某些操作。排查范围已经缩小到这个函数了。因此我们只需要一步步执行,最终执行到这里的时候发现:由此可以得出初步结论,当我们切换的语言为简体中文,即 "zh-CN" 时,并且和
h[2]
所设置的语言一致时,此时会将 K 设置为 true,并跳过我们的翻译。因此只需要排查h
是个什么东西。那
h[2]
又是什么呢??既然
c
是一个zu
类型的对象,那它一定是通过构造函数new zu
构造出来的,因此我们全局搜索new zu
并在这些语句的地方打个断点回到控制台,添加几行代码:
继续执行代码:
沿着
set
的调用栈往上找:于是在
send
函数的入口打一个断点可以知道
zh-CN
就是接口返回来的,因此我们去控制台查看网络请求:原来这是谷歌翻译接口返回来的标志,那这些标志代表什么?
回到我们的demo中
当我们初始化谷歌翻译实例时
如果指定的
pageLanguage
为 特定语言,比如en
,那么告诉谷歌只帮忙翻译页面中的英文单词,其余语言不用翻译。此时谷歌翻译接口返回的数据中不会带有语言标志,比如zh-CN
如果指定的
pageLanguage
为auto
,那么相当于告诉谷歌自定检测页面所有字的语言类型,并翻译成我选择的语言可以看到谷歌翻译接口返回了谷歌检测的原始语言类型。注意,这里
庫存
这个繁体单词,谷歌检测到的是zh-CN
,而不是zh-TW
!!!其余单词的检测均是正常的结论
pageLanguage
为auto
时,谷歌会自动检测页面单词的原始语言类型,并在翻译接口中返回对应的语言类型给前端。但是谷歌在检测中文繁体时,一律返回的是简体的标志zh-CN
,而不是zc-TW
zh-TW
切换成中文简体zh-CN
,谷歌翻译脚本会判断我们切换的语言zh-CN
和谷歌识别的语言是否相同,如果相同,则说明该节点不用翻译。举例如下:in stock
谷歌翻译接口返回的是en
,en
!==zh-CN
,因此这个节点会被翻译在庫あり
谷歌翻译接口返回的是ja
,ja
!==zh-CN
,因此这个节点也会被翻译库存
谷歌翻译接口返回的是zh-CN
,zh-CN
===zh-CN
,说明这个节点原本就是中文简体,然后我们切换的语言是中文简体,因此这个节点不需要翻译庫存
谷歌翻译接口返回的是zh-CN
而不是zh-TW
,zh-CN
===zh-CN
,导致这个繁体字没有被翻译。因此,这个说明谷歌翻译服务在检测繁体字时,是存在问题的。
The text was updated successfully, but these errors were encountered: