-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtranslate.js
More file actions
4965 lines (4413 loc) · 201 KB
/
translate.js
File metadata and controls
4965 lines (4413 loc) · 201 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
国际化,网页自动翻译。
作者:管雷鸣
开原仓库:https://github.com/xnx3/translate
*/
var translate = {
/*
* 当前的版本
*/
version:'3.2.3.20240403',
useVersion:'v2', //当前使用的版本,默认使用v2. 可使用 setUseVersion2(); //来设置使用v2 ,已废弃,主要是区分是否是v1版本来着,v2跟v3版本是同样的使用方式
setUseVersion2:function(){
translate.useVersion = 'v2';
console.log('提示:自 v2.10 之后的版本默认就是使用V2版本(当前版本为:'+translate.version+'), translate.setUseVersion2() 可以不用再加这一行了。当然加了也无所谓,只是加了跟不加是完全一样的。');
},
/*
* 翻译的对象,也就是 new google.translate.TranslateElement(...)
* 已废弃,v1使用的
*/
translate:null,
/*
* 支持哪些语言切换,包括:de,hi,lt,hr,lv,ht,hu,zh-CN,hy,uk,mg,id,ur,mk,ml,mn,af,mr,uz,ms,el,mt,is,it,my,es,et,eu,ar,pt-PT,ja,ne,az,fa,ro,nl,en-GB,no,be,fi,ru,bg,fr,bs,sd,se,si,sk,sl,ga,sn,so,gd,ca,sq,sr,kk,st,km,kn,sv,ko,sw,gl,zh-TW,pt-BR,co,ta,gu,ky,cs,pa,te,tg,th,la,cy,pl,da,tr
* 已废弃,请使用 translate.selectLanguageTag.languages
*/
includedLanguages:'zh-CN,zh-TW,en',
/*
* 资源文件url的路径
* 已废弃,v1使用的
*/
resourcesUrl:'//res.zvo.cn/translate',
/**
* 默认出现的选择语言的 select 选择框,可以通过这个选择切换语言。
*/
selectLanguageTag:{
/*
v3.1 增加,将 select切换语言的选择框赋予哪个id,这里是具体的id的名字。
如果这个id不存在,会创建这个id的元素
*/
documentId:'translate',
/* 是否显示 select选择语言的选择框,true显示; false不显示。默认为true */
show:true,
/*
支持哪些语言切换
v1.x 版本包括:de,hi,lt,hr,lv,ht,hu,zh-CN,hy,uk,mg,id,ur,mk,ml,mn,af,mr,uz,ms,el,mt,is,it,my,es,et,eu,ar,pt-PT,ja,ne,az,fa,ro,nl,en-GB,no,be,fi,ru,bg,fr,bs,sd,se,si,sk,sl,ga,sn,so,gd,ca,sq,sr,kk,st,km,kn,sv,ko,sw,gl,zh-TW,pt-BR,co,ta,gu,ky,cs,pa,te,tg,th,la,cy,pl,da,tr
v2.x 版本根据后端翻译服务不同,支持的语言也不同。具体支持哪些,可通过 http://api.translate.zvo.cn/doc/language.json.html 获取 (如果您私有部署的,将请求域名换为您自己私有部署的域名)
*/
languages:'',
alreadyRender:false, //当前是否已渲染过了 true为是 v2.2增加
selectOnChange:function(event){
var language = event.target.value;
translate.changeLanguage(language);
},
//重新绘制 select 语种下拉选择。比如进行二次开发过translate.js,手动进行了设置 translate.to ,但是手动改动后的,在select语种选择框中并不会自动进行改变,这是就需要手动重新绘制一下 select语种选择的下拉选择框
refreshRender:function(){
// 获取元素
let element = document.getElementById(translate.selectLanguageTag.documentId+"SelectLanguage");
// 删除元素
if (element) {
element.parentNode.removeChild(element);
}
//设置为未 render 状态,允许进行 render
translate.selectLanguageTag.alreadyRender = false;
translate.selectLanguageTag.render();
},
render:function(){ //v2增加
if(translate.selectLanguageTag.alreadyRender){
return;
}
translate.selectLanguageTag.alreadyRender = true;
//判断如果不显示select选择语言,直接就隐藏掉
if(!translate.selectLanguageTag.show){
return;
}
//判断translate 的id是否存在,不存在就创建一个
if(document.getElementById(translate.selectLanguageTag.documentId) == null){
var body_trans = document.getElementsByTagName('body')[0];
var div = document.createElement("div"); //创建一个script标签
div.id=translate.selectLanguageTag.documentId;
body_trans.appendChild(div);
}else{
//存在,那么判断一下 select是否存在,要是存在就不重复创建了
if(document.getElementById(translate.selectLanguageTag.documentId+'SelectLanguage') != null){
//select存在了,就不重复创建了
return;
}
}
//从服务器加载支持的语言库
translate.request.post(translate.request.api.language, {}, function(data){
if(data.result == 0){
console.log('load language list error : '+data.info);
return;
}
//select的onchange事件
var onchange = function(event){ translate.selectLanguageTag.selectOnChange(event); }
//创建 select 标签
var selectLanguage = document.createElement("select");
selectLanguage.id = translate.selectLanguageTag.documentId+'SelectLanguage';
selectLanguage.className = translate.selectLanguageTag.documentId+'SelectLanguage';
for(var i = 0; i<data.list.length; i++){
var option = document.createElement("option");
option.setAttribute("value",data.list[i].id);
//判断 selectLanguageTag.languages 中允许使用哪些
if(translate.selectLanguageTag.languages.length > 0){
//设置了自定义显示的语言
//都转小写判断
var langs_indexof = (','+translate.selectLanguageTag.languages+',').toLowerCase();
//console.log(langs_indexof)
if(langs_indexof.indexOf(','+data.list[i].id.toLowerCase()+',') < 0){
//没发现,那不显示这个语种,调出
continue
}
}
/*判断默认要选中哪个语言*/
if(translate.to != null && typeof(translate.to) != 'undefined' && translate.to.length > 0){
//设置了目标语言,那就进行判断显示目标语言
if(translate.to == data.list[i].id){
option.setAttribute("selected",'selected');
}
}else{
//没设置目标语言,那默认选中当前本地的语种
if(data.list[i].id == translate.language.getLocal()){
option.setAttribute("selected",'selected');
}
}
option.appendChild(document.createTextNode(data.list[i].name));
selectLanguage.appendChild(option);
}
//增加 onchange 事件
if(window.addEventListener){ // Mozilla, Netscape, Firefox
selectLanguage.addEventListener('change', onchange,false);
}else{ // IE
selectLanguage.attachEvent('onchange',onchange);
}
//将select加入进网页显示
document.getElementById(translate.selectLanguageTag.documentId).appendChild(selectLanguage);
/*
try{
document.getElementById('translateSelectLanguage').style.width = '94px';
}catch(e){ console.log(e);}
*/
});
}
},
/*
* 当前本地语言
* 已废弃,v1使用的
*/
//localLanguage:'zh-CN',
localLanguage:'zh-CN',
/**
* google翻译执行的
* 已废弃,v1使用的
*/
googleTranslateElementInit:function(){
var selectId = '';
if(document.getElementById('translate') != null){ // && document.getElementById('translate').innerHTML.indexOf('translateSelectLanguage') > 0
//已经创建过了,存在
selectId = 'translate';
}
translate.translate = new google.translate.TranslateElement(
{
//这参数没用,请忽略
pageLanguage: 'zh-CN',
//一共80种语言选择,这个是你需要翻译的语言,比如你只需要翻译成越南和英语,这里就只写en,vi
//includedLanguages: 'de,hi,lt,hr,lv,ht,hu,zh-CN,hy,uk,mg,id,ur,mk,ml,mn,af,mr,uz,ms,el,mt,is,it,my,es,et,eu,ar,pt-PT,ja,ne,az,fa,ro,nl,en-GB,no,be,fi,ru,bg,fr,bs,sd,se,si,sk,sl,ga,sn,so,gd,ca,sq,sr,kk,st,km,kn,sv,ko,sw,gl,zh-TW,pt-BR,co,ta,gu,ky,cs,pa,te,tg,th,la,cy,pl,da,tr',
includedLanguages: translate.selectLanguageTag.languages,
//选择语言的样式,这个是面板,还有下拉框的样式,具体的记不到了,找不到api~~
layout: 0,
//自动显示翻译横幅,就是翻译后顶部出现的那个,有点丑,设置这个属性不起作用的话,请看文章底部的其他方法
//autoDisplay: false,
//disableAutoTranslation:false,
//还有些其他参数,由于原插件不再维护,找不到详细api了,将就了,实在不行直接上dom操作
},
selectId //触发按钮的id
);
},
/**
* 初始化,如加载js、css资源
* 已废弃,v1使用的
*/
/* v2.11.11.20240124 彻底注释掉,有新的init方法替代
init:function(){
var protocol = window.location.protocol;
if(window.location.protocol == 'file:'){
//本地的,那就用http
protocol = 'http:';
}
if(this.resourcesUrl.indexOf('://') == -1){
//还没设置过,进行设置
this.resourcesUrl = protocol + this.resourcesUrl;
}
//this.resourcesUrl = 'file://G:/git/translate';
},
*/
/**
* 执行翻译操作
* 已废弃,v1使用的
*/
execute_v1:function(){
console.log('=====ERROR======');
console.log('The v1 version has been discontinued since 2022. Please use the latest V3 version and refer to: http://translate.zvo.cn/41162.html');
},
/**
* 设置Cookie,失效时间一年。
* @param name
* @param value
* * 已废弃,v1使用的
*/
setCookie:function (name,value){
var cookieString=name+"="+escape(value);
document.cookie=cookieString;
},
//获取Cookie。若是不存再,返回空字符串
//* 已废弃,v1使用的
getCookie:function (name){
var strCookie=document.cookie;
var arrCookie=strCookie.split("; ");
for(var i=0;i<arrCookie.length;i++){
var arr=arrCookie[i].split("=");
if(arr[0]==name){
return unescape(arr[1]);
}
}
return "";
},
/**
* 获取当前页面采用的是什么语言
* 返回值如 en、zh-CN、zh-TW (如果是第一次用,没有设置过,那么返回的是 translate.localLanguage 设置的值)
* * 已废弃,v1使用的
*/
currentLanguage:function(){
//translate.check();
var cookieValue = translate.getCookie('googtrans');
if(cookieValue.length > 0){
return cookieValue.substr(cookieValue.lastIndexOf('/')+1,cookieValue.length-1);
}else{
return translate.localLanguage;
}
},
/**
* 切换语言,比如切换为英语、法语
* @param languageName 要切换的语言语种。传入如 english
* 会自动根据传入的语言来判断使用哪种版本。比如传入 en、zh-CN 等,则会使用v1.x版本
* 传入 chinese_simplified 、english 等,则会使用 v2.x版本
*/
changeLanguage:function(languageName){
//判断使用的是否是v1.x
var v1 = ',en,de,hi,lt,hr,lv,ht,hu,zh-CN,hy,uk,mg,id,ur,mk,ml,mn,af,mr,uz,ms,el,mt,is,it,my,es,et,eu,ar,pt-PT,ja,ne,az,fa,ro,nl,en-GB,no,be,fi,ru,bg,fr,bs,sd,se,si,sk,sl,ga,sn,so,gd,ca,sq,sr,kk,st,km,kn,sv,ko,sw,gl,zh-TW,pt-BR,co,ta,gu,ky,cs,pa,te,tg,th,la,cy,pl,da,tr,';
if(v1.indexOf(','+languageName+',') > -1){
//用的是v1.x
console.log('您使用的是v1版本的切换语种方式,v1已在2021年就以废弃,请更换为v2,参考文档: http://translate.zvo.cn/41549.html');
translate.check();
var googtrans = '/'+translate.localLanguage+'/'+languageName;
//先清空泛解析域名的设置
var s = document.location.host.split('.');
if(s.length > 2){
var fanDomain = s[s.length-2]+'.'+s[s.length-1];
document.cookie = 'googtrans=;expires='+(new Date(1))+';domain='+fanDomain+';path=/';
document.cookie = 'googtrans='+googtrans+';domain='+fanDomain+';path=/';
}
translate.setCookie('googtrans', ''+googtrans);
location.reload();
return;
}
//用的是v2.x或更高
//translate.setUseVersion2();
translate.useVersion = 'v2';
//判断是否是第一次翻译,如果是,那就不用刷新页面了。 true则是需要刷新,不是第一次翻译
if(translate.to != null && translate.to.length > 0){
//当前目标值有值,且目标语言跟当前语言不一致,那当前才是已经被翻译过的
if(translate.to != translate.language.getLocal()){
var isReload = true; //标记要刷新页面
}
}
translate.to = languageName;
translate.storage.set('to',languageName); //设置目标翻译语言
if(isReload){
location.reload(); //刷新页面
}else{
//不用刷新,直接翻译
translate.execute(); //翻译
}
},
/**
* 自检提示,适用于 v1.x, 在 v2.x中已废弃
* english
* 已废弃,v1使用的
*/
check:function(){
if(window.location.protocol == 'file:'){
console.log('\r\n---WARNING----\r\ntranslate.js 主动翻译组件自检异常,当前协议是file协议,翻译组件要在正常的线上http、https协议下才能正常使用翻译功能\r\n------------');
}
},
/**************************** v2.0 */
to:'', //翻译为的目标语言,如 english 、chinese_simplified
//用户第一次打开网页时,自动判断当前用户所在国家使用的是哪种语言,来自动进行切换为用户所在国家的语种。
//如果使用后,第二次在用,那就优先以用户所选择的为主,这个就不管用了
//默认是false,不使用,可设置true:使用
//使用 setAutoDiscriminateLocalLanguage 进行设置
autoDiscriminateLocalLanguage:false,
documents:[], //指定要翻译的元素的集合,可设置多个,如设置: document.getElementsByTagName('DIV')
/*
v2.11.5增加
正在进行翻译的节点,会记录到此处。
这里是最底的节点了,不会再有下级了。这也就是翻译的最终节点,也就是 translate.element.findNode() 发现的节点
也就是扫描到要进行翻译的节点,在翻译前,加入到这里,在这个节点翻译结束后,将这里面记录的节点删掉。
格式如
[
{
node: node节点的对象
number: 2 (当前正在翻译进行中的次数,比如一个节点有中英文混合的文本,那么中文、英文 会同时进行两次翻译,也就是最后要进行两次替换,会导致这个node产生两次改动。每次便是+1、-1)
},
{
......
}
]
生命周期:
translate.execute() 执行后,会扫描要翻译的字符,扫描完成后首先会判断缓存中是否有,是否会命中缓存,如果缓存中有,那么在加入 task.add 之前就会将这个进行记录 ++
在浏览器缓存没有命中后,则会通过网络api请求进行翻译,此时在发起网络请求前,会进行记录 ++
当使用 translate.listener.start() 后,网页中动态渲染的部分会触发监听,触发监听后首先会判断这个节点是否存在于这里面正在被翻译,如果存在里面,那么忽略, 如果不存在里面,那么再进行 translate.execute(变动的节点) 进行翻译 (当然执行这个翻译后,自然也就又把它加入到此处进行记录 ++)
【唯一的减去操作】 在task.execute() 中,翻译完成并且渲染到页面执行完成后,会触发延迟50毫秒后将这个翻译的节点从这里减去
*/
inProgressNodes:[],
//翻译时忽略的一些东西,比如忽略某个tag、某个class等
ignore:{
tag:['style', 'script', 'link', 'pre', 'code'],
class:['ignore','translateSelectLanguage'],
id:[],
/*
传入一个元素,判断这个元素是否是被忽略的元素。 这个会找父类,看看父类中是否包含在忽略的之中。
return true是在忽略的之中,false不再忽略的之中
*/
isIgnore:function(ele){
if(ele == null || typeof(ele) == 'undefined'){
return false;
}
var parentNode = ele;
var maxnumber = 100; //最大循环次数,避免死循环
while(maxnumber-- > 0){
if(parentNode == null || typeof(parentNode) == 'undefined'){
//没有父元素了
return false;
}
//判断Tag
//var tagName = parentNode.nodeName.toLowerCase(); //tag名字,小写
var nodename = translate.element.getNodeName(parentNode).toLowerCase(); //tag名字,小写
if(nodename.length > 0){
//有nodename
if(nodename == 'body' || nodename == 'html' || nodename == '#document'){
//上层元素已经是顶级元素了,那肯定就不是了
return false;
}
if(translate.ignore.tag.indexOf(nodename) > -1){
//发现ignore.tag 当前是处于被忽略的 tag
return true;
}
}
//判断class name
if(parentNode.className != null){
var classNames = parentNode.className;
if(classNames == null || typeof(classNames) != 'string'){
continue;
}
//console.log('className:'+typeof(classNames));
//console.log(classNames);
classNames = classNames.trim().split(' ');
for(var c_index = 0; c_index < classNames.length; c_index++){
if(classNames[c_index] != null && classNames[c_index].trim().length > 0){
//有效的class name,进行判断
if(translate.ignore.class.indexOf(classNames[c_index]) > -1){
//发现ignore.class 当前是处于被忽略的 class
return true;
}
}
}
}
//判断id
if(parentNode.id != null && typeof(parentNode.id) != 'undefined'){
//有效的class name,进行判断
if(translate.ignore.id.indexOf(parentNode.id) > -1){
//发现ignore.id 当前是处于被忽略的 id
return true;
}
}
//赋予判断的元素向上一级
parentNode = parentNode.parentNode;
}
return false;
}
},
//自定义翻译术语
nomenclature:{
/*
术语表
一维:要转换的语种,如 english
二维:翻译至的目标语种,如 english
三维:要转换的字符串,如 "你好"
结果:自定义的翻译结果,如 “Hallo”
*/
data:new Array(),
/*
原始术语表,可编辑的
一维:要自定义目标词
二维:针对的是哪个语种
值:要翻译为什么内容
其设置如
var data = new Array();
data['版本'] = {
english : 'banben',
korean : 'BanBen'
};
data['国际化'] = {
english : 'guojihua',
korean : 'GuoJiHua'
};
【已过时】
*/
old_Data:[],
/*
set:function(data){
translate.nomenclature.data = data;
},
*/
set:function(data){
alert('请将 translate.nomenclature.set 更换为 append,具体使用可参考: https://github.com/xnx3/translate ');
},
/*
向当前术语库中追加自定义术语。如果追加的数据重复,会自动去重
传入参数:
from 要转换的语种
to 翻译至的目标语种
properties 属于配置表,格式如:
你好=Hello
世界=ShiJie
*/
append:function(from, to, properties){
if(typeof(translate.nomenclature.data[from]) == 'undefined'){
translate.nomenclature.data[from] = new Array();
}
if(typeof(translate.nomenclature.data[from][to]) == 'undefined'){
translate.nomenclature.data[from][to] = new Array();
}
//将properties进行分析
//按行拆分
var line = properties.split('\n');
//console.log(line)
for(var line_index = 0; line_index < line.length; line_index++){
var item = line[line_index].trim();
if(item.length < 1){
//空行,忽略
continue;
}
var kvs = item.split('=');
//console.log(kvs)
if(kvs.length != 2){
//不是key、value构成的,忽略
continue;
}
var key = kvs[0].trim();
var value = kvs[1].trim();
//console.log(key)
if(key.length == 0 || value.length == 0){
//其中某个有空,则忽略
continue;
}
//加入,如果之前有加入,则会覆盖
translate.nomenclature.data[from][to][key] = value;
//console.log(local+', '+target+', key:'+key+', value:'+value);
}
//追加完后,对整个对象数组进行排序,key越大越在前面
translate.nomenclature.data[from][to] = translate.util.objSort(translate.nomenclature.data[from][to]);
},
//获取当前定义的术语表
get:function(){
return translate.nomenclature.data;
},
//对传入的str字符进行替换,将其中的自定义术语提前进行替换,然后将替换后的结果返回
dispose:function(str){
if(str == null || str.length == 0){
return str;
}
//if(translate.nomenclature.data.length == 0){
// return str;
//}
//判断当前翻译的两种语种是否有自定义术语库
//console.log(typeof(translate.nomenclature.data[translate.language.getLocal()][translate.to]))
if(typeof(translate.nomenclature.data[translate.language.getLocal()]) == 'undefined' || typeof(translate.nomenclature.data[translate.language.getLocal()][translate.to]) == 'undefined'){
return str;
}
//console.log(str)
for(var originalText in translate.nomenclature.data[translate.language.getLocal()][translate.to]){
var translateText = translate.nomenclature.data[translate.language.getLocal()][translate.to][originalText];
if(typeof(translateText) == 'function'){
//进行异常的预处理调出
continue;
}
var index = str.indexOf(originalText);
if(index > -1){
//console.log('find -- '+originalText+', \t'+translateText);
if(translate.language.getLocal() == 'english'){
//如果本地语种是英文,那么还要判断它的前后,避免比如要替换 is 将 display 中的is给替换,将单词给强行拆分了
//判断这个词前面是否符合
var beforeChar = ''; //前面的字符
if(index == 0){
//前面没别的字符了,那前面合适
}else{
//前面有别的字符,判断是什么字符,如果是英文,那么这个是不能被拆分的,要忽略
beforeChar = str.substr(index-1,1);
//console.log('beforeChar:'+beforeChar+', str:'+str)
var lang = translate.language.getCharLanguage(beforeChar);
//console.log(lang);
if(lang == 'english'){
//调出,不能强拆
continue;
}
}
//判断这个词的后面是否符合
var afterChar = ''; //后面的字符
if(index + originalText.length == str.length ){
//后面没别的字符了,那前面合适
//console.log(originalText+', meile '+str)
}else{
//后面有别的字符,判断是什么字符,如果是英文,那么这个是不能被拆分的,要忽略
afterChar = str.substr(index+originalText.length,1);
var lang = translate.language.getCharLanguage(afterChar);
if(lang == 'english'){
//跳出,不能强拆
continue;
}
}
str = str.replace(new RegExp(beforeChar+originalText+afterChar,'g'), beforeChar+translateText+afterChar);
}else{
//其他情况,如汉语、汉语等语种
str = str.replace(new RegExp(originalText,'g'), translateText);
}
}
}
return str;
/*
//遍历一维
for(var originalText in translate.nomenclature.data){
var languageResult = translate.nomenclature.data[originalText];
if(typeof(languageResult) == 'function'){
//进行异常的预处理调出
continue;
}
if(typeof(languageResult[translate.to]) == 'undefined'){
//console.log('und');
continue;
}
//var hash = translate.util.hash(originalText);
//console.log(originalText+',\t'+str);
if(str.indexOf(originalText) > -1){
//console.log('find -- '+originalText+', \t'+languageResult[translate.to]);
str = str.replace(new RegExp(originalText,'g'),languageResult[translate.to]);
}
}
return str;
*/
},
},
office:{
/*
网页上翻译之后,自动导出当前页面的术语库
需要先指定本地语种,会自动将本地语种进行配置术语库
*/
export:function(){
if(translate.language.getLocal() == translate.language.getCurrent()){
alert('本地语种跟要翻译的语种一致,无需导出');
return;
}
var text = '';
for(var uuid in translate.nodeQueue){
var queueValue = translate.nodeQueue[uuid];
for(var lang in translate.nodeQueue[uuid].list){
//console.log('------'+lang)
if(typeof(lang) != 'string' || lang.length < 1){
continue;
}
//if(translate.language.getLocal() == lang){
//console.log(translate.nodeQueue[uuid].list[lang]);
for(var hash in translate.nodeQueue[uuid].list[lang]){
//console.log(translate.nodeQueue[uuid].list[lang][hash].original);
//console.log(translate.nodeQueue[uuid].list[lang][hash].original);
text = text + '\n' + translate.nodeQueue[uuid].list[lang][hash].original + '='+translate.storage.get('hash_'+translate.language.getCurrent()+'_'+hash);
}
//}
}
}
if(text.length > 0){
//有内容
text = 'translate.office.append(\''+translate.language.getCurrent()+'\',`'+text+'\n`);';
//console.log(text);
translate.util.loadMsgJs();
msg.popups({
text:'<textarea id="msgPopupsTextarea" style="width:100%; height:100%; color: black; padding: 8px;">loaing...</textarea>',
width:'750px',
height:'600px',
padding:'1px',
});
document.getElementById('msgPopupsTextarea').value = text;
}else{
msg.alert('无有效内容!');
}
},
//显示导出面板
showPanel:function(){
let panel = document.createElement('div');
panel.setAttribute('id', 'translate_export');
panel.setAttribute('class','ignore');
//导出按钮
let button = document.createElement('button');
button.onclick = function() {
translate.office.export();
};
button.innerHTML = '导出配置信息';
button.setAttribute('style', 'margin-left: 72px; margin-top: 30px; margin-bottom: 20px; font-size: 25px; background-color: blue; padding: 15px; padding-top: 3px; padding-bottom: 3px; border-radius: 3px;');
panel.appendChild(button);
//说明文字
let textdiv = document.createElement('div');
textdiv.innerHTML = '1. 首先将当前语种切换为你要翻译的语种<br/>2. 点击导出按钮,将翻译的配置信息导出<br/>3. 将导出的配置信息粘贴到代码中,即可完成<br/><a href="asd" target="_black" style="color: aliceblue;">点此进行查阅详细使用说明</a>';
textdiv.setAttribute('style','font-size: 14px; padding: 12px;');
panel.appendChild(textdiv);
panel.setAttribute('style', 'background-color: black; color: #fff; width: 320px; height: 206px; position: fixed; bottom: 50px; right: 50px;');
//把元素节点添加到body元素节点中成为其子节点,放在body的现有子节点的最后
document.body.appendChild(panel);
translate.util.loadMsgJs();
},
/*
追加离线翻译数据。如果追加的数据重复,会自动去重
传入参数:
from 要转换的语种
to 翻译至的目标语种
properties 属于配置表,格式如:
你好=Hello
世界=ShiJie
这个传入参数跟 translate.nomenclature.append 的传入参数格式是一致的
*/
append:function(to, properties){
//console.log(properties)
//将properties进行分析
//按行拆分
var line = properties.split('\n');
//console.log(line)
for(var line_index = 0; line_index < line.length; line_index++){
var item = line[line_index].trim();
if(item.length < 1){
//空行,忽略
continue;
}
var kvs = item.split('=');
//console.log(kvs)
if(kvs.length != 2){
//不是key、value构成的,忽略
continue;
}
var key = kvs[0];
var value = kvs[1];
//console.log(key)
if(key.length == 0 || value.length == 0){
//其中某个有空,则忽略
continue;
}
//console.log('set---'+key);
//加入 storate
translate.storage.set('hash_'+to+'_'+translate.util.hash(key), value);
}
},
},
setAutoDiscriminateLocalLanguage:function(){
translate.autoDiscriminateLocalLanguage = true;
},
/*
待翻译的页面的node队列
一维:key:uuid,也就是execute每次执行都会创建一个翻译队列,这个是翻译队列的唯一标识。
value:
k/v
二维:对象形态,具体有:
key:expireTime 当前一维数组key的过期时间,到达过期时间会自动删除掉这个一维数组。如果<0则代表永不删除,常驻内存
value:list 待翻译的页面的node队列
三维:针对二维的value, key:english、chinese_simplified等语种,这里的key便是对value的判断,取value中的要翻译的词是什么语种,对其进行了语种分类 value: k/v
四维:针对三维的value, key:要翻译的词(经过语种分割的)的hash, value: node数组
五维:针对四维的value, 这是个对象, 其中
original: 是三维的key的hash的原始文字,也就是 node 中的原始文字。
cacheHash: 如果翻译时匹配到了自定义术语库中的词,那么翻译完后存入到缓存中时,其缓存的翻译前字符串已经不是original,而是匹配完术语库后的文本的hash了。所以这里额外多增加了这个属性。如果匹配了术语库,那这里就是要进行缓存的翻译前文本的hash,如果未使用术语库,这里就跟其key-hash 相同。
translateText: 针对 original 的经过加工过的文字,比如经过自定义术语操作后的,待翻译的文字。
nodes: 有哪些node元素中包含了这个词,都会在这里记录
六维:针对五维的 nodes,将各个具体的 node 以及 其操作的 attribute 以数组形式列出
七维:针对六维列出的nodes数组,其中包含:
node: 具体操作的node元素
attribute: 也就是翻译文本针对的是什么,是node本身(nodeValue),还是 node 的某个属性,比如title属性,这则是设置为 "title"。如果这里不为空,那就是针对的属性操作的。 如果这里为空或者undefined ,那就是针对node本身,也就是 nodeValue 的字符串操作的
beforeText: node元素中进行翻译结果赋予时,额外在翻译结果的前面加上的字符串。其应用场景为,如果中英文混合场景下,避免中文跟英文挨着导致翻译为英语后,连到一块了。默认是空字符串 ''
afterText: node元素中进行翻译结果赋予时,额外在翻译结果的后面加上的字符串。其应用场景为,如果中英文混合场景下,避免中文跟英文挨着导致翻译为英语后,连到一块了。默认是空字符串 ''
生命周期: 当execute()执行时创建, 当execute结束(其中的所有request接收到响应并渲染完毕)时销毁(当前暂时不销毁,以方便调试)
*/
nodeQueue:{},
//指定要翻译的元素的集合,可传入一个元素或多个元素
//如设置一个元素,可传入如: document.getElementsById('test')
//如设置多个元素,可传入如: document.getElementsByTagName('DIV')
setDocuments:function(documents){
if (documents == null || typeof(documents) == 'undefined') {
return;
}
if(typeof(documents.length) == 'undefined'){
//不是数组,是单个元素
translate.documents[0] = documents;
}else{
//是数组,直接赋予
translate.documents = documents;
}
//清空翻译队列,下次翻译时重新检索
translate.nodeQueue = {};
//console.log('set documents , clear translate.nodeQueue');
},
//获取当前指定翻译的元素(数组形式 [document,document,...])
//如果用户未使用setDocuments 指定的,那么返回整个网页
getDocuments:function(){
if(translate.documents != null && typeof(translate.documents) != 'undefined' && translate.documents.length > 0){
// setDocuments 指定的
return translate.documents;
}else{
//未使用 setDocuments指定,那就是整个网页了
return document.all; //翻译所有的
}
},
listener:{
//当前页面打开后,是否已经执行完execute() 方法进行翻译了,只要执行完一次,这里便是true。 (多种语言的API请求完毕并已渲染html)
//isExecuteFinish:false,
//是否已经使用了 translate.listener.start() 了,如果使用了,那这里为true,多次调用 translate.listener.start() 只有第一次有效
isStart:false,
//translate.listener.start(); //开启html页面变化的监控,对变化部分会进行自动翻译。注意,这里变化区域,是指使用 translate.setDocuments(...) 设置的区域。如果未设置,那么为监控整个网页的变化
start:function(){
translate.temp_linstenerStartInterval = setInterval(function(){
if(document.readyState == 'complete'){
//dom加载完成,进行启动
clearInterval(translate.temp_linstenerStartInterval);//停止
translate.listener.addListener();
}
//if(translate.listener.isExecuteFinish){ //执行完过一次,那才能使用
/*if(translate.listener.isStart){
//已开启了
return;
}*/
//console.log('translate.temp_linstenerStartInterval Finish!');
//}
}, 300);
},
//增加监听,开始监听。这个不要直接调用,需要使用上面的 start() 开启
addListener:function(){
translate.listener.isStart = true; //记录已执行过启动方法了
// 观察器的配置(需要观察什么变动)
translate.listener.config = { attributes: true, childList: true, subtree: true, characterData: true, attributeOldValue:true, characterDataOldValue:true };
// 当观察到变动时执行的回调函数
translate.listener.callback = function(mutationsList, observer) {
var documents = []; //有变动的元素
//console.log('--------- lisetner 变动');
// Use traditional 'for loops' for IE 11
for(let mutation of mutationsList) {
let addNodes = [];
if (mutation.type === 'childList') {
if(mutation.addedNodes.length > 0){
//多了组件
addNodes = mutation.addedNodes;
//documents.push.apply(documents, mutation.addedNodes);
}else if(mutation.removedNodes.length > 0){
//console.log('remove:');
//console.log(mutation.removedNodes);
}else{
//console.log('not find:');
//console.log(mutation);
}
}else if (mutation.type === 'attributes') {
//console.log('The ' + mutation.attributeName + ' attribute was modified.');
}else if(mutation.type === 'characterData'){
//内容改变
addNodes = [mutation.target];
//documents.push.apply(documents, [mutation.target]);
}
//去重并加入 documents
for(let item of addNodes){
//console.log(item);
//判断是否已经加入过了,如果已经加入过了,就不重复加了
var isFind = false;
for(var di = 0; di < documents.length; di++){
if(documents[di].isSameNode(item)){
isFind = true;
break;
}
}
if(isFind){
break;
}
documents.push.apply(documents, [item]);
}
}
//console.log(documents.length);
if(documents.length > 0){
//有变动,需要看看是否需要翻译,延迟10毫秒执行
//判断是否属于在正在翻译的节点,重新组合出新的要翻译的node集合
var translateNodes = [];
//console.log(translate.inProgressNodes.length);
for(let ipnode of documents){
//console.log('---type:'+ipnode.nodeType);
var find = false;
for(var ini = 0; ini < translate.inProgressNodes.length; ini++){
if(translate.inProgressNodes[ini].node.isSameNode(ipnode)){
//有记录了,那么忽略这个node,这个node是因为翻译才导致的变动
//console.log('发现相同');
find = true;
break;
}
}
if(find){
continue;
}
//不相同,才追加到新的 translateNodes
translateNodes.push(ipnode);
//console.log('listener ++ '+ipnode.nodeValue);
//console.log(ipnode);
}
if(translateNodes.length < 1){
return;
}
//console.log('translateNodeslength: '+translateNodes.length);
setTimeout(function() {
//console.log(translateNodes);
translate.execute(translateNodes); //指定要翻译的元素的集合,可传入一个或多个元素。如果不设置,默认翻译整个网页
}, 10); //这个要比 task.execute() 中的 settimeout 延迟执行删除 translate.inpr.....nodes 的时间要小,目的是前一个发生变动后,记入 inpr...nodes 然后翻译完成后节点发生变化又触发了listener,此时 inpr....nodes 还有,那么这个变化将不做处理,然后 inp.....nodes 再删除这个标记
}
};
// 创建一个观察器实例并传入回调函数
translate.listener.observer = new MutationObserver(translate.listener.callback);
// 以上述配置开始观察目标节点
var docs = translate.getDocuments();
for(var docs_index = 0; docs_index < docs.length; docs_index++){
var doc = docs[docs_index];
if(doc != null){
translate.listener.observer.observe(doc, translate.listener.config);
}
}
},
/*
每当执行完一次渲染任务(翻译)时会触发此。注意页面一次翻译会触发多个渲染任务。普通情况下,一次页面的翻译可能会触发两三次渲染任务。
另外如果页面中有ajax交互方面的信息,时,每次ajax信息刷新后,也会进行翻译,也是一次渲染任务。
这个是为了方便扩展使用。比如在layui中扩展,监控 select 的渲染
*/
renderTaskFinish:function(renderTask){
//console.log(renderTask);
}
},
//对翻译结果进行替换渲染的任务,将待翻译内容替换为翻译内容的过程
renderTask:class{
constructor(){
/*
* 任务列表
* 一维数组 [hash] = tasks; tasks 是多个task的数组集合
* 二维数组 [task,task,...],存放多个 task,每个task是一个替换。这里的数组是同一个nodeValue的多个task替换
* 三维数组 task['originalText'] 、 task['resultText'] 存放要替换的字符串
task['attribute'] 存放要替换的属性,比如 a标签的title属性。 如果是直接替换node.nodeValue ,那这个没有
*/
this.taskQueue = [];
/*
* 要进行翻译的node元素,
* 一维数组 key:node.nodeValue 的 hash , value:node的元素数组
* 二维数组,也就是value中包含的node集合 [node,node,...]
*/
this.nodes = [];
}
/**
* 向替换队列中增加替换任务
* node:要替换的字符属于那个node元素
* originalText:待翻译的字符
* resultText:翻译后的结果字符
* attribute: 要替换的是哪个属性,比如 a标签的title属性,这里便是传入title。如果不是替换属性,这里不用传入,或者传入null
*/
add(node, originalText, resultText, attribute){
var nodeAnaly = translate.element.nodeAnalyse.get(node, attribute); //node解析
//var hash = translate.util.hash(translate.element.getTextByNode(node)); //node中内容的hash
var hash = translate.util.hash(nodeAnaly['text']);
//console.log('--------------'+hash);
//console.log(nodeAnaly);
//console.log(node);
//console.log('originalText:'+originalText+', resultText:'+resultText+', attribute:'+attribute);
/****** 加入翻译的元素队列 */
if(typeof(this.nodes[hash]) == 'undefined'){
this.nodes[hash] = new Array();
}
this.nodes[hash].push(node);
//console.log(node)
/****** 加入翻译的任务队列 */
var tasks = this.taskQueue[hash];
if(tasks == null || typeof(tasks) == 'undefined'){
//console.log(node.nodeValue);
tasks = new Array(); //任务列表,存放多个 task,每个task是一个替换。这里的数组是同一个nodeValue的多个task替换
}
var task = new Array();