i18n-jsautotranslate 3.16.0 → 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +109 -103
  2. package/index.js +1287 -244
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -14,7 +14,7 @@ var translate = {
14
14
  * 格式:major.minor.patch.date
15
15
  */
16
16
  // AUTO_VERSION_START
17
- version: '3.16.0.20250618',
17
+ version: '3.18.0.20250816',
18
18
  // AUTO_VERSION_END
19
19
  /*
20
20
  当前使用的版本,默认使用v2. 可使用 setUseVersion2();
@@ -177,21 +177,20 @@ var translate = {
177
177
  }
178
178
 
179
179
  //从服务器加载支持的语言库
180
- translate.request.post(translate.request.api.language, {}, function(data){
181
- if(data.result == 0){
182
- console.log('load language list error : '+data.info);
183
- return;
184
- }
185
-
186
- translate.selectLanguageTag.customUI(data.list);
187
-
188
- /*
189
- try{
190
- document.getElementById('translateSelectLanguage').style.width = '94px';
191
- }catch(e){ console.log(e);}
192
- */
193
- }, null);
194
-
180
+ if(typeof(translate.request.api.language) == 'string' && translate.request.api.language.length > 0){
181
+ //从接口加载语种
182
+ translate.request.post(translate.request.api.language, {}, function(data){
183
+ if(data.result == 0){
184
+ console.log('load language list error : '+data.info);
185
+ return;
186
+ }
187
+ //console.log(data.list);
188
+ translate.selectLanguageTag.customUI(data.list);
189
+ }, null);
190
+ }else if(typeof(translate.request.api.language) == 'object'){
191
+ //无网络环境下,自定义显示语种
192
+ translate.selectLanguageTag.customUI(translate.request.api.language);
193
+ }
195
194
 
196
195
  }
197
196
  },
@@ -368,7 +367,10 @@ var translate = {
368
367
  if(window.self !== window.top){
369
368
  if(typeof(window.parent.translate) == 'object' && typeof(window.parent.translate.version) == 'string'){
370
369
  //iframe页面中存在 translate,那么也控制iframe中的进行翻译
371
- window.parent.translate.changeLanguage(languageName);
370
+ if(window.parent.translate.language.getCurrent() != languageName){
371
+ //如果父页面当前的语种不是需要翻译的语种,对其进行翻译
372
+ window.parent.translate.changeLanguage(languageName);
373
+ }
372
374
  }
373
375
  }
374
376
  }catch(e){
@@ -406,7 +408,9 @@ var translate = {
406
408
 
407
409
  //当用户代码设置里启用了 translate.listener.start() 然后用户加载页面后并没有翻译(这时listener是不启动的只是把listener.use标记为true),然后手动点击翻译按钮翻译为其他语种(这是不会刷新页面),翻译后也要跟着启动监听
408
410
  if(translate.listener.use == true && translate.listener.isStart == false){
409
- translate.listener.start();
411
+ if(typeof(translate.listener.start) != 'undefined'){
412
+ translate.listener.start();
413
+ }
410
414
  }
411
415
  },
412
416
 
@@ -536,8 +540,26 @@ var translate = {
536
540
  * 这个其实是借用了 自定义术语 的能力,设置了自定义术语的原字符等于翻译后的字符, 于是这个字符就不会被翻译了
537
541
  * 这里可以是多个,数组,如 ['你好','世界']
538
542
  */
539
- text:[]
540
-
543
+ text:[],
544
+ /*
545
+ 下面的 textRegex 、 setTextRegexs 正则方式设置忽略不翻译text的能力,有 https://github.com/wangliangyu 提交贡献, 弥补 translate.ignore.text 固定设置的不足
546
+ */
547
+ textRegex:[],
548
+ /*
549
+ 使用方式如:
550
+ translate.ignore.setTextRegexs([/请求/g, /[\u4a01-\u4a05]+/g]);
551
+ */
552
+ setTextRegexs: function(arr) {
553
+ if (!Array.isArray(arr)) throw new Error('参数必须为数组');
554
+ for (let i = 0; i < arr.length; i++) {
555
+ if (!(arr[i] instanceof RegExp)) {
556
+ throw new Error('第' + i + '项不是RegExp对象');
557
+ }
558
+ }
559
+ //this.textRegex = [...this.textRegex, ...arr];
560
+ //改为兼容 es5 的方式,提供更多兼容
561
+ this.textRegex = this.textRegex.concat(arr);
562
+ },
541
563
  },
542
564
  //刷新页面,你可以自定义刷新页面的方式,比如在 uniapp 打包生成 apk 时,apk中的刷新页面就不是h5的这个刷新,而是app的刷新方式,就需要自己进行重写这个刷新页面的方法了
543
565
  refreshCurrentPage:function(){
@@ -654,6 +676,10 @@ var translate = {
654
676
  }
655
677
  //console.log(str)
656
678
  for(var originalText in translate.nomenclature.data[translate.language.getLocal()][translate.to]){
679
+ if (!translate.nomenclature.data[translate.language.getLocal()][translate.to].hasOwnProperty(originalText)) {
680
+ continue;
681
+ }
682
+
657
683
  var translateText = translate.nomenclature.data[translate.language.getLocal()][translate.to][originalText];
658
684
  if(typeof(translateText) == 'function'){
659
685
  //进行异常的预处理调出
@@ -753,8 +779,15 @@ var translate = {
753
779
 
754
780
  var text = '';
755
781
  for(var uuid in translate.nodeQueue){
782
+ if (!translate.nodeQueue.hasOwnProperty(uuid)) {
783
+ continue;
784
+ }
785
+
756
786
  var queueValue = translate.nodeQueue[uuid];
757
787
  for(var lang in translate.nodeQueue[uuid].list){
788
+ if (!translate.nodeQueue[uuid].list.hasOwnProperty(lang)) {
789
+ continue;
790
+ }
758
791
  //console.log('------'+lang)
759
792
  if(typeof(lang) != 'string' || lang.length < 1){
760
793
  continue;
@@ -762,6 +795,9 @@ var translate = {
762
795
  //if(translate.language.getLocal() == lang){
763
796
  //console.log(translate.nodeQueue[uuid].list[lang]);
764
797
  for(var hash in translate.nodeQueue[uuid].list[lang]){
798
+ if (!translate.nodeQueue[uuid].list[lang].hasOwnProperty(hash)) {
799
+ continue;
800
+ }
765
801
  //console.log(translate.nodeQueue[uuid].list[lang][hash].original);
766
802
  //console.log(translate.nodeQueue[uuid].list[lang][hash].original);
767
803
  text = text + '\n' + translate.nodeQueue[uuid].list[lang][hash].original + '='+translate.storage.get('hash_'+translate.language.getCurrent()+'_'+hash);
@@ -903,6 +939,9 @@ var translate = {
903
939
 
904
940
  var data = await translate.storage.IndexedDB.list('hash_*');
905
941
  for(var i in data){
942
+ if (!data.hasOwnProperty(i)) {
943
+ continue;
944
+ }
906
945
  var originalText = data[i].value.originalText.replace(/\n/g, "\\n").replace(/\t/g, "\\t");
907
946
  text = text + '\n' + originalText + '='+data[i].value.english.replace(/\n/g, "\\n").replace(/\t/g, "\\t");
908
947
  }
@@ -1183,11 +1222,10 @@ var translate = {
1183
1222
  }
1184
1223
  //console.log('translateNodeslength: '+translateNodes.length);
1185
1224
 
1186
-
1187
- setTimeout(function() {
1188
- //console.log(translateNodes);
1189
- translate.execute(translateNodes); //指定要翻译的元素的集合,可传入一个或多个元素。如果不设置,默认翻译整个网页
1190
- }, 10); //这个要比 task.execute() 中的 settimeout 延迟执行删除 translate.inpr.....nodes 的时间要小,目的是前一个发生变动后,记入 inpr...nodes 然后翻译完成后节点发生变化又触发了listener,此时 inpr....nodes 还有,那么这个变化将不做处理,然后 inp.....nodes 再删除这个标记
1225
+ translate.execute(translateNodes);
1226
+ //setTimeout(function() {
1227
+ // translate.execute(translateNodes); //指定要翻译的元素的集合,可传入一个或多个元素。如果不设置,默认翻译整个网页
1228
+ //}, 10); //这个要比 task.execute() 中的 settimeout 延迟执行删除 translate.inpr.....nodes 的时间要小,目的是前一个发生变动后,记入 inpr...nodes 然后翻译完成后节点发生变化又触发了listener,此时 inpr....nodes 还有,那么这个变化将不做处理,然后 inp.....nodes 再删除这个标记
1191
1229
  }
1192
1230
  };
1193
1231
  // 创建一个观察器实例并传入回调函数
@@ -1246,7 +1284,7 @@ var translate = {
1246
1284
  //console.log(translate.nodeQueue[uuid]);
1247
1285
  for(var i = 0; i < translate.listener.execute.renderFinishByApi.length; i++){
1248
1286
  try{
1249
- translate.listener.execute.renderFinishByApi[i](uuid, from, to);
1287
+ translate.listener.execute.renderFinishByApi[i](uuid, from, to);
1250
1288
  }catch(e){
1251
1289
  console.log(e);
1252
1290
  }
@@ -1336,6 +1374,10 @@ var translate = {
1336
1374
  execute(){
1337
1375
  //先对tasks任务队列的替换词进行排序,将同一个node的替换词有大到小排列,避免先替换了小的,大的替换时找不到
1338
1376
  for(var hash in this.taskQueue){
1377
+ if (!this.taskQueue.hasOwnProperty(hash)) {
1378
+ continue;
1379
+ }
1380
+
1339
1381
  var tasks = this.taskQueue[hash];
1340
1382
  if(typeof(tasks) == 'function'){
1341
1383
  //进行异常的预处理调出
@@ -1358,6 +1400,10 @@ var translate = {
1358
1400
 
1359
1401
  //对nodeQueue进行翻译
1360
1402
  for(var hash in this.nodes){
1403
+ if (!this.nodes.hasOwnProperty(hash)) {
1404
+ continue;
1405
+ }
1406
+
1361
1407
  var tasks = this.taskQueue[hash]; //取出当前node元素对应的替换任务
1362
1408
  //var tagName = this.nodes[hash][0].nodeName; //以下节点的tag name
1363
1409
  //console.log(tasks);
@@ -1438,8 +1484,16 @@ var translate = {
1438
1484
  /** 执行完成后,保存翻译的历史node **/
1439
1485
  //将当前翻译完成的node进行缓存记录,以node唯一标识为key, node、以及node当前翻译之后的内容为值进行缓存。方便下一次执行 translate.execute() 时,若值未变化则不进行翻译
1440
1486
  for(var hash in renderTask.nodes){
1487
+ if (!renderTask.nodes.hasOwnProperty(hash)) {
1488
+ continue;
1489
+ }
1490
+
1441
1491
  //console.log(translate.nodeQueue[uuid].list[lang][hash])
1442
1492
  for(var nodeindex in renderTask.nodes[hash]){
1493
+ if (!renderTask.nodes[hash].hasOwnProperty(nodeindex)) {
1494
+ continue;
1495
+ }
1496
+
1443
1497
  //console.log(translate.nodeQueue[uuid].list[lang][hash].original);
1444
1498
  //var nodename = translate.element.getNodeName(translate.nodeQueue[uuid].list[lang][hash].nodes[0].node);
1445
1499
  //console.log("nodename:"+nodename);
@@ -1566,8 +1620,13 @@ var translate = {
1566
1620
  @param to 翻译为的语种
1567
1621
  */
1568
1622
  isAllExecuteFinish:function(uuid, from, to){
1623
+ translate.listener.execute.renderFinishByApiRun(uuid, from, to);
1624
+
1569
1625
  //console.log('uuid:'+uuid+', from:'+from+', to:'+to);
1570
1626
  for(var lang in translate.translateRequest[uuid]){
1627
+ if (!translate.translateRequest[uuid].hasOwnProperty(lang)) {
1628
+ continue;
1629
+ }
1571
1630
  //console.log(translate.translateRequest[uuid])
1572
1631
  for(var i = 0; i<translate.translateRequest[uuid][lang].length; i++){
1573
1632
  if(translate.translateRequest[uuid][lang][i].executeFinish == 0){
@@ -1581,11 +1640,12 @@ var translate = {
1581
1640
  }
1582
1641
  }
1583
1642
 
1643
+ //生命周期触发事件
1644
+ translate.lifecycle.execute.renderFinish_Trigger(uuid, to);
1645
+
1584
1646
  //都执行完了,那么设置完毕
1585
1647
  translate.state = 0;
1586
1648
  translate.executeNumber++;
1587
-
1588
- translate.listener.execute.renderFinishByApiRun(uuid, from, to);
1589
1649
  }
1590
1650
 
1591
1651
  },
@@ -1593,6 +1653,116 @@ var translate = {
1593
1653
  //execute() 方法已经被执行过多少次了, 只有execute() 完全执行完,也就是界面渲染完毕后,它才会+1
1594
1654
  executeNumber:0,
1595
1655
 
1656
+ lifecycle:{
1657
+
1658
+ /*
1659
+ translate.execute() 执行相关
1660
+ */
1661
+ execute:{
1662
+ /*
1663
+ 每当触发执行 translate.execute() 时,会先进行当前是否可以正常进行翻译的判定,比如 当前语种是否就已经是翻译之后的语种了是否没必要翻译了等。(这些初始判定可以理解成它的耗时小于1毫秒,几乎没有耗时)
1664
+ 经过初始的判断后,发现允许被翻译,那么在向后执行之前,先触发此。
1665
+ 也就是在进行翻译之前,触发此。
1666
+
1667
+ @param uuid:translate.nodeQueue[uuid] 这里的
1668
+ @param to 翻译为的语种
1669
+ */
1670
+ start : [],
1671
+ start_Trigger:function(uuid, to){
1672
+ for(var i = 0; i < translate.lifecycle.execute.start.length; i++){
1673
+ try{
1674
+ translate.lifecycle.execute.start[i](uuid, to);
1675
+ }catch(e){
1676
+ console.log(e);
1677
+ }
1678
+ }
1679
+ },
1680
+
1681
+
1682
+ //待整理
1683
+ start_old : [],
1684
+ startRun:function(uuid, from, to){
1685
+ //console.log(translate.nodeQueue[uuid]);
1686
+ for(var i = 0; i < translate.listener.execute.renderStartByApi.length; i++){
1687
+ try{
1688
+ translate.listener.execute.renderStartByApi[i](uuid, from, to);
1689
+ }catch(e){
1690
+ console.log(e);
1691
+ }
1692
+ }
1693
+ },
1694
+
1695
+ /*
1696
+ 当扫描整个节点完成,进行翻译(1. 命中本地缓存、 2.进行网络翻译请求)之前,触发
1697
+ 待整理
1698
+ */
1699
+ scanNodesFinsh: [],
1700
+
1701
+
1702
+ /*
1703
+ 每当触发执行 translate.execute() 时,当缓存中未发现,需要请求翻译API进行翻译时,在发送API请求前,触发此
1704
+
1705
+ @param uuid:translate.nodeQueue[uuid] 这里的
1706
+ @param from 来源语种,翻译前的语种
1707
+ @param to 翻译为的语种
1708
+ @param texts 要翻译的文本,它是一个数组形态,是要进行通过API翻译接口进行翻译的文本,格式如 ['你好','世界']
1709
+ */
1710
+ translateNetworkBefore:[],
1711
+ translateNetworkBefore_Trigger:function(uuid, from, to, texts){
1712
+ for(var i = 0; i < translate.lifecycle.execute.translateNetworkBefore.length; i++){
1713
+ try{
1714
+ translate.lifecycle.execute.translateNetworkBefore[i](uuid,from, to, texts);
1715
+ }catch(e){
1716
+ console.log(e);
1717
+ }
1718
+ }
1719
+ },
1720
+
1721
+ /*
1722
+ 当 translate.execute() 触发网络翻译请求完毕(不管成功还是失败),并将翻译结果渲染到页面完毕后,触发此。
1723
+ @param uuid translate.nodeQueue 的uuid
1724
+ @param from
1725
+ @param to 当前是执行的翻译为什么语种
1726
+ @param text 网络请求翻译的文本/节点/。。。待定
1727
+ */
1728
+ translateNetworkAfter:[], //已废弃
1729
+ /*
1730
+
1731
+ translateNetworkAfter_Trigger:function(uuid, to){
1732
+ for(var i = 0; i < translate.lifecycle.execute.translateNetworkAfter.length; i++){
1733
+ try{
1734
+ translate.lifecycle.execute.translateNetworkAfter[i](uuid, to);
1735
+ }catch(e){
1736
+ console.log(e);
1737
+ }
1738
+ }
1739
+ },
1740
+ */
1741
+
1742
+ /*
1743
+ translate.execute() 的翻译渲染完毕触发
1744
+ 这个完毕是指它当触发 translate.execute() 进行翻译后,无论是全部命中了本地缓存,还是有部分要通过翻译接口发起多个网络请求,当拿到结果(缓存中的翻译结果或多个不同的有xx语种翻译的网络请求全部完成,这个完成是包含所有成功跟失败的响应),并完成将翻译结果渲染到页面中进行显示后,触发此
1745
+ 它跟 translateNetworkFinish 的区别是, translateNetworkFinish 仅仅针对有网络请求的才会触发,而 renderFinish 是如果全部命中了浏览器本地缓存,无需发起任何网络翻译请求这种情况时,也会触发。
1746
+ @param uuid translate.nodeQueue 的uuid
1747
+ @param to 当前是执行的翻译为什么语种
1748
+ */
1749
+ renderFinish:[function(uuid, to){ //这里默认带着一个触发翻译为英文后,自动对英文进行元素视觉处理,追加空格的
1750
+ if(typeof(translate.visual) != 'undefined'){
1751
+ translate.visual.adjustTranslationSpacesByNodequeueUuid(uuid);
1752
+ }
1753
+ }],
1754
+ renderFinish_Trigger:function(uuid, to){
1755
+ for(var i = 0; i < translate.lifecycle.execute.renderFinish.length; i++){
1756
+ try{
1757
+ translate.lifecycle.execute.renderFinish[i](uuid, to);
1758
+ }catch(e){
1759
+ console.log(e);
1760
+ }
1761
+ }
1762
+ }
1763
+ }
1764
+ },
1765
+
1596
1766
  /*translate.execute() start */
1597
1767
  /*
1598
1768
  执行翻译操作。翻译的是 nodeQueue 中的
@@ -1667,11 +1837,11 @@ var translate = {
1667
1837
  //未指定,判断如果指定了自动获取用户本国语种了,那么进行获取
1668
1838
  if(translate.autoDiscriminateLocalLanguage){
1669
1839
  translate.executeByLocalLanguage();
1840
+ }else{
1841
+ //没有指定翻译目标语言、又没自动获取用户本国语种,则不翻译
1842
+ translate.state = 0;
1843
+ return;
1670
1844
  }
1671
-
1672
- //没有指定翻译目标语言、又没自动获取用户本国语种,则不翻译
1673
- translate.state = 0;
1674
- return;
1675
1845
  }
1676
1846
 
1677
1847
  //判断本地语种跟要翻译的目标语种是否一样,如果是一样,那就不需要进行任何翻译
@@ -1687,6 +1857,10 @@ var translate = {
1687
1857
 
1688
1858
 
1689
1859
  /********** 翻译进行 */
1860
+
1861
+ //生命周期-触发翻译进行之前,用户自定义的钩子
1862
+ translate.lifecycle.execute.start_Trigger(uuid, translate.to);
1863
+
1690
1864
 
1691
1865
  //先进行图片的翻译替换,毕竟图片还有加载的过程
1692
1866
  translate.images.execute();
@@ -1732,6 +1906,27 @@ var translate = {
1732
1906
  console.log('translate.execute( docs ) 传入的docs.length 过大,超过500,这很不正常,当前 docs.length : '+all.length+' ,如果你感觉真的没问题,请联系作者 http://translate.zvo.cn/43006.html 说明情况,根据你的情况进行分析。 当前只取前500个元素进行翻译');
1733
1907
  }
1734
1908
 
1909
+ //初始化 translate.element.tagAttribute ,主要针对 v3.17.10 版本的适配调整,对 translate.element.tagAttribute 的设置做了改变,做旧版本的适配
1910
+ try{
1911
+ for(var te_tag in translate.element.tagAttribute){
1912
+ if (!translate.element.tagAttribute.hasOwnProperty(te_tag)) {
1913
+ continue;
1914
+ }
1915
+ if(translate.element.tagAttribute[te_tag] instanceof Array){
1916
+ //是 v3.17.10 之前版本的设置方式,要进行对旧版本的适配
1917
+ var tArray = translate.element.tagAttribute[te_tag];
1918
+ translate.element.tagAttribute[te_tag] = {
1919
+ attribute: tArray,
1920
+ condition: function(element){
1921
+ return true;
1922
+ }
1923
+ }
1924
+ }
1925
+ }
1926
+ }catch(e){
1927
+ console.log(e);
1928
+ }
1929
+
1735
1930
  //检索目标内的node元素
1736
1931
  for(var i = 0; i< all.length & i < 500; i++){
1737
1932
  var node = all[i];
@@ -1742,6 +1937,9 @@ var translate = {
1742
1937
  if(translate.language.translateLanguagesRange.length > 0){
1743
1938
  //如果大于0,则是有设置,那么只翻译有设置的语种,不在设置中的语种不会参与翻译
1744
1939
  for(var lang in translate.nodeQueue[uuid].list){
1940
+ if (!translate.nodeQueue[uuid].list.hasOwnProperty(lang)) {
1941
+ continue;
1942
+ }
1745
1943
  if(translate.language.translateLanguagesRange.indexOf(lang) < 0){
1746
1944
  //删除这个语种
1747
1945
  delete translate.nodeQueue[uuid].list[lang];
@@ -1759,13 +1957,23 @@ var translate = {
1759
1957
  console.log('您使用translate.js时可能放的位置不对,不要吧 translate.js 放在网页最顶部,这样当 translate.js 进行执行,也就是 translate.execute() 执行时,因为网页是从上往下加载,它放在网页最顶部,那么它执行时网页后面的内容都还没加载出来,这个是不会获取到网页任何内容的,也就是它是不起任何作用的');
1760
1958
  }
1761
1959
  for(var lang in translate.nodeQueue[uuid].list){
1960
+ if (!translate.nodeQueue[uuid].list.hasOwnProperty(lang)) {
1961
+ continue;
1962
+ }
1762
1963
  //console.log('lang:'+lang)
1763
1964
  for(var hash in translate.nodeQueue[uuid].list[lang]){
1965
+ if (!translate.nodeQueue[uuid].list[lang].hasOwnProperty(hash)) {
1966
+ continue;
1967
+ }
1764
1968
  //console.log(hash)
1765
1969
  if(typeof(translate.nodeQueue[uuid].list[lang][hash]) == 'function'){
1766
1970
  //v2.10增加,避免hash冒出个 Contains 出来导致for中的.length 出错
1767
1971
  continue;
1768
1972
  }
1973
+ if(typeof(translate.nodeQueue[uuid].list[lang][hash].nodes) == 'undefined' || typeof(translate.nodeQueue[uuid].list[lang][hash].nodes.length) == 'undefined'){
1974
+ //v3.16.2 增加,针对深圳北理莫斯科学校龙老师提出的这里 .length 遇到了 undefined 的情况
1975
+ continue;
1976
+ }
1769
1977
  for(var nodeindex = translate.nodeQueue[uuid].list[lang][hash].nodes.length-1; nodeindex > -1; nodeindex--){
1770
1978
  //console.log(translate.nodeQueue[uuid].list[lang][hash].nodes);
1771
1979
  var analyse = translate.element.nodeAnalyse.get(translate.nodeQueue[uuid].list[lang][hash].nodes[nodeindex].node);
@@ -1832,6 +2040,9 @@ var translate = {
1832
2040
  var twoScanNodes = {};
1833
2041
  var cacheScanNodes = []; //同上面的 twoScanNodes,只不过 twoScanNodes 是按照lang存的,而这个不再有lang区分
1834
2042
  for(var lang in translate.nodeQueue[uuid]['list']){ //二维数组中,取语言
2043
+ if (!translate.nodeQueue[uuid]['list'].hasOwnProperty(lang)) {
2044
+ continue;
2045
+ }
1835
2046
  //console.log('lang:'+lang); //lang为english这种语言标识
1836
2047
  if(lang == null || typeof(lang) == 'undefined' || lang.length == 0 || lang == 'undefined'){
1837
2048
  //console.log('lang is null : '+lang);
@@ -1847,6 +2058,9 @@ var translate = {
1847
2058
  twoScanNodes[lang] = [];
1848
2059
  //二维数组,取hash、value
1849
2060
  for(var hash in translate.nodeQueue[uuid]['list'][lang]){
2061
+ if (!translate.nodeQueue[uuid]['list'][lang].hasOwnProperty(hash)) {
2062
+ continue;
2063
+ }
1850
2064
  if(typeof(translate.nodeQueue[uuid]['list'][lang][hash]) == 'function'){
1851
2065
  //跳出,增加容错。 正常情况下应该不会这样
1852
2066
  continue;
@@ -1994,8 +2208,25 @@ var translate = {
1994
2208
  //console.log('cacheScanNodes:');
1995
2209
  //console.log(cacheScanNodes);
1996
2210
 
2211
+
2212
+ if(typeof(translate.request.api.translate) != 'string' || translate.request.api.translate == null || translate.request.api.translate.length < 1){
2213
+ //用户已经设置了不掉翻译接口进行翻译
2214
+ translate.state = 0;
2215
+
2216
+ //生命周期触发事件
2217
+ translate.lifecycle.execute.renderFinish_Trigger(uuid, translate.to);
2218
+ translate.executeNumber++;
2219
+ return;
2220
+ }
2221
+
2222
+
2223
+
1997
2224
  /******* 进行第二次扫描、追加入翻译队列。目的是防止缓存打散扫描的待翻译文本 ********/
1998
2225
  for(var lang in twoScanNodes){
2226
+ if (!twoScanNodes.hasOwnProperty(lang)) {
2227
+ continue;
2228
+ }
2229
+
1999
2230
  //记录第一次扫描的数据,以便跟第二次扫描后的进行对比
2000
2231
  var firstScan = Object.keys(translate.nodeQueue[uuid]['list'][lang]);
2001
2232
  var firstScan_lang_langth = firstScan.length; //第一次扫描后的数组长度
@@ -2077,6 +2308,10 @@ var translate = {
2077
2308
  var fanyiLangs = [];
2078
2309
  //console.log(translateTextArray)
2079
2310
  for(var lang in translate.nodeQueue[uuid]['list']){ //二维数组中取语言
2311
+ if (!translate.nodeQueue[uuid]['list'].hasOwnProperty(lang)) {
2312
+ continue;
2313
+ }
2314
+
2080
2315
  if(typeof(translateTextArray[lang]) == 'undefined'){
2081
2316
  continue;
2082
2317
  }
@@ -2112,6 +2347,10 @@ var translate = {
2112
2347
  //console.log(translate.nodeQueue[uuid]['list'])
2113
2348
  if(fanyiLangs.length == 0){
2114
2349
  //没有需要翻译的,直接退出
2350
+
2351
+ //生命周期触发事件
2352
+ translate.lifecycle.execute.renderFinish_Trigger(uuid, translate.to);
2353
+
2115
2354
  translate.state = 0;
2116
2355
  translate.executeNumber++;
2117
2356
  return;
@@ -2119,6 +2358,9 @@ var translate = {
2119
2358
 
2120
2359
  //加入 translate.inProgressNodes -- start
2121
2360
  for(var lang in translateHashArray){
2361
+ if (!translateHashArray.hasOwnProperty(lang)) {
2362
+ continue;
2363
+ }
2122
2364
  if(typeof(translateHashArray[lang]) == 'undefined'){
2123
2365
  continue;
2124
2366
  }
@@ -2132,6 +2374,11 @@ var translate = {
2132
2374
  //console.log('translate.nodeQueue[\''+uuid+'\'][\'list\'][\'chinese_simplified\'][\''+thhash+'\']');
2133
2375
  //console.log(lang);
2134
2376
  //console.log(translate.nodeQueue[uuid]['list'][lang][thhash].nodes);
2377
+ if(typeof(translate.nodeQueue[uuid]['list'][lang][thhash].nodes) == 'undefined' || typeof(translate.nodeQueue[uuid]['list'][lang][thhash].nodes.length) == 'undefined'){
2378
+ console.log('translate.nodeQueue[\''+uuid+'\'][\'list\'][\''+lang+'\'][\''+thhash+'\'].nodes.length is null ,理论上不应该存在,进行异常报出,但不影响使用,已容错。');
2379
+ continue;
2380
+ }
2381
+
2135
2382
  for(var ipni = 0; ipni < translate.nodeQueue[uuid]['list'][lang][thhash].nodes.length; ipni++){
2136
2383
  //取得这个翻译的node
2137
2384
  var ipnode = translate.nodeQueue[uuid]['list'][lang][thhash].nodes[ipni].node;
@@ -2163,14 +2410,21 @@ var translate = {
2163
2410
  //状态
2164
2411
  translate.state = 20;
2165
2412
 
2166
-
2167
2413
  //进行掉接口翻译
2168
2414
  for(var lang_index in fanyiLangs){ //一维数组,取语言
2415
+ if (!fanyiLangs.hasOwnProperty(lang_index)) {
2416
+ continue;
2417
+ }
2169
2418
  var lang = fanyiLangs[lang_index];
2170
- //console.log(typeof(translateTextArray[lang]))
2419
+ if(typeof(lang) != 'string'){
2420
+ continue;
2421
+ }
2171
2422
 
2172
2423
  if(typeof(translateTextArray[lang]) == 'undefined' || translateTextArray[lang].length < 1){
2173
- console.log('异常,理论上不应该存在: typeof(translateTextArray[lang]) == \'undefined\' || translateTextArray[lang].length < 1');
2424
+ console.log('异常,理论上不应该存在, lang:'+lang+', translateTextArray:');
2425
+ console.log(translateTextArray);
2426
+ console.log('你无需担心,这个只是个提示,它并不影响你翻译的正常进行,只是个异常提示而已,它会自动容错处理的,不会影响翻译的使用。');
2427
+
2174
2428
  translate.state = 0;
2175
2429
  translate.executeNumber++;
2176
2430
  return;
@@ -2193,7 +2447,8 @@ var translate = {
2193
2447
 
2194
2448
  //listener
2195
2449
  translate.listener.execute.renderStartByApiRun(uuid, lang, translate.to);
2196
-
2450
+ translate.lifecycle.execute.translateNetworkBefore_Trigger(uuid, lang, translate.to, translateTextArray[lang]);
2451
+
2197
2452
  /*** 翻译开始 ***/
2198
2453
  var url = translate.request.api.translate;
2199
2454
  var data = {
@@ -2313,10 +2568,10 @@ var translate = {
2313
2568
  translate.waitingExecute.isAllExecuteFinish(uuid, data.from, data.to);
2314
2569
  },10);
2315
2570
  }, function(xhr){
2316
- translate.translateRequest[uuid][lang].executeFinish = 1; //1是执行完毕
2317
- translate.translateRequest[uuid][lang].stoptime = Math.floor(Date.now() / 1000);
2318
- translate.translateRequest[uuid][lang].result = 3;
2319
- translate.waitingExecute.isAllExecuteFinish(uuid, lang, translate.to);
2571
+ translate.translateRequest[uuid][xhr.data.from].executeFinish = 1; //1是执行完毕
2572
+ translate.translateRequest[uuid][xhr.data.from].stoptime = Math.floor(Date.now() / 1000);
2573
+ translate.translateRequest[uuid][xhr.data.from].result = 3;
2574
+ translate.waitingExecute.isAllExecuteFinish(uuid, xhr.data.from, translate.to);
2320
2575
  });
2321
2576
  /*** 翻译end ***/
2322
2577
  }
@@ -2371,6 +2626,32 @@ var translate = {
2371
2626
  有几个要翻译的属性,就写上几个。
2372
2627
  同样,有几个要额外翻译的tag,就加上几行。
2373
2628
  详细文档参考: http://translate.zvo.cn/231504.html
2629
+
2630
+
2631
+ //针对宁德时代提出的需求,需要对 标签本身进行一个判定,是否符合条件符合条件才会翻译,不符合条件则不要进行翻译
2632
+ //比如标签带有 disabled 的才会被翻译,所以要增加一个自定义入参的 function ,返回 true、false
2633
+ translate.element.tagAttribute['input']={
2634
+ //要被翻译的tag的属性,这里是要翻译 input 的 value 、 data-value 这两个属性。
2635
+ //数组格式,可以一个或多个属性
2636
+ attribute:['value','data-value'],
2637
+ //条件,传入一个function,返回一个布尔值。
2638
+ //只有当返回的布尔值是true时,才会对上面设置的 attribute 进行翻译,否则并不会对当前设定标签的 attribute 进行任何翻译操作。
2639
+ condition:function(element){
2640
+ // element 便是当前的元素,
2641
+ // 比如这里是 translate.element.tagAttribute['input'] 那这个 element 参数便是扫描到的具体的 input 元素
2642
+ // 可以针对 element 这个当前元素本身来进行判定,来决定是否进行翻译。
2643
+ // 返回值是布尔值 true、false
2644
+ // return true; //要对 attribute中设置的 ['value','data-value'] 这两个input 的属性的值进行翻译。
2645
+ // 如果不设置或传入 condition ,比如单纯这样设置:
2646
+ // translate.element.tagAttribute['input']={
2647
+ // attribute:['value','data-value']
2648
+ // }
2649
+ // 那么这里默认就是 return true;
2650
+ // return false; //不对 attribute中设置的 ['value','data-value'] 这两个input 的属性的值进行任何操作
2651
+ return true;
2652
+ }
2653
+ };
2654
+
2374
2655
  */
2375
2656
  tagAttribute : {},
2376
2657
 
@@ -2431,14 +2712,29 @@ var translate = {
2431
2712
 
2432
2713
  if(attribute != null && typeof(attribute) == 'string' && attribute.length > 0){
2433
2714
  //这个node有属性,替换的是node的属性,而不是nodeValue
2434
- result['text'] = node[attribute];
2715
+
2716
+ var nodeAttributeValue; //这个 attribute 属性的值
2717
+ if(nodename == 'INPUT' && attribute.toLowerCase() == 'value'){
2718
+ //如果是input 的value属性,那么要直接获取,而非通过 attribute ,不然用户自己输入的通过 attribute 是获取不到的 -- catl 赵阳 提出
2719
+
2720
+ nodeAttributeValue = node.value;
2721
+ }else{
2722
+ nodeAttributeValue = node[attribute];
2723
+ }
2724
+ result['text'] = nodeAttributeValue;
2725
+
2435
2726
 
2436
2727
  //替换渲染
2437
2728
  if(typeof(originalText) != 'undefined' && originalText.length > 0){
2438
- if(typeof(node[attribute]) != 'undefined'){
2729
+ if(typeof(nodeAttributeValue) != 'undefined'){
2439
2730
  //这种是主流框架,像是vue、element、react 都是用这种 DOM Property 的方式,更快
2440
- var resultShowText = translate.util.textReplace(node[attribute], originalText, resultText, translate.to);
2441
- node[attribute] = resultShowText; //2025.4.26 变更为此方式
2731
+ var resultShowText = translate.util.textReplace(nodeAttributeValue, originalText, resultText, translate.to);
2732
+ if(nodename == 'INPUT' && attribute.toLowerCase() == 'value'){
2733
+ //input 的value 对于用户输入的必须用 .value 操作
2734
+ node.value = resultShowText;
2735
+ }else{
2736
+ node[attribute] = resultShowText; //2025.4.26 变更为此方式
2737
+ }
2442
2738
  if(resultShowText.indexOf(resultText) > -1){
2443
2739
  result['resultText'] = resultShowText;
2444
2740
  }else{
@@ -2602,7 +2898,9 @@ var translate = {
2602
2898
  }else{
2603
2899
  //替换渲染
2604
2900
  if(typeof(originalText) != 'undefined' && originalText != null && originalText.length > 0){
2901
+ //console.log(originalText+'|');
2605
2902
  var resultShowText = translate.util.textReplace(node.nodeValue, originalText, resultText, translate.to);
2903
+ //console.log(resultShowText+'|');
2606
2904
  node.nodeValue = resultShowText; //2025.4.26 变更为此方式
2607
2905
  if(resultShowText.indexOf(resultText) > -1){
2608
2906
  result['resultText'] = resultShowText;
@@ -2674,34 +2972,47 @@ var translate = {
2674
2972
  //console.log('find:'+nodeNameLowerCase);
2675
2973
  //console.log(translate.element.tagAttribute[nodeNameLowerCase]);
2676
2974
 
2677
- for(var attributeName_index in translate.element.tagAttribute[nodeNameLowerCase]){
2975
+ for(var attributeName_index in translate.element.tagAttribute[nodeNameLowerCase].attribute){
2976
+ if (!translate.element.tagAttribute[nodeNameLowerCase].attribute.hasOwnProperty(attributeName_index)) {
2977
+ continue;
2978
+ }
2979
+ if(typeof(translate.element.tagAttribute[nodeNameLowerCase].condition) !='undefined' && !translate.element.tagAttribute[nodeNameLowerCase].condition(node)){
2980
+ continue;
2981
+ }
2678
2982
 
2679
- var attributeName = translate.element.tagAttribute[nodeNameLowerCase][attributeName_index];
2983
+ var attributeName = translate.element.tagAttribute[nodeNameLowerCase].attribute[attributeName_index];
2680
2984
  //console.log(attributeName);
2681
2985
  //console.log(node.getAttribute(attributeName));
2682
2986
 
2683
- /*
2684
- * 默认是 HtmlAtrribute 也就是 HTML特性。取值有两个:
2685
- * HTMLAtrribute : HTML特性
2686
- * DOMProperty : DOM属性
2687
- */
2688
- var DOMPropOrHTMLAttr = 'HTMLAtrribute';
2689
- var attributeValue = node.getAttribute(attributeName);
2690
- if(typeof(attributeValue) == 'undefined' || attributeValue == null){
2691
- //vue、element、react 中的一些动态赋值,比如 element 中的 el-select 选中后赋予显示出来的文本,getAttribute 就取不到,因为是改动的 DOM属性,所以要用这种方式才能取出来
2692
- attributeValue = node[attributeName];
2987
+
2988
+ if(nodeNameLowerCase == 'input' && attributeName.toLowerCase() == 'value'){
2989
+ //如果是input 的value属性,那么要直接获取,而非通过 attribute ,不然用户自己输入的通过 attribute 是获取不到的 - catl 赵阳 提出
2990
+ attributeValue = node.value;
2693
2991
  DOMPropOrHTMLAttr = 'DOMProperty';
2992
+ }else{
2993
+ /*
2994
+ * 默认是 HtmlAtrribute 也就是 HTML特性。取值有两个:
2995
+ * HTMLAtrribute : HTML特性
2996
+ * DOMProperty : DOM属性
2997
+ */
2998
+ var DOMPropOrHTMLAttr = 'HTMLAtrribute';
2999
+ var attributeValue = node.getAttribute(attributeName);
3000
+ if(typeof(attributeValue) == 'undefined' || attributeValue == null){
3001
+ //vue、element、react 中的一些动态赋值,比如 element 中的 el-select 选中后赋予显示出来的文本,getAttribute 就取不到,因为是改动的 DOM属性,所以要用这种方式才能取出来
3002
+ attributeValue = node[attributeName];
3003
+ DOMPropOrHTMLAttr = 'DOMProperty';
3004
+ }
3005
+ if(typeof(attributeValue) == 'undefined' || attributeValue == null){
3006
+ //这个tag标签没有这个属性,忽略
3007
+ continue;
3008
+ }
2694
3009
  }
2695
- if(typeof(attributeValue) == 'undefined' || attributeValue == null){
2696
- //这个tag标签没有这个属性,忽略
2697
- continue;
2698
- }
3010
+
2699
3011
 
2700
3012
  //if(typeof(node.getAttribute(attributeName)) == 'undefined' && typeof(node[attributeName]) == 'undefined'){
2701
3013
  // //这个tag标签没有这个 attribute,忽略
2702
3014
  // continue
2703
3015
  //}
2704
-
2705
3016
  //判断当前元素是否在ignore忽略的tag、id、class name中 v3.15.7 增加
2706
3017
  if(!translate.ignore.isIgnore(node)){
2707
3018
  //加入翻译
@@ -2780,6 +3091,7 @@ var translate = {
2780
3091
 
2781
3092
  //node分析
2782
3093
  var nodeAnaly = translate.element.nodeAnalyse.get(node);
3094
+ //console.log(nodeAnaly)
2783
3095
  if(nodeAnaly['text'].length > 0){
2784
3096
  //有要翻译的目标内容,加入翻译队列
2785
3097
  //console.log('addNodeToQueue -- '+nodeAnaly['node']+', text:' + nodeAnaly['text']);
@@ -2912,6 +3224,16 @@ var translate = {
2912
3224
  textArray.push(text); //先将主 text 赋予 ,后面如果对主text进行加工分割,分割后会将主text给删除掉
2913
3225
  //console.log(textArray);
2914
3226
 
3227
+ // 处理 ignore.regex
3228
+ for (var ri = 0; ri < translate.ignore.textRegex.length; ri++) {
3229
+ var regex = translate.ignore.textRegex[ri];
3230
+ for (var tai = 0; tai < textArray.length; tai++) {
3231
+ var text = textArray[tai];
3232
+ var ignoreTexts = text.match(regex) || []
3233
+ translate.ignore.text = translate.ignore.text.concat(ignoreTexts)
3234
+ }
3235
+ }
3236
+
2915
3237
  /**** v3.10.2.20241206 - 增加自定义忽略翻译的文本,忽略翻译的文本不会被翻译 - 当然这样会打乱翻译之后阅读的连贯性 ****/
2916
3238
  for(var ti = 0; ti<translate.ignore.text.length; ti++){
2917
3239
  if(translate.ignore.text[ti].trim().length == 0){
@@ -2924,7 +3246,6 @@ var translate = {
2924
3246
  }
2925
3247
 
2926
3248
 
2927
-
2928
3249
  /**** v3.10.2.20241206 - 自定义术语能力全面优化 - 当然这样会打乱翻译之后阅读的连贯性 ****/
2929
3250
  //判断是否进行了翻译,也就是有设置目标语种,并且跟当前语种不一致
2930
3251
  if(typeof(translate.temp_nomenclature) == 'undefined'){
@@ -2936,6 +3257,9 @@ var translate = {
2936
3257
  if(typeof(translate.nomenclature.data[translate.language.getLocal()]) != 'undefined' && typeof(translate.nomenclature.data[translate.language.getLocal()][translate.to]) != 'undefined'){
2937
3258
  var nomenclatureKeyArray;
2938
3259
  for(var nomenclatureKey in translate.nomenclature.data[translate.language.getLocal()][translate.to]){
3260
+ if (!translate.nomenclature.data[translate.language.getLocal()][translate.to].hasOwnProperty(nomenclatureKey)) {
3261
+ continue;
3262
+ }
2939
3263
  //nomenclatureKey 便是自定义术语的原始文本,值是要替换为的文本
2940
3264
  //console.log(nomenclatureKey);
2941
3265
  //自定义属于的指定的结果字符串
@@ -3147,6 +3471,9 @@ var translate = {
3147
3471
  //console.log(langs);
3148
3472
 
3149
3473
  for(var lang in langs) {
3474
+ if (!langs.hasOwnProperty(lang)) {
3475
+ continue;
3476
+ }
3150
3477
  //创建二维数组, key为语种,如 english
3151
3478
  /*
3152
3479
  放到了 translate.addNodeQueueItem 进行判断
@@ -3421,11 +3748,10 @@ var translate = {
3421
3748
  language:{
3422
3749
  /*
3423
3750
  英语的变种语种,也就是在英语26个字母的基础上加了点别的特殊字母另成的一种语言,而这些语言是没法直接通过识别字符来判断出是哪种语种的
3424
- v3.4.2 增加
3425
- 当前先只加上:
3426
- 法语、意大利语、德语
3751
+
3752
+ 法语、意大利语、德语、葡萄牙语
3427
3753
  */
3428
- englishVarietys : ['french','italian','deutsch'],
3754
+ englishVarietys : ['french','italian','deutsch', 'portuguese'],
3429
3755
 
3430
3756
  //当前本地语种,本地语言,默认是简体中文。设置请使用 translate.language.setLocal(...)。不可直接使用,使用需用 getLocal()
3431
3757
  local:'',
@@ -3487,6 +3813,14 @@ var translate = {
3487
3813
  清除历史翻译语种的缓存
3488
3814
  */
3489
3815
  clearCacheLanguage:function(){
3816
+ if(typeof(translate.language.setUrlParamControl_use) != 'undefined'){
3817
+ if(translate.language.setUrlParamControl_use){
3818
+ console.log('使用提示:')
3819
+ console.log('translate.language.setUrlParamControl(...) 的作用是 可以通过URL传一个语种,来指定当前页面以什么语种显示。 参考文档: http://translate.zvo.cn/4075.html');
3820
+ console.log('translate.language.clearCacheLanguage() 是清除历史翻译语种缓存,也就是清除之前指定翻译为什么语种。 参考文档:http://translate.zvo.cn/4080.html')
3821
+ console.log('如果你执行了 translate.language.setUrlParamControl(...) 那么是要根据url传参来切换语种的,但是后面又出现了 translate.language.clearCacheLanguage() 它会阻止 translate.language.setUrlParamControl(...) 它的设置,即使有url传递翻译为什么语言,也会因为 translate.language.clearCacheLanguage() 给清除掉,使URL传参的语种不起任何作用。')
3822
+ }
3823
+ }
3490
3824
  translate.to = '';
3491
3825
  translate.storage.set('to','');
3492
3826
  },
@@ -3494,6 +3828,7 @@ var translate = {
3494
3828
  //设置可以根据当前访问url的某个get参数来控制使用哪种语言显示。
3495
3829
  //比如当前语种是简体中文,网页url是http://translate.zvo.cn/index.html ,那么可以通过在url后面增加 language 参数指定翻译语种,来使网页内容以英文形态显示 http://translate.zvo.cn/index.html?language=english
3496
3830
  setUrlParamControl:function(paramName){
3831
+ translate.language.setUrlParamControl_use = true; //标记已执行了 translate.language.setUrlParamControl ,仅仅只是标记,无其他作用
3497
3832
  if(typeof(paramName) == 'undefined' || paramName.length < 1){
3498
3833
  paramName = 'language';
3499
3834
  }
@@ -3508,25 +3843,85 @@ var translate = {
3508
3843
  translate.storage.set('to', paramValue);
3509
3844
  translate.to = paramValue;
3510
3845
  },
3511
- //自动识别当前页面是什么语种
3512
- autoRecognitionLocalLanguage:function(){
3513
- if(translate.language.local != null && translate.language.local.length > 2){
3514
- //已设置过了,不需要再设置
3515
- return;
3516
- }
3846
+ /*
3847
+ 获取翻译区域的原始文本,翻译前的文本。 这里会把空白符等过滤掉,只返回纯显示的文本
3848
+ 也就是获取 translate.setDocument(...) 定义的翻译区域中,翻译前,要参与翻译的文本。
3849
+ 其中像是 translate.ignore.tag 这种忽略翻译的标签,这里也不会获取的,这里只是获取实际要参与翻译的文本。
3850
+ */
3851
+ getTranslateAreaText:function(){
3852
+ //v3.16.1 优化,获取本地语种,针对开源中国只对 readme 部分进行翻译的场景,将针对设置的 translate.setDocument() 区域的元素的显示文本进行判定语种
3853
+ var translateAreaText = ''; //翻译区域内当前的文本
3854
+
3855
+ /** 构建虚拟容器,将要翻译的区域放入虚拟容器,以便后续处理 **/
3856
+ var virtualContainer = document.createElement('div'); // 创建虚拟容器,处理、判断也都是针对这个虚拟容器
3857
+ if(translate.documents != null && typeof(translate.documents) != 'undefined' && translate.documents.length > 0){
3858
+ // setDocuments 指定的
3859
+ for(var docs_index = 0; docs_index < translate.documents.length; docs_index++){
3860
+ var doc = translate.documents[docs_index];
3861
+ if(typeof(doc) != 'undefined' && doc != null && typeof(doc.innerText) != 'undefined' && doc.innerText != null && doc.innerText.length > 0){
3862
+ virtualContainer.appendChild(doc.cloneNode(true));
3863
+ }
3864
+ }
3865
+ }else{
3866
+ //未使用 setDocuments指定,那就是整个网页了
3867
+ //return document.all; //翻译所有的 这是 v3.5.0之前的
3868
+ //v3.5.0 之后采用 拿 html的最上层的demo,而不是 document.all 拿到可能几千个dom
3869
+ if(typeof(document.head) != 'undefined'){
3870
+ virtualContainer.appendChild(document.head.cloneNode(true));
3871
+ }
3872
+ if(typeof(document.body) != 'undefined'){
3873
+ virtualContainer.appendChild(document.body.cloneNode(true));
3874
+ }
3875
+ }
3876
+ //console.log(virtualContainer);
3877
+
3878
+
3879
+ /** 对虚拟容器中的元素进行处理,移除忽略的 tag (这里暂时就只是移除忽略的tag, 其他忽略的后续再加) **/
3880
+ // 遍历标签列表
3881
+ //console.log('---- remove element');
3882
+ for (var i = 0; i < translate.ignore.tag.length; i++) {
3883
+ var tagName = translate.ignore.tag[i];
3884
+ var elements = virtualContainer.querySelectorAll(tagName);
3885
+ // 将 NodeList 转换为数组
3886
+ var elementArray = Array.prototype.slice.call(elements);
3887
+ // 遍历并移除每个匹配的元素
3888
+ for (var j = 0; j < elementArray.length; j++) {
3889
+ var element = elementArray[j];
3890
+ if (element.parentNode) {
3891
+ //console.log(element);
3892
+ element.parentNode.removeChild(element);
3893
+ }
3894
+ }
3895
+ }
3896
+ //console.log('---- remove element end');
3517
3897
 
3518
- var bodyText = document.body.outerText;
3519
- if(bodyText == null || typeof(bodyText) == 'undefined' || bodyText.length < 1){
3898
+
3899
+ /*** 取过滤完后的文本字符 ***/
3900
+ translateAreaText = virtualContainer.innerText;
3901
+ if(translateAreaText == null || typeof(translateAreaText) == 'undefined' || translateAreaText.length < 1){
3520
3902
  //未取到,默认赋予简体中文
3521
3903
  translate.language.local = 'chinese_simplified';
3522
3904
  return;
3523
3905
  }
3906
+ // 移除所有空白字符(包括空格、制表符、换行符等)
3907
+ translateAreaText = translateAreaText.replace(/\s/g, '');
3908
+
3909
+ //console.log('translateAreaText:\n'+translateAreaText);
3910
+ return translateAreaText;
3911
+ },
3912
+ //自动识别当前页面是什么语种
3913
+ autoRecognitionLocalLanguage:function(){
3914
+ if(translate.language.local != null && translate.language.local.length > 2){
3915
+ //已设置过了,不需要再设置
3916
+ return translate.language.local;
3917
+ }
3524
3918
 
3525
- bodyText = bodyText.replace(/\n|\t|\r/g,''); //将回车换行等去掉
3919
+ var translateAreaText = translate.language.getTranslateAreaText();
3526
3920
 
3527
3921
  //默认赋予简体中文
3528
3922
  translate.language.local = 'chinese_simplified';
3529
- var recognition = translate.language.recognition(bodyText);
3923
+ var recognition = translate.language.recognition(translateAreaText);
3924
+ //console.log(recognition);
3530
3925
  translate.language.local = recognition.languageName;
3531
3926
  return translate.language.local;
3532
3927
  /* v3.1优化
@@ -3676,6 +4071,109 @@ var translate = {
3676
4071
  //console.log('get end');
3677
4072
  return langStrs;
3678
4073
  },
4074
+ /*
4075
+ 语种识别策略
4076
+
4077
+ str 要识别的字符串
4078
+ data 对于str字符串识别的结果,格式如:
4079
+ {
4080
+ languageName: 'english',
4081
+ languageArray:[
4082
+ english:[
4083
+ list[
4084
+ {beforeText: ' ', afterText: ' ', text: 'hello word'},
4085
+ {beforeText: ' ', afterText: ' ', text: 'who?'},
4086
+ ],
4087
+ number:12
4088
+ ],
4089
+ japanese:[
4090
+ ......
4091
+ ]
4092
+ ]
4093
+ }
4094
+ 有关这里面具体参数的说明,参考 translate.language.recognition 的说明
4095
+ languagesSize key:语言名, value:语言字符数
4096
+ allSize 当前所有发现的语种,加起来的总字符数,也就是 languagesSize 遍历所有的value相加的数
4097
+
4098
+ 最后,要 return data;
4099
+ */
4100
+ recognitionAlgorithm:function(str, data, languagesSize, allSize){
4101
+
4102
+ /*
4103
+ 如果英语跟罗曼语族(法语意大利语等多个语言)一起出现,且当前 data.languageName 认定是英语(也就是英文字符占比最大),那么要判定一下:
4104
+ 如果 罗曼语族的字符数/英文的字符数 > 0.008 , 那么认为当前是罗曼语族的中的某个语种, 在对其判定出具体是罗曼语族中的哪个语种赋予最终结果。
4105
+ */
4106
+ if(typeof(languagesSize['english']) != 'undefined' && typeof(languagesSize['romance']) != 'undefined' && data.languageName == 'english'){
4107
+ if(languagesSize['romance']/languagesSize['english'] > 0.008){
4108
+ //排定是罗曼语族了,那么判断一下到底是 法语、西班牙语、葡萄牙语、意大利语 中的哪一种呢
4109
+
4110
+ //先判定是否有设置本地语种是罗曼语族中其中的某一个
4111
+ if(typeof(translate.language.local) != 'undefined' && translate.language.local.length > 1){
4112
+ if(translate.language.englishVarietys.indexOf(translate.language.local) > -1){
4113
+ //发现当前设置的是小语种,那么将当前识别的语种识别为 本地设置的这个小语种。
4114
+ data.languageName = translate.language.local;
4115
+ }
4116
+ }
4117
+
4118
+ if(data.languageName == 'english'){
4119
+ //还是英语,那就是没有经过上面本地语种的判定,那进行罗曼语的具体语种识别
4120
+
4121
+ var romanceSentenceLanguage = translate.language.romanceSentenceAnaly(str);
4122
+ if(romanceSentenceLanguage.length == 0){
4123
+ console.log('语种识别异常,应该是 法语、西班牙语、葡萄牙语、意大利语 中的一种才是,除非是除了这四种语种之外的别的 罗曼语族 中的语种,当前已将 '+ str +'识别为英语。 你可以联系我们求助 https://translate.zvo.cn/4030.html');
4124
+ }else{
4125
+ data.languageName = romanceSentenceLanguage;
4126
+ }
4127
+ }
4128
+ }
4129
+ }
4130
+
4131
+
4132
+ /*
4133
+ 日语判定
4134
+ 如果发现日语存在,且当前 data.languageName 认定不是日语,那么要判定一下:
4135
+ 如果 日语的字符数/所有字符数 的字符数 > 0.08 , 那么认为当前是日语的
4136
+ */
4137
+ if( typeof(languagesSize['japanese']) != 'undefined' && data.languageName != 'japanese'){
4138
+ if(languagesSize['japanese']/allSize > 0.08){
4139
+ data.languageName = 'japanese'
4140
+ }
4141
+ }
4142
+
4143
+ /*
4144
+ 如果发现英语、简体中文或繁体中文 一起存在,且当前 data.languageName 认定是英语时,那么要判定一下:
4145
+ 如果 (简体中文+繁体中文)的字符数/英语 > 0.05 , 那么认为当前是简体中文(不认为是繁体中文,因为下面还有 简体中文跟繁体中文的判定)
4146
+ */
4147
+ if( (typeof(languagesSize['chinese_simplified']) != 'undefined' || typeof(languagesSize['chinese_traditional']) != 'undefined' ) && typeof(languagesSize['english']) != 'undefined' && data.languageName == 'english'){
4148
+ var size = 0;
4149
+ if(typeof(languagesSize['chinese_simplified']) != 'undefined'){
4150
+ size = size + languagesSize['chinese_simplified'];
4151
+ }
4152
+ if(typeof(languagesSize['chinese_traditional']) != 'undefined'){
4153
+ size = size + languagesSize['chinese_traditional'];
4154
+ }
4155
+ if(size/languagesSize['english'] > 0.05){
4156
+ data.languageName = 'chinese_simplified'
4157
+ }
4158
+ }
4159
+
4160
+
4161
+ /*
4162
+ 如果简体中文跟繁体中文一起出现,且当前 data.languageName 认定是简体中文(也就是简体中文字符占比最大),那么要判定一下繁体中文:
4163
+ 如果 繁体中文的字符数/简体中文的字符数 > 0.08 , 那么认为当前是繁体中文的
4164
+ */
4165
+ if(typeof(languagesSize['chinese_simplified']) != 'undefined' && typeof(languagesSize['chinese_traditional']) != 'undefined' && data.languageName == 'chinese_simplified'){
4166
+ if(languagesSize['chinese_traditional']/languagesSize['chinese_simplified'] > 0.03){
4167
+ data.languageName = 'chinese_traditional'
4168
+ }
4169
+ }
4170
+ /* if(langkeys.indexOf('chinese_simplified') > -1 && langkeys.indexOf('chinese_traditional') > -1){
4171
+ langsNumber['chinese_simplified'] = 0;
4172
+ } */
4173
+
4174
+
4175
+ return data;
4176
+ },
3679
4177
 
3680
4178
  /*
3681
4179
  * 识别字符串是什么语种。它是 get() 的扩展,以代替get返回更多
@@ -3686,12 +4184,14 @@ var translate = {
3686
4184
  {
3687
4185
  languageName: 'english',
3688
4186
  languageArray:[
3689
- "english":[
3690
- {beforeText: '', afterText: '', text: 'emoambue hag'},
3691
- ......
4187
+ english:[
4188
+ list[
4189
+ {beforeText: ' ', afterText: ' ', text: 'hello word'},
4190
+ {beforeText: ' ', afterText: ' ', text: 'who?'},
4191
+ ],
4192
+ number:12
3692
4193
  ],
3693
- "japanese":[
3694
- {beforeText: ' ', afterText: ' ', text: 'ẽ '},
4194
+ japanese:[
3695
4195
  ......
3696
4196
  ]
3697
4197
  ]
@@ -3711,7 +4211,12 @@ var translate = {
3711
4211
  var langsNumber = []; //key 语言名, value 语言字符数
3712
4212
  var langsNumberOriginal = []; //同上,只不过这个不会进行清空字符数
3713
4213
  var allNumber = 0;//总字数
4214
+
4215
+ /** 进行字数统计相关 - start **/
3714
4216
  for(var key in langs){
4217
+ if (!langs.hasOwnProperty(key)) {
4218
+ continue;
4219
+ }
3715
4220
  if(typeof(langs[key]) != 'object'){
3716
4221
  continue;
3717
4222
  }
@@ -3723,53 +4228,17 @@ var translate = {
3723
4228
  langsNumber[key] = langStrLength;
3724
4229
  langsNumberOriginal[key] = langStrLength;
3725
4230
  }
4231
+ /** 进行字数统计相关 - end **/
3726
4232
 
3727
- //过滤 语种的字符数小于总字符数 百分之五的,低于这个数,将忽略
3728
- var langkeys = [];
3729
- for(var lang in langsNumber){
3730
- if(langsNumber[lang]/allNumber > 0.01){
3731
- langkeys[langkeys.length] = lang+'';
3732
- }
3733
- }
3734
4233
 
3735
- if(langkeys.length > 1 && langkeys.indexOf('english') > -1){
3736
- //console.log('出现了english, 并且english跟其他语种一起出现那么删除english,因为什么法语德语乱七八糟的都有英语。而且中文跟英文一起,如果认为是英文的话,有时候中文会不被翻译');
3737
- //这里先判断一下是否有发现了 罗曼语族
3738
- if(langkeys.indexOf('romance') > -1){
3739
- //发现了,那么判断一下到底是 法语、西班牙语、葡萄牙语、意大利语 中的哪一种呢
3740
- var romanceSentenceLanguage = translate.language.romanceSentenceAnaly(str);
3741
- if(romanceSentenceLanguage.length == 0){
3742
- console.log('语种识别异常,应该是 法语、西班牙语、葡萄牙语、意大利语 中的一种才是,除非是除了这四种语种之外的别的 罗曼语族 中的语种,当前已将 '+ str +'识别为英语。 你可以联系我们求助 https://translate.zvo.cn/4030.html');
3743
- }else{
3744
- //console.log(langsNumber);
3745
- langsNumber[romanceSentenceLanguage] = langsNumber['romance']+langsNumber['english'];
3746
- //console.log('set romance to '+romanceSentenceLanguage+' : \t'+str);
3747
- langsNumber['english'] = 0;
3748
- }
3749
- }else{
3750
- langsNumber['english'] = 0;
3751
- }
3752
- }
3753
-
3754
- //如果简体中文跟繁体中文一起出现,那么会判断当前句子为繁体中文,将简体中文字符数置0
3755
- if(langkeys.indexOf('chinese_simplified') > -1 && langkeys.indexOf('chinese_traditional') > -1){
3756
- //langkeys.splice(langkeys.indexOf('chinese_simplified'), 1);
3757
- langsNumber['chinese_simplified'] = 0;
3758
- }
3759
-
3760
-
3761
- //如果发现日语字符,那么将发现的简体中文、繁体中文字符数量置零
3762
- if(langkeys.length > 1 && langkeys.indexOf('japanese') > -1){
3763
- langsNumber['chinese_simplified'] = 0;
3764
- langsNumber['chinese_traditional'] = 0;
3765
- }
3766
-
3767
-
3768
4234
 
3769
4235
  //从 langsNumber 中找出字数最多的来
3770
4236
  var maxLang = ''; //字数最多的语种
3771
4237
  var maxNumber = 0;
3772
4238
  for(var lang in langsNumber){
4239
+ if (!langsNumber.hasOwnProperty(lang)) {
4240
+ continue;
4241
+ }
3773
4242
  if(langsNumber[lang] > maxNumber){
3774
4243
  maxLang = lang;
3775
4244
  maxNumber = langsNumber[lang];
@@ -3779,6 +4248,9 @@ var translate = {
3779
4248
  //重新组合返回值的 languageArray
3780
4249
  var languageArray = {};
3781
4250
  for(var lang in langs){
4251
+ if (!langs.hasOwnProperty(lang)) {
4252
+ continue;
4253
+ }
3782
4254
  languageArray[lang] = {};
3783
4255
  languageArray[lang].number = langsNumberOriginal[lang];
3784
4256
  languageArray[lang].list = langs[lang];
@@ -3788,19 +4260,9 @@ var translate = {
3788
4260
  languageName: maxLang,
3789
4261
  languageArray: languageArray
3790
4262
  };
3791
-
3792
- //v3.4.2 增加,增加对法语等英文变种的小语种的识别
3793
- if(result.languageName == 'english'){
3794
- //如果识别的语种是英语,那便不仅仅是英语,因为法语、德语、意大利语也是在英语里面,还要判断一下是否用户自己设置了具体语种是否是英语延伸的小语种
3795
-
3796
- var localLang = translate.language.getLocal(); //本地语种
3797
- if(translate.language.englishVarietys.indexOf(localLang) > -1){
3798
- //发现当前设置的是小语种,那么将当前识别的语种识别为 本地设置的这个小语种。
3799
- result.languageName = localLang;
3800
- }
3801
- }
3802
-
3803
- return result;
4263
+
4264
+ //最后进行一层简单的算法处理
4265
+ return translate.language.recognitionAlgorithm(str, result, langsNumber, allNumber);
3804
4266
  },
3805
4267
  /*
3806
4268
  传入一个char,返回这个char属于什么语种,返回如 如果返回空字符串,那么表示未获取到是什么语种
@@ -4448,6 +4910,10 @@ var translate = {
4448
4910
  return;
4449
4911
  }
4450
4912
 
4913
+ if(typeof(translate.request.api.ip) != 'string' || translate.request.api.ip == null || translate.request.api.ip.length < 1){
4914
+ return;
4915
+ }
4916
+
4451
4917
  //如果用户浏览器没读到默认语言,或者默认语言没有对应到translate.js支持的语种,那么在采用ip识别的方式
4452
4918
  translate.request.post(translate.request.api.ip, {}, function(data){
4453
4919
  //console.log(data);
@@ -4517,7 +4983,7 @@ var translate = {
4517
4983
 
4518
4984
  //判断它后面是否还有文本
4519
4985
  if(originalIndex+1 < text.length){
4520
- let char = text.charAt(originalIndex+translateOriginal.length);
4986
+ let char = text.charAt(originalIndex+translateOriginal.length);
4521
4987
  //console.log(char);
4522
4988
  if(/。/.test(char)){
4523
4989
  replaceResultText = replaceResultText + '. ';
@@ -4531,8 +4997,12 @@ var translate = {
4531
4997
  }else if([' ', '\n','\t',']','|', '_','-','/'].indexOf(char) !== -1){
4532
4998
  // 如果后面的字符是 这些字符,那么不用添加空格隔开
4533
4999
  }else{
4534
- //补充上一个空格,用于将两个单词隔开
4535
- replaceResultText = replaceResultText + ' ';
5000
+ //补充上一个空格,用于将两个单词隔开。 不过 ,如果当前 replaceResultText 的最后一个字符也是空格,那就不需要再加空格了。 这里就只判断空格就好了,至于其他的换行等基本不会出现这个情况,所以不考虑
5001
+ if(replaceResultText.length > 0 && replaceResultText.charAt(replaceResultText.length-1) == ' '){
5002
+ //replaceResultText 本身有值,且最后一个字符就是空格,就不需要再追加空格进行隔开了
5003
+ }else{
5004
+ replaceResultText = replaceResultText + ' ';
5005
+ }
4536
5006
  }
4537
5007
 
4538
5008
  }
@@ -4553,16 +5023,23 @@ var translate = {
4553
5023
  replaceOriginalText = ':'+replaceOriginalText;
4554
5024
  }else if([' ', '\n','\t','[', '|', '_','-','/'].indexOf(char) !== -1){
4555
5025
  // 如果前面的字符是 这些字符,那么不用添加空格隔开
5026
+ //console.log('不需要空格隔开的');
4556
5027
  }else{
4557
- //补充上一个空格,用于将两个单词隔开
4558
- //text = text.replace(translateOriginal, ' '+translateResult);
4559
- replaceResultText = ' '+replaceResultText;
5028
+ //补充上一个空格,用于将两个单词隔开。 不过 ,如果当前 replaceResultText 的第一个字符也是空格,那就不需要再加空格了。 这里就只判断空格就好了,至于其他的换行等基本不会出现这个情况,所以不考虑
5029
+ if(replaceResultText.length > 0 && replaceResultText.charAt(0) == ' '){
5030
+ //replaceResultText 本身有值,且最后一个字符就是空格,就不需要再追加空格进行隔开了
5031
+ }else{
5032
+ replaceResultText = ' '+replaceResultText;
5033
+ }
5034
+ //console.log('before add space : '+replaceResultText);
4560
5035
  }
4561
5036
  }
4562
5037
  }else{
4563
5038
  //如果是其他语种比如英语法语翻译为中文、日文,那么标点符号也要判断的,这个因为目前这个场景还没咋遇到,就不判断了,遇到了在加。
4564
5039
 
4565
5040
  }
5041
+ //console.log(replaceResultText)
5042
+ //console.log(replaceResultText.length)
4566
5043
 
4567
5044
  let replaceResult = translate.util.replaceFromIndex(text, currentReplaceEndIndex, replaceOriginalText, replaceResultText);
4568
5045
  if(replaceResult.replaceEndIndex < 1){
@@ -4658,6 +5135,9 @@ var translate = {
4658
5135
  }
4659
5136
 
4660
5137
  for(var item in numbers){
5138
+ if (!numbers.hasOwnProperty(item)) {
5139
+ continue;
5140
+ }
4661
5141
  if(numbers[item]===maxNum){
4662
5142
  maxStr.push(item)
4663
5143
  }
@@ -5025,6 +5505,7 @@ var translate = {
5025
5505
  value: translate.js 的语种标识
5026
5506
  */
5027
5507
  browserLanguage:{
5508
+ 'zh':'chinese_simplified',
5028
5509
  'zh-CN':'chinese_simplified',
5029
5510
  'zh-TW':'chinese_traditional',
5030
5511
  'zh-HK':'chinese_traditional',
@@ -5198,7 +5679,43 @@ var translate = {
5198
5679
  }
5199
5680
 
5200
5681
  return result;
5682
+ },
5683
+
5684
+ /*js translate.util.getElementPosition start*/
5685
+ /*
5686
+ 计算一个元素在浏览器中的坐标系,其绝对定位、以及实际显示出来所占用的区域,宽、高
5687
+ */
5688
+ getElementPosition:function (node) {
5689
+ // 获取元素的边界矩形信息(相对于视口)
5690
+ const rect = node.getBoundingClientRect();
5691
+
5692
+ // 获取当前页面的滚动位置(兼容不同浏览器)
5693
+ const scrollX = window.scrollX || document.documentElement.scrollLeft;
5694
+ const scrollY = window.scrollY || document.documentElement.scrollTop;
5695
+
5696
+ // 计算元素在文档中的起始坐标
5697
+ const startX = rect.left + scrollX;
5698
+ const startY = rect.top + scrollY;
5699
+
5700
+ // 计算元素的宽度和高度
5701
+ const width = rect.right - rect.left;
5702
+ const height = rect.bottom - rect.top;
5703
+
5704
+ // 计算元素在文档中的结束坐标
5705
+ const endX = startX + width;
5706
+ const endY = startY + height;
5707
+
5708
+ // 返回包含所有信息的对象(使用ES5兼容语法)
5709
+ return {
5710
+ startX: startX,
5711
+ startY: startY,
5712
+ endX: endX,
5713
+ endY: endY,
5714
+ width: width,
5715
+ height: height
5716
+ };
5201
5717
  }
5718
+ /*js translate.util.getElementPosition end*/
5202
5719
  },
5203
5720
  //机器翻译采用哪种翻译服务
5204
5721
  service:{
@@ -5227,6 +5744,9 @@ var translate = {
5227
5744
  if(typeof(serviceName) == 'string'){
5228
5745
  translate.service.name = serviceName;
5229
5746
  if(serviceName != 'translate.service'){
5747
+ //增加元素整体翻译能力
5748
+ translate.whole.enableAll();
5749
+
5230
5750
  if(serviceName.toLowerCase() == 'giteeai'){
5231
5751
  //设定翻译接口为GiteeAI的
5232
5752
  translate.request.api.host=['https://giteeai.zvo.cn/','https://deutsch.enterprise.api.translate.zvo.cn:1000/','https://api.translate.zvo.cn:1000/'];
@@ -5237,9 +5757,6 @@ var translate = {
5237
5757
  translate.request.api.host=['https://siliconflow.zvo.cn/','https://america.api.translate.zvo.cn:1414/','https://deutsch.enterprise.api.translate.zvo.cn:1414/'];
5238
5758
  return;
5239
5759
  }
5240
-
5241
- //增加元素整体翻译能力
5242
- translate.whole.enableAll();
5243
5760
  }
5244
5761
  }
5245
5762
  },
@@ -5254,6 +5771,7 @@ var translate = {
5254
5771
  },
5255
5772
 
5256
5773
  language:{
5774
+
5257
5775
  json:[{"id":"ukrainian","name":"Україна","serviceId":"uk"},{"id":"norwegian","name":"Norge","serviceId":"no"},{"id":"welsh","name":"Iaith Weleg","serviceId":"cy"},{"id":"dutch","name":"nederlands","serviceId":"nl"},{"id":"japanese","name":"日本語","serviceId":"ja"},{"id":"filipino","name":"Pilipino","serviceId":"fil"},{"id":"english","name":"English","serviceId":"en"},{"id":"lao","name":"ກະຣຸນາ","serviceId":"lo"},{"id":"telugu","name":"తెలుగుName","serviceId":"te"},{"id":"romanian","name":"Română","serviceId":"ro"},{"id":"nepali","name":"नेपालीName","serviceId":"ne"},{"id":"french","name":"Français","serviceId":"fr"},{"id":"haitian_creole","name":"Kreyòl ayisyen","serviceId":"ht"},{"id":"czech","name":"český","serviceId":"cs"},{"id":"swedish","name":"Svenska","serviceId":"sv"},{"id":"russian","name":"Русский язык","serviceId":"ru"},{"id":"malagasy","name":"Malagasy","serviceId":"mg"},{"id":"burmese","name":"ဗာရမ်","serviceId":"my"},{"id":"pashto","name":"پښتوName","serviceId":"ps"},{"id":"thai","name":"คนไทย","serviceId":"th"},{"id":"armenian","name":"Արմենյան","serviceId":"hy"},{"id":"chinese_simplified","name":"简体中文","serviceId":"zh-CHS"},{"id":"persian","name":"Persian","serviceId":"fa"},{"id":"chinese_traditional","name":"繁體中文","serviceId":"zh-CHT"},{"id":"kurdish","name":"Kurdî","serviceId":"ku"},{"id":"turkish","name":"Türkçe","serviceId":"tr"},{"id":"hindi","name":"हिन्दी","serviceId":"hi"},{"id":"bulgarian","name":"български","serviceId":"bg"},{"id":"malay","name":"Malay","serviceId":"ms"},{"id":"swahili","name":"Kiswahili","serviceId":"sw"},{"id":"oriya","name":"ଓଡିଆ","serviceId":"or"},{"id":"icelandic","name":"ÍslandName","serviceId":"is"},{"id":"irish","name":"Íris","serviceId":"ga"},{"id":"khmer","name":"ភាសា​ខ្មែរName","serviceId":"km"},{"id":"gujarati","name":"ગુજરાતી","serviceId":"gu"},{"id":"slovak","name":"Slovenská","serviceId":"sk"},{"id":"kannada","name":"ಕನ್ನಡ್Name","serviceId":"kn"},{"id":"hebrew","name":"היברית","serviceId":"he"},{"id":"hungarian","name":"magyar","serviceId":"hu"},{"id":"marathi","name":"मराठीName","serviceId":"mr"},{"id":"tamil","name":"தாமில்","serviceId":"ta"},{"id":"estonian","name":"eesti keel","serviceId":"et"},{"id":"malayalam","name":"മലമാലം","serviceId":"ml"},{"id":"inuktitut","name":"ᐃᓄᒃᑎᑐᑦ","serviceId":"iu"},{"id":"arabic","name":"بالعربية","serviceId":"ar"},{"id":"deutsch","name":"Deutsch","serviceId":"de"},{"id":"slovene","name":"slovenščina","serviceId":"sl"},{"id":"bengali","name":"বেঙ্গালী","serviceId":"bn"},{"id":"urdu","name":"اوردو","serviceId":"ur"},{"id":"azerbaijani","name":"azerbaijani","serviceId":"az"},{"id":"portuguese","name":"português","serviceId":"pt"},{"id":"samoan","name":"lifiava","serviceId":"sm"},{"id":"afrikaans","name":"afrikaans","serviceId":"af"},{"id":"tongan","name":"汤加语","serviceId":"to"},{"id":"greek","name":"ελληνικά","serviceId":"el"},{"id":"indonesian","name":"IndonesiaName","serviceId":"id"},{"id":"spanish","name":"Español","serviceId":"es"},{"id":"danish","name":"dansk","serviceId":"da"},{"id":"amharic","name":"amharic","serviceId":"am"},{"id":"punjabi","name":"ਪੰਜਾਬੀName","serviceId":"pa"},{"id":"albanian","name":"albanian","serviceId":"sq"},{"id":"lithuanian","name":"Lietuva","serviceId":"lt"},{"id":"italian","name":"italiano","serviceId":"it"},{"id":"vietnamese","name":"Tiếng Việt","serviceId":"vi"},{"id":"korean","name":"한국어","serviceId":"ko"},{"id":"maltese","name":"Malti","serviceId":"mt"},{"id":"finnish","name":"suomi","serviceId":"fi"},{"id":"catalan","name":"català","serviceId":"ca"},{"id":"croatian","name":"hrvatski","serviceId":"hr"},{"id":"bosnian","name":"bosnian","serviceId":"bs-Latn"},{"id":"polish","name":"Polski","serviceId":"pl"},{"id":"latvian","name":"latviešu","serviceId":"lv"},{"id":"maori","name":"Maori","serviceId":"mi"}],
5258
5776
  /*
5259
5777
  获取map形式的语言列表
@@ -5282,11 +5800,22 @@ var translate = {
5282
5800
  var textArray = JSON.parse(decodeURIComponent(data.text));
5283
5801
  let translateTextArray = translate.util.split(textArray, 40000, 900);
5284
5802
 
5285
- translate.request.send(translate.service.edge.api.auth, {}, function(auth){
5803
+ translate.request.send(translate.service.edge.api.auth, {},{}, function(auth){
5804
+ var appendXhrData = {
5805
+ "from":data.from+'',
5806
+ "to":data.to,
5807
+ "text":data.text
5808
+ };
5286
5809
  var from = data.from;
5287
5810
  if(from != 'auto'){
5288
- from = translate.service.edge.language.getMap()[data.from];
5811
+ if(from == 'romance'){
5812
+ //这里额外加了一个罗曼语族(romance)会自动认为是法语(fr)
5813
+ from = 'fr';
5814
+ }else{
5815
+ from = translate.service.edge.language.getMap()[data.from];
5816
+ }
5289
5817
  }
5818
+
5290
5819
  var to = translate.service.edge.language.getMap()[data.to];
5291
5820
  var transUrl = translate.service.edge.api.translate.replace('{from}',from).replace('{to}',to);
5292
5821
 
@@ -5297,7 +5826,7 @@ var translate = {
5297
5826
  json.push({"Text":translateTextArray[tai][i]});
5298
5827
  }
5299
5828
 
5300
- translate.request.send(transUrl, JSON.stringify(json), function(result){
5829
+ translate.request.send(transUrl, JSON.stringify(json), appendXhrData, function(result){
5301
5830
  var d = {};
5302
5831
  d.info = 'SUCCESS';
5303
5832
  d.result = 1;
@@ -5570,6 +6099,10 @@ var translate = {
5570
6099
  'content-type':'application/x-www-form-urlencoded',
5571
6100
  };
5572
6101
 
6102
+ if(typeof(translate.request.api.connectTest) != 'string' || translate.request.api.connectTest == null || translate.request.api.connectTest.length < 1){
6103
+ return;
6104
+ }
6105
+
5573
6106
 
5574
6107
  translate.request.speedDetectionControl.checkHostQueue = []; //用于实际存储
5575
6108
  translate.request.speedDetectionControl.checkHostQueueMap = []; //只是map,通过key取值,无其他作用
@@ -5591,6 +6124,7 @@ var translate = {
5591
6124
  translate.request.send(
5592
6125
  host+translate.request.api.connectTest,
5593
6126
  {host:host},
6127
+ {host:host},
5594
6128
  function(data){
5595
6129
  var host = data.info;
5596
6130
  var map = translate.request.speedDetectionControl.checkHostQueueMap[host];
@@ -5623,7 +6157,7 @@ var translate = {
5623
6157
  headers,
5624
6158
  function(data){
5625
6159
  //translate.request.speedDetectionControl.checkResponseSpeed_Storage(host, time);
5626
- var hostUrl = data.requestURL.replace('connectTest.json','');
6160
+ var hostUrl = data.requestURL.replace(translate.request.api.connectTest,'');
5627
6161
  translate.request.speedDetectionControl.checkResponseSpeed_Storage(hostUrl, translate.request.speedDetectionControl.disableTime);
5628
6162
  },
5629
6163
  false
@@ -5727,12 +6261,13 @@ var translate = {
5727
6261
  }
5728
6262
  // ------- edge end --------
5729
6263
 
5730
- this.send(path, data, func, 'post', true, headers, abnormalFunc, true);
6264
+ this.send(path, data, data, func, 'post', true, headers, abnormalFunc, true);
5731
6265
  },
5732
6266
  /**
5733
6267
  * 发送请求
5734
6268
  * url 请求的url或者path(path,传入的是translate.request.api.translate 这种的,需要使用 getUrl 来组合真正请求的url )
5735
6269
  * data 请求的数据,如 {"author":"管雷鸣",'site':'www.guanleiming.com'}
6270
+ * appendXhrData 附加到 xhr.data 中的对象数据,传入比如 {"from":"english","to":"japanese"} ,他会直接赋予 xhr.data
5736
6271
  * func 请求完成的回调,传入如 function(data){}
5737
6272
  * method 请求方式,可传入 post、get
5738
6273
  * isAsynchronize 是否是异步请求, 传入 true 是异步请求,传入false 是同步请求。 如果传入false,则本方法返回xhr
@@ -5740,7 +6275,7 @@ var translate = {
5740
6275
  * abnormalFunc 响应异常所执行的方法,响应码不是200就会执行这个方法 ,传入如 function(xhr){} 另外这里的 xhr 会额外有个参数 xhr.requestURL 返回当前请求失败的url
5741
6276
  * showErrorLog 是否控制台打印出来错误日志,true打印, false 不打印
5742
6277
  */
5743
- send:function(url, data, func, method, isAsynchronize, headers, abnormalFunc, showErrorLog){
6278
+ send:function(url, data, appendXhrData, func, method, isAsynchronize, headers, abnormalFunc, showErrorLog){
5744
6279
  //post提交的参数
5745
6280
  var params = '';
5746
6281
 
@@ -5749,7 +6284,7 @@ var translate = {
5749
6284
  }
5750
6285
 
5751
6286
  if(typeof(data) == 'string'){
5752
- params = data; //payload 方式
6287
+ params = data; //payload 方式 , edge 的方式
5753
6288
  }else{
5754
6289
  //表单提交方式
5755
6290
 
@@ -5758,6 +6293,9 @@ var translate = {
5758
6293
 
5759
6294
  //追加附加参数
5760
6295
  for(var apindex in translate.request.appendParams){
6296
+ if (!translate.request.appendParams.hasOwnProperty(apindex)) {
6297
+ continue;
6298
+ }
5761
6299
  data[apindex] = translate.request.appendParams[apindex];
5762
6300
  }
5763
6301
 
@@ -5770,6 +6308,9 @@ var translate = {
5770
6308
 
5771
6309
  //组合参数
5772
6310
  for(var index in data){
6311
+ if (!data.hasOwnProperty(index)) {
6312
+ continue;
6313
+ }
5773
6314
  if(params.length > 0){
5774
6315
  params = params + '&';
5775
6316
  }
@@ -5790,17 +6331,24 @@ var translate = {
5790
6331
  }catch(e){
5791
6332
  xhr=new ActiveXObject("Microsoft.XMLHTTP");
5792
6333
  }
6334
+ xhr.data=appendXhrData;
5793
6335
  //2.调用open方法(true----异步)
5794
6336
  xhr.open(method,url,isAsynchronize);
5795
6337
  //设置headers
5796
6338
  if(headers != null){
5797
6339
  for(var index in headers){
6340
+ if (!headers.hasOwnProperty(index)) {
6341
+ continue;
6342
+ }
5798
6343
  xhr.setRequestHeader(index,headers[index]);
5799
6344
  }
5800
6345
  }
5801
6346
 
5802
6347
  //追加附加参数
5803
6348
  for(var ahindex in translate.request.appendHeaders){
6349
+ if (!translate.request.appendHeaders.hasOwnProperty(ahindex)) {
6350
+ continue;
6351
+ }
5804
6352
  xhr.setRequestHeader(ahindex,translate.request.appendHeaders[ahindex]);
5805
6353
  }
5806
6354
 
@@ -5937,41 +6485,83 @@ var translate = {
5937
6485
  return;
5938
6486
  }
5939
6487
  }
5940
-
5941
- /*
5942
- for(var i = texts.length - 1; i >= 0; i--){
5943
- console.log(texts[i]);
6488
+ //console.log(obj);
6489
+ //返回的翻译结果,下标跟 obj.texts 一一对应的
6490
+ var translateResultArray = new Array();
5944
6491
 
6492
+ // 筛选需要翻译的文本及其原始索引
6493
+ var apiTranslateText = [];
6494
+ var apiTranslateArray = {};
6495
+ for(var i = 0; i < texts.length; i++){
5945
6496
  //判断是否在浏览器缓存中出现了
5946
- var cacheHash = translate.util.hash(texts[i]);
5947
- var cache = translate.storage.get('hash_'+translate.to+'_'+cacheHash);
6497
+ var hash = translate.util.hash(texts[i]);
6498
+ var cache = translate.storage.get('hash_'+to+'_'+hash);
6499
+ //console.log(hash+'\t'+texts[i]+'\t'+cache);
5948
6500
  if(cache != null && cache.length > 0){
5949
6501
  //缓存中发现了这个得结果,那这个就不需要再进行翻译了
5950
- }
6502
+ translateResultArray[i] = cache;
6503
+ }else{
6504
+ translateResultArray[i] = '';
6505
+ apiTranslateText.push(texts[i]);
6506
+ apiTranslateArray[hash] = i;
6507
+ }
6508
+ }
6509
+ if (apiTranslateText.length == 0) {
6510
+ //没有需要进行通过网络API翻译的任务了,全部命中缓存,那么直接返回
6511
+ var data = {
6512
+ from:from,
6513
+ to: to,
6514
+ text:translateResultArray,
6515
+ result:1
6516
+ };
6517
+ //console.log(data);
6518
+ func(data);
6519
+ return;
6520
+ }
6521
+
6522
+
6523
+
6524
+ //还有需要进行通过API接口进行翻译的文本,需要调用翻译接口
6525
+ if(typeof(translate.request.api.translate) != 'string' || translate.request.api.translate == null || translate.request.api.translate.length < 1){
6526
+ //用户已经设置了不掉翻译接口进行翻译
6527
+ return;
5951
6528
  }
5952
- */
5953
-
5954
6529
 
5955
6530
  var url = translate.request.api.translate;
5956
6531
  var data = {
5957
6532
  from:from,
5958
6533
  to: to,
5959
- text:encodeURIComponent(JSON.stringify(texts))
6534
+ text:encodeURIComponent(JSON.stringify(apiTranslateText))
5960
6535
  };
5961
- //console.log(data);
5962
- translate.request.post(url, data, function(data){
6536
+ //console.log(apiTranslateText);
6537
+ translate.request.post(url, data, function(resultData){
6538
+ //console.log(resultData);
5963
6539
  //console.log(data);
5964
- if(data.result == 0){
6540
+ if(resultData.result == 0){
5965
6541
  console.log('=======ERROR START=======');
5966
- console.log('from : '+data.from);
5967
- console.log('to : '+data.to);
6542
+ console.log('from : '+resultData.from);
6543
+ console.log('to : '+resultData.to);
5968
6544
  console.log('translate text array : '+texts);
5969
- console.log('response : '+data.info);
6545
+ console.log('response : '+resultData.info);
5970
6546
  console.log('=======ERROR END =======');
5971
6547
  //return;
5972
6548
  }
5973
6549
 
5974
- func(data);
6550
+ for(var i = 0; i < resultData.text.length; i++){
6551
+ //将翻译结果以 key:hash value翻译结果的形式缓存
6552
+ var hash = translate.util.hash(apiTranslateText[i]);
6553
+ translate.storage.set('hash_'+to+'_'+hash, resultData.text[i]);
6554
+ //如果离线翻译启用了全部提取,那么还要存入离线翻译指定存储
6555
+ if(translate.office.fullExtract.isUse){
6556
+ translate.office.fullExtract.set(hash, apiTranslateText[i], data.to, resultData.text[i]);
6557
+ }
6558
+
6559
+ //进行组合数据到 translateResultArray
6560
+ translateResultArray[apiTranslateArray[hash]] = resultData.text[i];
6561
+ }
6562
+ resultData.text = translateResultArray;
6563
+
6564
+ func(resultData);
5975
6565
  }, null);
5976
6566
  },
5977
6567
  listener:{
@@ -6030,6 +6620,8 @@ var translate = {
6030
6620
  trigger:function(url){
6031
6621
  return true;
6032
6622
  },
6623
+
6624
+ /*js translate.request.listener.start start*/
6033
6625
  /*
6034
6626
  启动根据ajax请求来自动触发执行翻译,避免有时候有的框架存在漏翻译的情况。
6035
6627
  这个只需要执行一次即可,如果执行多次,只有第一次会生效
@@ -6145,6 +6737,7 @@ var translate = {
6145
6737
  }
6146
6738
 
6147
6739
  }
6740
+ /*js translate.request.listener.start end*/
6148
6741
  }
6149
6742
  },
6150
6743
  //存储,本地缓存
@@ -6293,6 +6886,9 @@ var translate = {
6293
6886
  }
6294
6887
  */
6295
6888
  for(var key in queueArray){
6889
+ if (!queueArray.hasOwnProperty(key)) {
6890
+ continue;
6891
+ }
6296
6892
  translate.images.queues[key] = queueArray[key];
6297
6893
  }
6298
6894
  },
@@ -6387,50 +6983,81 @@ var translate = {
6387
6983
  //对翻译结果进行复原。比如当前网页是简体中文的,被翻译为了英文,执行此方法即可复原为网页本身简体中文的状态,而无需在通过刷新页面来实现
6388
6984
  reset:function(){
6389
6985
  var currentLanguage = translate.language.getCurrent(); //获取当前翻译至的语种
6986
+
6987
+ var lastUuid = ''; //最后一次的uuid
6390
6988
  for(var queue in translate.nodeQueue){
6391
- //console.log(queue);
6392
- for(var lang in translate.nodeQueue[queue].list){
6393
- //console.log(lang);
6394
-
6395
- for(var hash in translate.nodeQueue[queue].list[lang]){
6396
- var item = translate.nodeQueue[queue].list[lang][hash];
6397
- //console.log(item);
6398
- for(var index in item.nodes){
6399
- //console.log(item.nodes[index]);
6400
- //item.nodes[index].node.nodeValue = item.original;
6401
- var currentShow = translate.storage.get('hash_'+currentLanguage+'_'+hash); //当前显示出来的文字,也就是已经翻译后的文字
6402
- //console.log('hash_'+lang+'_'+hash+' -- '+currentShow);
6403
- if(typeof(currentShow) == 'undefined'){
6404
- continue;
6405
- }
6406
- if(currentShow == null){
6407
- continue;
6408
- }
6409
- if(currentShow.length == 0){
6410
- continue;
6411
- }
6989
+ if (!translate.nodeQueue.hasOwnProperty(queue)) {
6990
+ continue;
6991
+ }
6992
+ lastUuid = queue;
6993
+ }
6994
+ //console.log(queue);
6995
+
6996
+ if(lastUuid == ''){
6997
+ console.log('提示,你当前还未执行过翻译,所以你无需执行 translate.reset(); 进行还原。');
6998
+ return;
6999
+ }
7000
+
7001
+ for(var lang in translate.nodeQueue[lastUuid].list){
7002
+ if (!translate.nodeQueue[lastUuid].list.hasOwnProperty(lang)) {
7003
+ continue;
7004
+ }
7005
+ //console.log(lang);
7006
+
7007
+ for(var hash in translate.nodeQueue[lastUuid].list[lang]){
7008
+ if (!translate.nodeQueue[lastUuid].list[lang].hasOwnProperty(hash)) {
7009
+ continue;
7010
+ }
7011
+ var item = translate.nodeQueue[lastUuid].list[lang][hash];
7012
+ //console.log(item);
7013
+ for(var index in item.nodes){
7014
+ if (!item.nodes.hasOwnProperty(index)) {
7015
+ continue;
7016
+ }
7017
+ //console.log(item.nodes[index]);
7018
+ //item.nodes[index].node.nodeValue = item.original;
7019
+ var currentShow = translate.storage.get('hash_'+currentLanguage+'_'+hash); //当前显示出来的文字,也就是已经翻译后的文字
7020
+ //console.log('hash_'+lang+'_'+hash+' -- '+currentShow);
7021
+ if(typeof(currentShow) == 'undefined'){
7022
+ continue;
7023
+ }
7024
+ if(currentShow == null){
7025
+ continue;
7026
+ }
7027
+ if(currentShow.length == 0){
7028
+ continue;
7029
+ }
6412
7030
  /*
6413
- if(item.beforeText.length > 0 || item.afterText.length > 0){
6414
- console.log('----'+currentShow);
6415
- console.log(item);
6416
- }
6417
-
6418
- if(item.beforeText.length > 0){
6419
- currentShow = currentShow.substring(currentShow.lastIndexOf(item.beforeText)+1, currentShow.length);
6420
- }
6421
- if(item.afterText.length > 0){
6422
- currentShow = currentShow.substring(0, currentShow.lastIndexOf(item.afterText));
6423
- }
6424
- if(item.beforeText.length > 0 || item.afterText.length > 0){
6425
- console.log(currentShow);
6426
- }
7031
+ if(item.beforeText.length > 0 || item.afterText.length > 0){
7032
+ console.log('----'+currentShow);
7033
+ console.log(item);
7034
+ }
7035
+
7036
+ if(item.beforeText.length > 0){
7037
+ currentShow = currentShow.substring(currentShow.lastIndexOf(item.beforeText)+1, currentShow.length);
7038
+ }
7039
+ if(item.afterText.length > 0){
7040
+ currentShow = currentShow.substring(0, currentShow.lastIndexOf(item.afterText));
7041
+ }
7042
+ if(item.beforeText.length > 0 || item.afterText.length > 0){
7043
+ console.log(currentShow);
7044
+ }
6427
7045
  */
6428
- translate.element.nodeAnalyse.analyse(item.nodes[index].node, currentShow, item.original, item.nodes[index].node.attribute);
7046
+ // v3.16.5 针对gitee readme 接入优化
7047
+ if(typeof(item.nodes[index].node) == 'undefined'){
7048
+ continue;
6429
7049
  }
7050
+
7051
+ var attribute = typeof(item.nodes[index].node.attribute) == 'undefined' ? null:item.nodes[index].node.attribute;
7052
+ var analyse = translate.element.nodeAnalyse.analyse(item.nodes[index].node, '', '', attribute);
7053
+ translate.element.nodeAnalyse.analyse(item.nodes[index].node, analyse.text, item.original, attribute);
6430
7054
  }
6431
7055
  }
6432
7056
  }
6433
7057
 
7058
+
7059
+
7060
+
6434
7061
  //清除设置storage中的翻译至的语种
6435
7062
  translate.storage.set('to', '');
6436
7063
  translate.to = null;
@@ -6454,6 +7081,13 @@ var translate = {
6454
7081
  if (curSelection.anchorOffset == curSelection.focusOffset) return;
6455
7082
  let translateText = window.getSelection().toString();
6456
7083
 
7084
+ //还有需要进行通过API接口进行翻译的文本,需要调用翻译接口
7085
+ if(typeof(translate.request.api.translate) != 'string' || translate.request.api.translate == null || translate.request.api.translate.length < 1){
7086
+ //用户已经设置了不掉翻译接口进行翻译
7087
+ console.log('已设置了不使用 translate 翻译接口,翻译请求被阻止');
7088
+ return;
7089
+ }
7090
+
6457
7091
  //简单Copy原有代码了
6458
7092
  var url = translate.request.api.translate
6459
7093
  var data = {
@@ -6554,10 +7188,15 @@ var translate = {
6554
7188
  return;
6555
7189
  }
6556
7190
  translate.init_execute = '已进行';
7191
+
7192
+ if(typeof(translate.request.api.init) != 'string' || translate.request.api.init == null || translate.request.api.init.length < 1){
7193
+ return;
7194
+ }
6557
7195
  try{
6558
7196
  translate.request.send(
6559
7197
  translate.request.api.init,
6560
7198
  {},
7199
+ {},
6561
7200
  function(data){
6562
7201
  if (data.result == 0){
6563
7202
  console.log('translate.js init 初始化异常:'+data.info);
@@ -6571,8 +7210,6 @@ var translate = {
6571
7210
  if(newVersion > currentVersion){
6572
7211
  console.log('Tip : translate.js find new version : '+data.version);
6573
7212
  }
6574
- }else{
6575
- eval(data.info);
6576
7213
  }
6577
7214
  },
6578
7215
  'post',
@@ -6654,6 +7291,35 @@ var translate = {
6654
7291
  setUITip:function(tip){
6655
7292
  translate.progress.api.isTip = tip;
6656
7293
  },
7294
+ //移除子元素(无限级别)中的所有 class name 的loading 遮罩
7295
+ //level 层级,数字,比如第一次调用,传入1, 第一次里面产生的第二次调用,这里就是2
7296
+ removeChildClass:function(node, level){
7297
+
7298
+ //判断是否有子元素,判断其两级子元素,是否有加了loading遮罩了
7299
+ var childNodes = node.childNodes;
7300
+ if(childNodes == null || typeof(childNodes) == 'undefined'){
7301
+
7302
+ }else if(childNodes.length > 0){
7303
+ for(var i = 0; i<childNodes.length; i++){
7304
+ translate.progress.api.removeChildClass(childNodes[i], level+1);
7305
+ }
7306
+ }
7307
+
7308
+ if(level == 1){
7309
+ //第一次调用,是不删除本身的class name
7310
+ return;
7311
+ }
7312
+ if(typeof(node) == 'undefined'){
7313
+ return;
7314
+ }
7315
+ if(typeof(node.className) != 'string'){
7316
+ return;
7317
+ }
7318
+ if(node.className.indexOf('translate_api_in_progress') < -1){
7319
+ return;
7320
+ }
7321
+ node.className = node.className.replace(/translate_api_in_progress/g, '');
7322
+ },
6657
7323
  startUITip:function(){
6658
7324
  // 创建一个 style 元素
6659
7325
  const style = document.createElement('style');
@@ -6665,49 +7331,67 @@ var translate = {
6665
7331
 
6666
7332
  if(translate.progress.api.isTip){
6667
7333
  translate.listener.execute.renderStartByApi.push(function(uuid, from, to){
6668
- for(var lang in translate.nodeQueue[uuid].list){
6669
- //console.log('lang:'+lang);
6670
- //console.log(translate.nodeQueue[uuid].list[lang]);
6671
- if(translate.language.getCurrent() == lang){
6672
- //忽略这个语种
6673
- //console.log('ignore-------');
6674
- continue;
6675
- }
6676
-
6677
- for(var hash in translate.nodeQueue[uuid].list[lang]){
6678
- for(var nodeindex in translate.nodeQueue[uuid].list[lang][hash].nodes){
6679
- var node = translate.nodeQueue[uuid].list[lang][hash].nodes[nodeindex].node;
6680
- //console.log(node);
6681
- var nodeParent = node.parentNode;
6682
- if(nodeParent == null){
6683
- continue;
6684
- }
6685
- /* 这里先不考虑多隐藏的问题,只要符合的都隐藏,宁愿吧一些不需要隐藏的也会跟着一起隐藏
6686
- if(nodeParent.childNodes.length != 1){
6687
- //这个文本节点所在的元素里,不止有这一个文本元素,还有别的文本元素
6688
- continue;
6689
- }
6690
- */
6691
- if(typeof(nodeParent.className) == 'undefined' || nodeParent.className == null || nodeParent.className == ''){
6692
- nodeParent.className = ' translate_api_in_progress';
6693
- }else{
6694
- //这个元素本身有class了,那就追加
6695
- if(nodeParent.className.indexOf('translate_api_in_progress') > -1){
6696
- continue;
6697
- }
7334
+
7335
+ for(var hash in translate.nodeQueue[uuid].list[from]){
7336
+ if (!translate.nodeQueue[uuid].list[from].hasOwnProperty(hash)) {
7337
+ continue;
7338
+ }
7339
+ for(var nodeindex in translate.nodeQueue[uuid].list[from][hash].nodes){
7340
+ if (!translate.nodeQueue[uuid].list[from][hash].nodes.hasOwnProperty(nodeindex)) {
7341
+ continue;
7342
+ }
7343
+ var node = translate.nodeQueue[uuid].list[from][hash].nodes[nodeindex].node;
7344
+
7345
+ if(typeof(node) == 'undefined' || typeof(node.parentNode) == 'undefined'){
7346
+ continue;
7347
+ }
7348
+ var nodeParent = node.parentNode;
7349
+ if(nodeParent == null){
7350
+ continue;
7351
+ }
7352
+ /* 这里先不考虑多隐藏的问题,只要符合的都隐藏,宁愿吧一些不需要隐藏的也会跟着一起隐藏
7353
+ if(nodeParent.childNodes.length != 1){
7354
+ //这个文本节点所在的元素里,不止有这一个文本元素,还有别的文本元素
7355
+ continue;
7356
+ }
7357
+ */
7358
+
7359
+
7360
+ //判断其在上一层的父级是否已经加了,如果父级加了,那作为子集就不需要在加了,免得出现两个重合的 loading 遮罩
7361
+ var nodeParentParent = node.parentNode;
7362
+ if(nodeParentParent != null && typeof(nodeParentParent.className) != 'undefined' && nodeParentParent.className != null && nodeParent.className.indexOf('translate_api_in_progress') > -1){
7363
+ //父有了,那么子就不需要再加了
7364
+ continue;
7365
+ }
7366
+ //判断是否有子元素,判断其两级子元素,是否有加了loading遮罩了
7367
+ translate.progress.api.removeChildClass(nodeParent, 1);
6698
7368
 
6699
- nodeParent.className = nodeParent.className+' translate_api_in_progress';
7369
+
7370
+ if(typeof(nodeParent.className) == 'undefined' || nodeParent.className == null || nodeParent.className == ''){
7371
+ nodeParent.className = ' translate_api_in_progress';
7372
+ }else{
7373
+ //这个元素本身有class了,那就追加
7374
+ if(nodeParent.className.indexOf('translate_api_in_progress') > -1){
7375
+ continue;
6700
7376
  }
7377
+ nodeParent.className = nodeParent.className+' translate_api_in_progress';
7378
+ }
6701
7379
 
6702
- }
6703
- }
6704
- }
7380
+ }
7381
+ }
7382
+
6705
7383
  });
6706
7384
  translate.listener.execute.renderFinishByApi.push(function(uuid, from, to){
6707
- //console.log('uuid:'+uuid+', from:'+from+', to:'+to);
6708
-
6709
- for(var hash in translate.nodeQueue[uuid].list[from]){
7385
+ for(var hash in translate.nodeQueue[uuid].list[from]){
7386
+ if (!translate.nodeQueue[uuid].list[from].hasOwnProperty(hash)) {
7387
+ continue;
7388
+ }
7389
+
6710
7390
  for(var nodeindex in translate.nodeQueue[uuid].list[from][hash].nodes){
7391
+ if (!translate.nodeQueue[uuid].list[from][hash].nodes.hasOwnProperty(nodeindex)) {
7392
+ continue;
7393
+ }
7394
+
6711
7395
  var node = translate.nodeQueue[uuid].list[from][hash].nodes[nodeindex].node;
6712
7396
  var nodeParent = node.parentNode;
6713
7397
  if(nodeParent == null){
@@ -6729,6 +7413,7 @@ var translate = {
6729
7413
  continue;
6730
7414
  }
6731
7415
 
7416
+
6732
7417
  nodeParent.className = parentClassName.replace(/translate_api_in_progress/g, '');
6733
7418
  //nodeParent.className = parentClassName.replace(/loading/g, '');
6734
7419
  }
@@ -7313,9 +7998,366 @@ var translate = {
7313
7998
  _requestContext: null
7314
7999
 
7315
8000
  }
7316
- }
8001
+ },
7317
8002
 
7318
8003
  /*js translate.network end*/
8004
+
8005
+
8006
+ /*js translate.visual start*/
8007
+ /*
8008
+ 人眼所看到的纯视觉层的处理
8009
+ */
8010
+ visual: {
8011
+ /**
8012
+ * 获取一组节点的视觉矩形信息
8013
+ * @param {Node[]} nodes - 节点数组,格式如
8014
+ * [node1,node2,node3]
8015
+ * @returns {Object[]} - 矩形信息数组,与输入节点一一对应
8016
+ */
8017
+ getRects:function(nodes){
8018
+ return nodes.map(node => {
8019
+ if (!node) return null;
8020
+
8021
+ let rect;
8022
+ if (node.nodeType === Node.TEXT_NODE) {
8023
+ const range = document.createRange();
8024
+ range.selectNodeContents(node);
8025
+ const rects = range.getClientRects();
8026
+ //console.log(rect);
8027
+ rect = rects.length > 0 ? rects[0] : null;
8028
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
8029
+ rect = node.getBoundingClientRect();
8030
+ }
8031
+
8032
+ return rect ? {
8033
+ node,
8034
+ left: rect.left,
8035
+ top: rect.top,
8036
+ right: rect.right,
8037
+ bottom: rect.bottom,
8038
+ width: rect.width,
8039
+ height: rect.height
8040
+ } : null;
8041
+ });
8042
+ },
8043
+ /*
8044
+ 对一组坐标进行排序
8045
+ 按开始坐标从左到右、从上到下排序
8046
+ @param rects translate.visual.getRects获取到的坐标数据
8047
+ */
8048
+ coordinateSort:function(rects){
8049
+ // 按从左到右、从上到下排序
8050
+ const sortedRects = rects
8051
+ .filter(rect => rect !== null)
8052
+ .sort((a, b) => {
8053
+ if (Math.abs(a.top - b.top) < 5) { // 同一行
8054
+ return a.left - b.left;
8055
+ }
8056
+ return a.top - b.top;
8057
+ });
8058
+ return sortedRects;
8059
+ },
8060
+ /**
8061
+ * 查找左右紧邻的矩形对
8062
+ * @param rects translate.visual.getRects获取到的坐标数据
8063
+ * @returns {Array<{before: Object, after: Object}>} - 左右紧邻的矩形对数组
8064
+ */
8065
+ afterAdjacent:function(rects){
8066
+ var sortedRects = translate.visual.coordinateSort(rects);
8067
+
8068
+ const adjacentPairs = [];
8069
+ const lineGroups = translate.visual.groupRectsByLine(sortedRects);
8070
+
8071
+ // 检查每行中的所有紧邻元素对
8072
+ lineGroups.forEach(line => {
8073
+ for (let i = 0; i < line.length; i++) {
8074
+ for (let j = i + 1; j < line.length; j++) {
8075
+ const prev = line[i];
8076
+ const next = line[j];
8077
+
8078
+ // 如果后续元素与当前元素不紧邻,则后续其他元素也不可能紧邻
8079
+ if (!translate.visual.areHorizontallyAdjacent(prev, next)) {
8080
+ break;
8081
+ }
8082
+
8083
+ adjacentPairs.push({ before: prev, after: next });
8084
+ }
8085
+ }
8086
+ });
8087
+
8088
+ return adjacentPairs;
8089
+ },
8090
+ /**
8091
+ * 按行分组矩形
8092
+ * @param rects - 排序后的矩形数组 @param rects translate.visual.coordinateSort 获取到的坐标数据
8093
+ * @returns {Object[][]} - 按行分组的矩形
8094
+ */
8095
+ groupRectsByLine:function(rects){
8096
+ const lineGroups = [];
8097
+ let currentLine = [];
8098
+
8099
+ rects.forEach(rect => {
8100
+ if (currentLine.length === 0) {
8101
+ currentLine.push(rect);
8102
+ } else {
8103
+ const lastRect = currentLine[currentLine.length - 1];
8104
+ // 如果在同一行,则添加到当前行
8105
+ if (Math.abs(rect.top - lastRect.top) < 5) {
8106
+ currentLine.push(rect);
8107
+ } else {
8108
+ // 否则开始新的一行
8109
+ lineGroups.push(currentLine);
8110
+ currentLine = [rect];
8111
+ }
8112
+ }
8113
+ });
8114
+
8115
+ // 添加最后一行
8116
+ if (currentLine.length > 0) {
8117
+ lineGroups.push(currentLine);
8118
+ }
8119
+
8120
+ return lineGroups;
8121
+ },
8122
+ /**
8123
+ * 判断两个矩形是否水平紧邻
8124
+ * @param {Object} rect1 - 第一个矩形
8125
+ * @param {Object} rect2 - 第二个矩形
8126
+ * @returns {boolean} - 是否水平紧邻
8127
+ */
8128
+ areHorizontallyAdjacent:function(rect1, rect2){
8129
+ // 检查垂直方向是否有重叠(在同一行)
8130
+ const verticalOverlap = Math.min(rect1.bottom, rect2.bottom) - Math.max(rect1.top, rect2.top);
8131
+
8132
+ // 检查水平间距是否在阈值范围内
8133
+ const horizontalGap = rect2.left - rect1.right;
8134
+
8135
+ return verticalOverlap > 0 && Math.abs(horizontalGap) < 1; // 允许1px误差
8136
+ },
8137
+ /**
8138
+ * 找到需要在节点文本末尾添加空格的节点
8139
+ * @param {Array<{before: Object, after: Object}>} adjacentPairs - 左右紧邻的矩形对数组
8140
+ * @returns {Node[]} - 需要添加空格的节点数组
8141
+ */
8142
+ afterAddSpace:function(adjacentPairs){
8143
+
8144
+ const nodesToAddSpace = [];
8145
+
8146
+ adjacentPairs.forEach(pair => {
8147
+ const { before, after } = pair;
8148
+ const beforeNode = before.node;
8149
+ const afterNode = after.node;
8150
+
8151
+ // 获取计算样式
8152
+ const beforeStyle = window.getComputedStyle(
8153
+ beforeNode.nodeType === Node.TEXT_NODE ? beforeNode.parentElement : beforeNode
8154
+ );
8155
+
8156
+ const afterStyle = window.getComputedStyle(
8157
+ afterNode.nodeType === Node.TEXT_NODE ? afterNode.parentElement : afterNode
8158
+ );
8159
+
8160
+ // 检查间距是否由CSS属性引起
8161
+ const hasRightSpacing = parseFloat(beforeStyle.marginRight) > 0 ||
8162
+ parseFloat(beforeStyle.paddingRight) > 0;
8163
+
8164
+ const hasLeftSpacing = parseFloat(afterStyle.marginLeft) > 0 ||
8165
+ parseFloat(afterStyle.paddingLeft) > 0;
8166
+
8167
+ // 如果没有明确的间距,且后一个节点的开始非空白符,则需要添加空格
8168
+ if (!hasRightSpacing && !hasLeftSpacing) {
8169
+ //判断 before 节点的最后一个字符是否是空白符
8170
+ if(typeof(beforeNode.textContent) == 'string' && typeof(afterNode.textContent) == 'string'){
8171
+ if(/\s$/.test(beforeNode.textContent)){
8172
+ //before 最后一个字符是空格,则不需要追加空格符了
8173
+ }else if(/^\s/.test(afterNode.textContent)){
8174
+ //after 节点的开始第一个字符是空白符,那么也不需要追加空格符了
8175
+ }else{
8176
+ //这里就需要对 beforeNode 追加空格了
8177
+ nodesToAddSpace.push(beforeNode);
8178
+ }
8179
+ }
8180
+ }
8181
+ });
8182
+
8183
+ return nodesToAddSpace;
8184
+ },
8185
+ /**
8186
+ * 主函数:处理翻译后的空格调整
8187
+ * @param {Node[]} nodes - 节点数组
8188
+ */
8189
+ adjustTranslationSpaces:function(nodes){
8190
+
8191
+ //先判断当前要显示的语种,是否需要用空格进行间隔单词,如果本身不需要空格间隔,像是中文,那就根本不需要去计算视觉距离
8192
+ if(!translate.language.wordBlankConnector(translate.to)){
8193
+ return;
8194
+ }
8195
+
8196
+ //var startTime = Date.now();
8197
+ // 1. 获取节点视觉矩形
8198
+ const rects = translate.visual.getRects(nodes);
8199
+ //console.log('rects:');
8200
+ //console.log(rects);
8201
+
8202
+ // 2. 查找左右紧邻的矩形对
8203
+ const adjacentPairs = translate.visual.afterAdjacent(rects);
8204
+ //console.log('adjacentPairs:');
8205
+ //console.log(adjacentPairs);
8206
+
8207
+ // 3. 确定需要添加空格的节点
8208
+ const nodesToAddSpace = translate.visual.afterAddSpace(adjacentPairs);
8209
+ //console.log('nodesToAddSpace:');
8210
+ //console.log(nodesToAddSpace);
8211
+
8212
+ // 4. 添加非断行空格
8213
+ nodesToAddSpace.forEach(node => {
8214
+ // 确保只修改文本内容,不影响HTML结构
8215
+ if (node.nodeType === Node.TEXT_NODE) {
8216
+ node.textContent = node.textContent + '\u00A0';
8217
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
8218
+ // 如果是元素节点,修改其最后一个子节点(假设是文本节点)
8219
+ const lastChild = node.lastChild;
8220
+ if (lastChild && lastChild.nodeType === Node.TEXT_NODE) {
8221
+ lastChild.textContent = lastChild.textContent + '\u00A0';
8222
+ }
8223
+ }
8224
+ });
8225
+ //var endTime = Date.now();
8226
+ //console.log('visual recognition time: '+(endTime-startTime)+'ms');
8227
+ },
8228
+ /*
8229
+ 通过 translate.nodeQueue[uuid] 中的uuid,来传入这个 translate.nodeQueue[uuid] 中所包含涉及到的所有node (除特殊字符外 ,也就是 translate.nodeQueue[uuid].list 下 特殊字符那一类是不会使用的)
8230
+ */
8231
+ adjustTranslationSpacesByNodequeueUuid:function(uuid){
8232
+ var nodes = [];
8233
+ for(var from in translate.nodeQueue[uuid].list){
8234
+ if (!translate.nodeQueue[uuid].list.hasOwnProperty(from)) {
8235
+ continue;
8236
+ }
8237
+ if(from.length < 1){
8238
+ continue;
8239
+ }
8240
+ for(var hash in translate.nodeQueue[uuid].list[from]){
8241
+ if (!translate.nodeQueue[uuid].list[from].hasOwnProperty(hash)) {
8242
+ continue;
8243
+ }
8244
+ for(var nodeindex in translate.nodeQueue[uuid].list[from][hash].nodes){
8245
+ if (!translate.nodeQueue[uuid].list[from][hash].nodes.hasOwnProperty(nodeindex)) {
8246
+ continue;
8247
+ }
8248
+ var node = translate.nodeQueue[uuid].list[from][hash].nodes[nodeindex].node;
8249
+ nodes.push(node);
8250
+ }
8251
+ }
8252
+ }
8253
+ translate.visual.adjustTranslationSpaces(nodes);
8254
+ },
8255
+ /*
8256
+ 通过 translate.nodeQueue 中最后一次执行的 uuid,来获取这个 translate.nodeQueue[uuid] 中所包含涉及到的所有node (除特殊字符外 ,也就是 translate.nodeQueue[uuid].list 下 特殊字符那一类是不会使用的)
8257
+ */
8258
+ adjustTranslationSpacesByLastNodequeueUuid:function(uuid){
8259
+ var uuid = '';
8260
+ for(var uuid_index in translate.nodeQueue){
8261
+ uuid = uuid_index;
8262
+ break;
8263
+ }
8264
+ if(typeof(uuid) == 'string' && uuid.length > 1){
8265
+ translate.visual.adjustTranslationSpacesByNodequeueUuid(uuid);
8266
+ }
8267
+ },
8268
+
8269
+ /**
8270
+ * 隐藏当前网页的所有文本
8271
+ *
8272
+ */
8273
+ hideText:{
8274
+ style:`
8275
+ /* 文本隐藏核心样式 - 仅隐藏文本内容 */
8276
+ html.translatejs-text-hidden p, html.translatejs-text-hidden div,
8277
+ html.translatejs-text-hidden h1, html.translatejs-text-hidden h2, html.translatejs-text-hidden h3,
8278
+ html.translatejs-text-hidden h4, html.translatejs-text-hidden h5, html.translatejs-text-hidden h6,
8279
+ html.translatejs-text-hidden span, html.translatejs-text-hidden a, html.translatejs-text-hidden b,
8280
+ html.translatejs-text-hidden strong, html.translatejs-text-hidden i, html.translatejs-text-hidden em,
8281
+ html.translatejs-text-hidden mark,
8282
+ html.translatejs-text-hidden blockquote, html.translatejs-text-hidden ul, html.translatejs-text-hidden ol,
8283
+ html.translatejs-text-hidden li, html.translatejs-text-hidden table, html.translatejs-text-hidden th,
8284
+ html.translatejs-text-hidden td, html.translatejs-text-hidden label, html.translatejs-text-hidden button,
8285
+ html.translatejs-text-hidden input, html.translatejs-text-hidden select, html.translatejs-text-hidden textarea {
8286
+ color: transparent !important;
8287
+ text-shadow: none !important;
8288
+ }
8289
+
8290
+ /* 隐藏占位符文字 */
8291
+ html.translatejs-text-hidden ::placeholder {
8292
+ color: transparent !important;
8293
+ }
8294
+
8295
+ /* 确保媒体元素不受影响 */
8296
+ img, video, iframe, canvas, svg,
8297
+ object, embed, picture, source {
8298
+ color: initial !important;
8299
+ }
8300
+
8301
+ /* 忽略隐藏的元素保持可见 */
8302
+ .ignore-hidden {
8303
+ color: inherit !important;
8304
+ }
8305
+ `,
8306
+
8307
+ /**
8308
+ * 当点击切换语言按钮后,会刷新当前页面,然后再进行翻译。
8309
+ * 这时会出现刷新当前页面后,会先显示原本的文本,然后再翻译为切换为的语种,体验效果有点欠缺。
8310
+ * 这个得作用就是增强用户视觉的体验效果,在页面初始化加载时,如果判定需要翻译,那么会隐藏所有网页中的文本 。
8311
+ * 这个需要在body标签之前执行,需要在head标签中执行此。也就是加载 translate.js 以及触发此都要放到head标签中
8312
+ */
8313
+ hide:function(){
8314
+ const style = document.createElement('style');
8315
+ style.textContent = translate.visual.hideText.style;
8316
+ document.head.appendChild(style);
8317
+ document.documentElement.classList.add('translatejs-text-hidden');
8318
+ },
8319
+ /**
8320
+ * 撤销隐藏状态,将原本的文本正常显示出来
8321
+ *
8322
+ */
8323
+ show:function(){
8324
+ document.documentElement.classList.remove('translatejs-text-hidden');
8325
+ }
8326
+ },
8327
+
8328
+ /**
8329
+ * 网页加载,且要进行翻译时,翻译之前,隐藏当前网页的文本。
8330
+ * 当点击切换语言按钮后,会刷新当前页面,然后再进行翻译。
8331
+ * 这时会出现刷新当前页面后,会先显示原本的文本,然后再翻译为切换为的语种,体验效果有点欠缺。
8332
+ * 这个得作用就是增强用户视觉的体验效果,在页面初始化加载时,如果判定需要翻译,那么会隐藏所有网页中的文本 。
8333
+ * 这个需要在body标签之前执行,需要在head标签中执行此。也就是加载 translate.js 以及触发此都要放到head标签中
8334
+ */
8335
+ webPageLoadTranslateBeforeHiddenText:function(){
8336
+ if(typeof(document.body) == 'undefined' || document.body == null){
8337
+ //正常,body还没加载
8338
+ }else{
8339
+ console.log('错误警告: translate.visual.webPageLoadTranslateBeforeHiddenText() 要在 head 标签中触发才能达到最好的效果!');
8340
+ }
8341
+ if(translate.language.local == ''){
8342
+ console.log('错误警告:在使用 translate.visual.webPageLoadTranslateBeforeHiddenText() 之前,请先手动设置你的本地语种,参考: http://translate.zvo.cn/4066.html 如果你不设置,则不管你是否有切换语言,网页打开后都会先短暂的不显示文字');
8343
+ }
8344
+
8345
+ if(translate.language.local == '' || translate.language.local != translate.language.getCurrent()){
8346
+ translate.visual.hideText.hide();
8347
+
8348
+ //设置翻译完成后,移除隐藏文本的css 的class name
8349
+ translate.lifecycle.execute.renderFinish.push(function(uuid, to){
8350
+ translate.visual.hideText.show();
8351
+ });
8352
+ }
8353
+ }
8354
+
8355
+
8356
+
8357
+
8358
+
8359
+ }
8360
+ /*js translate.visual end*/
7319
8361
 
7320
8362
  }
7321
8363
  /*
@@ -7377,13 +8419,14 @@ var nodeuuid = {
7377
8419
  console.log('------ translate.js ------\nTwo lines of js html automatic translation, page without change, no language configuration file, no API Key, SEO friendly! Open warehouse : https://github.com/xnx3/translate \n两行js实现html全自动翻译。 无需改动页面、无语言配置文件、无API Key、对SEO友好!完全开源,代码仓库:https://gitee.com/mail_osc/translate');
7378
8420
  /*js copyright-notice end*/
7379
8421
 
8422
+
7380
8423
  /*js amd-cmd-commonjs start*/
7381
8424
  /*兼容 AMD、CMD、CommonJS 规范 - start*/
7382
8425
  /**
7383
8426
  * 兼容 AMD、CMD、CommonJS 规范
7384
8427
  * node 环境使用:`npm i i18n-jsautotranslate` 安装包
7385
8428
  */
7386
- (function (root, factory) {
8429
+ ;(function (root, factory) {
7387
8430
  if (typeof define === 'function' && define.amd) {
7388
8431
  define([], () => factory());
7389
8432
  } else if (typeof module === 'object' && module.exports) {