i18n-jsautotranslate 3.15.5 → 3.16.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 +5 -2
  2. package/index.js +974 -152
  3. package/package.json +2 -2
package/index.js CHANGED
@@ -14,32 +14,40 @@ var translate = {
14
14
  * 格式:major.minor.patch.date
15
15
  */
16
16
  // AUTO_VERSION_START
17
- version: '3.15.5.20250517',
17
+ version: '3.16.0.20250618',
18
18
  // AUTO_VERSION_END
19
19
  /*
20
20
  当前使用的版本,默认使用v2. 可使用 setUseVersion2();
21
21
  来设置使用v2 ,已废弃,主要是区分是否是v1版本来着,v2跟v3版本是同样的使用方式
22
22
  */
23
- useVersion:'v2',
23
+ useVersion:'v2',
24
+ /*js translate.setUseVersion2 start*/
24
25
  setUseVersion2:function(){
25
26
  translate.useVersion = 'v2';
26
27
  console.log('提示:自 v2.10 之后的版本默认就是使用V2版本(当前版本为:'+translate.version+'), translate.setUseVersion2() 可以不用再加这一行了。当然加了也无所谓,只是加了跟不加是完全一样的。');
27
28
  },
29
+ /*js translate.setUseVersion2 end*/
28
30
  /*
29
31
  * 翻译的对象,也就是 new google.translate.TranslateElement(...)
30
32
  * 已废弃,v1使用的
31
33
  */
32
34
  translate:null,
35
+
36
+ /*js translate.includedLanguages end*/
33
37
  /*
34
38
  * 支持哪些语言切换,包括: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
35
39
  * 已废弃,请使用 translate.selectLanguageTag.languages
36
40
  */
37
41
  includedLanguages:'zh-CN,zh-TW,en',
42
+ /*js translate.includedLanguages end*/
43
+
44
+ /*js translate.resourcesUrl start*/
38
45
  /*
39
46
  * 资源文件url的路径
40
47
  * 已废弃,v1使用的
41
48
  */
42
49
  resourcesUrl:'//res.zvo.cn/translate',
50
+ /*js translate.resourcesUrl end*/
43
51
 
44
52
  /**
45
53
  * 默认出现的选择语言的 select 选择框,可以通过这个选择切换语言。
@@ -193,8 +201,11 @@ var translate = {
193
201
  * 已废弃,v1使用的
194
202
  */
195
203
  //localLanguage:'zh-CN',
204
+ /*js translate.localLanguage start*/
196
205
  localLanguage:'zh-CN',
206
+ /*js translate.localLanguage end*/
197
207
 
208
+ /*js translate.googleTranslateElementInit start*/
198
209
  /**
199
210
  * google翻译执行的
200
211
  * 已废弃,v1使用的
@@ -223,6 +234,7 @@ var translate = {
223
234
  selectId //触发按钮的id
224
235
  );
225
236
  },
237
+ /*js translate.googleTranslateElementInit end*/
226
238
 
227
239
  /**
228
240
  * 初始化,如加载js、css资源
@@ -244,6 +256,9 @@ var translate = {
244
256
 
245
257
  },
246
258
  */
259
+
260
+
261
+ /*js translate.execute_v1 start*/
247
262
  /**
248
263
  * 执行翻译操作
249
264
  * 已废弃,v1使用的
@@ -252,7 +267,9 @@ var translate = {
252
267
  console.log('=====ERROR======');
253
268
  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');
254
269
  },
255
-
270
+ /*js translate.execute_v1 end*/
271
+
272
+ /*js translate.setCookie start*/
256
273
  /**
257
274
  * 设置Cookie,失效时间一年。
258
275
  * @param name
@@ -263,7 +280,9 @@ var translate = {
263
280
  var cookieString=name+"="+escape(value);
264
281
  document.cookie=cookieString;
265
282
  },
283
+ /*js translate.setCookie end*/
266
284
 
285
+ /*js translate.getCookie start*/
267
286
  //获取Cookie。若是不存再,返回空字符串
268
287
  //* 已废弃,v1使用的
269
288
  getCookie:function (name){
@@ -277,11 +296,14 @@ var translate = {
277
296
  }
278
297
  return "";
279
298
  },
299
+ /*js translate.getCookie end*/
280
300
 
281
- /**
282
- * 获取当前页面采用的是什么语言
283
- * 返回值如 en、zh-CN、zh-TW (如果是第一次用,没有设置过,那么返回的是 translate.localLanguage 设置的值)
284
- * * 已废弃,v1使用的
301
+
302
+ /*js translate.currentLanguage start*/
303
+ /*
304
+ 获取当前页面采用的是什么语言
305
+ 返回值如 en、zh-CN、zh-TW (如果是第一次用,没有设置过,那么返回的是 translate.localLanguage 设置的值)
306
+ 已废弃,v1使用的
285
307
  */
286
308
  currentLanguage:function(){
287
309
  //translate.check();
@@ -292,6 +314,7 @@ var translate = {
292
314
  return translate.localLanguage;
293
315
  }
294
316
  },
317
+ /*js translate.currentLanguage end*/
295
318
 
296
319
  /**
297
320
  * 切换语言,比如切换为英语、法语
@@ -364,17 +387,27 @@ var translate = {
364
387
  for (let i = 0; i < iframes.length; i++) {
365
388
  const iframe = iframes[i];
366
389
  // 获取 iframe 的 window 对象
367
- const iframeWindow = iframe.contentWindow;
368
- if(typeof(iframeWindow.translate) == 'object' && typeof(iframeWindow.translate.version) == 'string'){
369
- //iframe页面中存在 translate,那么也控制iframe中的进行翻译
370
- if(iframeWindow.translate.to != languageName){
371
- iframeWindow.translate.to = languageName;
372
- iframeWindow.translate.storage.set('to',languageName); //设置目标翻译语言
373
- iframeWindow.translate.execute();
390
+ const iframeWindow = iframe.contentWindow;
391
+ try{
392
+ if(typeof(iframeWindow.translate) == 'object' && typeof(iframeWindow.translate.version) == 'string'){
393
+ //iframe页面中存在 translate,那么也控制iframe中的进行翻译
394
+ if(iframeWindow.translate.to != languageName){
395
+ iframeWindow.translate.to = languageName;
396
+ iframeWindow.translate.storage.set('to',languageName); //设置目标翻译语言
397
+ iframeWindow.translate.execute();
398
+ }
374
399
  }
400
+ }catch(e){
401
+ //增加try,避免异常,比如跨域,中断导致无法用
402
+ console.log(e);
375
403
  }
376
404
  }
377
405
  }
406
+
407
+ //当用户代码设置里启用了 translate.listener.start() 然后用户加载页面后并没有翻译(这时listener是不启动的只是把listener.use标记为true),然后手动点击翻译按钮翻译为其他语种(这是不会刷新页面),翻译后也要跟着启动监听
408
+ if(translate.listener.use == true && translate.listener.isStart == false){
409
+ translate.listener.start();
410
+ }
378
411
  },
379
412
 
380
413
  /**
@@ -382,11 +415,13 @@ var translate = {
382
415
  * english
383
416
  * 已废弃,v1使用的
384
417
  */
418
+ /*js translate.check start*/
385
419
  check:function(){
386
420
  if(window.location.protocol == 'file:'){
387
421
  console.log('\r\n---WARNING----\r\ntranslate.js 主动翻译组件自检异常,当前协议是file协议,翻译组件要在正常的线上http、https协议下才能正常使用翻译功能\r\n------------');
388
422
  }
389
423
  },
424
+ /*js translate.check end*/
390
425
 
391
426
 
392
427
  /**************************** v2.0 */
@@ -823,6 +858,74 @@ var translate = {
823
858
  translate.storage.set('hash_'+to+'_'+translate.util.hash(key), value);
824
859
  }
825
860
  },
861
+
862
+
863
+ //全部提取能力(整站的离线翻译数据提取)
864
+ fullExtract:{
865
+ /*js translate.office.fullExtract.set start*/
866
+ /*
867
+ 将翻译的结果加入
868
+ hash: 翻译前的文本的hash
869
+ originalText: 翻以前的文本,原始文本
870
+ toLanguage: 翻译为什么语言
871
+ translateText: 翻译结果的文本
872
+ */
873
+ set: async function(hash, originalText, toLanguage, translateText){
874
+ if(typeof(translate.storage.IndexedDB) == 'undefined'){
875
+ console.log('ERROR: translate.storage.IndexedDB not find');
876
+ return;
877
+ }
878
+ var obj = await translate.storage.IndexedDB.get('hash_'+hash);
879
+ if(typeof(obj) == 'undefined' && obj == null){
880
+ obj = {
881
+ originalText:originalText
882
+ };
883
+ }
884
+ obj[toLanguage] = translateText;
885
+ await translate.storage.IndexedDB.set('hash_'+hash, obj);
886
+ },
887
+ /*js translate.office.fullExtract.set end*/
888
+
889
+ /*js translate.office.fullExtract.export start*/
890
+ /*
891
+ 将存储的数据导出为 txt 文件下载下来
892
+ */
893
+ export: async function(to){
894
+ if(typeof(translate.storage.IndexedDB) == 'undefined'){
895
+ console.log('ERROR: translate.storage.IndexedDB not find');
896
+ return;
897
+ }
898
+ if(typeof(to) != 'string'){
899
+ console.log('error : to param not find, example: "english"');
900
+ return;
901
+ }
902
+ var text = 'translate.office.append(\'';
903
+
904
+ var data = await translate.storage.IndexedDB.list('hash_*');
905
+ for(var i in data){
906
+ var originalText = data[i].value.originalText.replace(/\n/g, "\\n").replace(/\t/g, "\\t");
907
+ text = text + '\n' + originalText + '='+data[i].value.english.replace(/\n/g, "\\n").replace(/\t/g, "\\t");
908
+ }
909
+ text = text + '\n`);'
910
+
911
+ const blob = new Blob([text], { type: "text/plain" });
912
+ const url = URL.createObjectURL(blob);
913
+ const link = document.createElement("a");
914
+ link.href = url;
915
+ link.download = to+".txt";
916
+ link.click();
917
+ URL.revokeObjectURL(url);
918
+ },
919
+ /*js translate.office.fullExtract.export end*/
920
+
921
+ /*
922
+ 是否启用全部提取的能力
923
+ true: 启用, 默认是false不启用。
924
+ 如果设置为true,则每次通过调用翻译接口进行翻译后,都会将翻译的原文、译文、翻译为什么语种,都会单独记录一次,存入浏览器的 IndexedDB 的 translate.js 数据库
925
+ 然后可以浏览所有页面后,把所有翻译一对一的对应翻译结果直接全部导出,用于做离线翻译配置使用。
926
+ */
927
+ isUse:false,
928
+ }
826
929
  },
827
930
  setAutoDiscriminateLocalLanguage:function(){
828
931
  translate.autoDiscriminateLocalLanguage = true;
@@ -893,9 +996,12 @@ var translate = {
893
996
  //isExecuteFinish:false,
894
997
  //是否已经使用了 translate.listener.start() 了,如果使用了,那这里为true,多次调用 translate.listener.start() 只有第一次有效
895
998
  isStart:false,
999
+ //用户的代码里是否启用了 translate.listener.start() ,true:启用
1000
+ //当用户加载页面后,但是未启用翻译时,为了降低性能,监听是不会启动的,但是用户手动点击翻译后,也要把监听启动起来,所以就加了这个参数,来表示当前是否在代码里启用了监听,以便当触发翻译时,监听也跟着触发
1001
+ use:false,
896
1002
  //translate.listener.start(); //开启html页面变化的监控,对变化部分会进行自动翻译。注意,这里变化区域,是指使用 translate.setDocuments(...) 设置的区域。如果未设置,那么为监控整个网页的变化
897
1003
  start:function(){
898
-
1004
+ translate.listener.use = true;
899
1005
  translate.temp_linstenerStartInterval = setInterval(function(){
900
1006
  if(document.readyState == 'complete'){
901
1007
  //dom加载完成,进行启动
@@ -945,8 +1051,9 @@ var translate = {
945
1051
  node 是哪个节点被listener扫描到改动后忽略。
946
1052
  可传入 node、也可以传入node的uuid字符串
947
1053
  expireTime 过期时间,也就是执行当前方法将 node 加入后,过多长时间失效,这里是毫秒,比如传入 500 则这个node在当前时间往后的500毫秒内,如果被listener监听到改动,是直接被忽略的,不会触发任何翻译操作
1054
+ showResultText 实际显示出来的文本,翻译之后显示出来的文本。如果翻译过程中其他js改动了这个文本内容,导致未能翻译,则 analyse.set 的 resultText 会返回 空字符串设置到这里
948
1055
  */
949
- addIgnore:function(node, expireTime){
1056
+ addIgnore:function(node, expireTime, showResultText){
950
1057
  let nodeid = '';
951
1058
  if(typeof(node) == 'string'){
952
1059
  nodeid = node;
@@ -954,7 +1061,10 @@ var translate = {
954
1061
  nodeid = nodeuuid.uuid(node);
955
1062
  }
956
1063
 
957
- translate.listener.ignoreNode[nodeid] = Date.now()+expireTime;
1064
+ translate.listener.ignoreNode[nodeid] = {
1065
+ addtime:Date.now()+expireTime,
1066
+ text:showResultText
1067
+ };
958
1068
 
959
1069
  //translate.listener.renderTaskFinish();
960
1070
  },
@@ -965,7 +1075,7 @@ var translate = {
965
1075
  //console.log('refresh ignore ,current: '+Object.keys(translate.listener.ignoreNode).length);
966
1076
  var currentTime = Date.now();
967
1077
  for (const node in translate.listener.ignoreNode) {
968
- if(translate.listener.ignoreNode[node] < currentTime){
1078
+ if(translate.listener.ignoreNode[node].addtime < currentTime){
969
1079
  //console.log('delete : ');
970
1080
  //console.log(node);
971
1081
  delete translate.listener.ignoreNode[node];
@@ -977,6 +1087,10 @@ var translate = {
977
1087
 
978
1088
  //增加监听,开始监听。这个不要直接调用,需要使用上面的 start() 开启
979
1089
  addListener:function(){
1090
+ if(translate.listener.isStart == true){
1091
+ console.log('translate.listener.start() 已经启动了,无需再重复启动监听,增加浏览器负担');
1092
+ return;
1093
+ }
980
1094
  translate.listener.isStart = true; //记录已执行过启动方法了
981
1095
 
982
1096
  // 观察器的配置(需要观察什么变动)
@@ -1052,8 +1166,8 @@ var translate = {
1052
1166
 
1053
1167
  //console.log(node);
1054
1168
  let nodeid = nodeuuid.uuid(node);
1055
- if(typeof(translate.listener.ignoreNode[nodeid]) == 'number'){
1056
- if(translate.listener.ignoreNode[nodeid] > Date.now()){
1169
+ if(typeof(translate.listener.ignoreNode[nodeid]) != 'undefined'){
1170
+ if(translate.listener.ignoreNode[nodeid].addtime > Date.now() && typeof(node.nodeValue) == 'string' && node.nodeValue == translate.listener.ignoreNode[nodeid].text){
1057
1171
  //console.log('node 未过忽略期,listener扫描后忽略:'+nodeid);
1058
1172
  continue;
1059
1173
  }
@@ -1279,12 +1393,10 @@ var translate = {
1279
1393
 
1280
1394
  }, 50, ipnode);
1281
1395
 
1396
+ //渲染页面进行翻译显示
1397
+ var analyseSet = translate.element.nodeAnalyse.set(this.nodes[hash][task_index], task.originalText, task.resultText, task['attribute']);
1282
1398
  //加入 translate.listener.ignoreNode
1283
- translate.listener.addIgnore(this.nodes[hash][task_index], translate.listener.translateExecuteNodeIgnoreExpireTime);
1284
- translate.element.nodeAnalyse.set(this.nodes[hash][task_index], task.originalText, task.resultText, task['attribute']);
1285
-
1286
-
1287
-
1399
+ translate.listener.addIgnore(this.nodes[hash][task_index], translate.listener.translateExecuteNodeIgnoreExpireTime, analyseSet.resultText);
1288
1400
 
1289
1401
 
1290
1402
  /*
@@ -1331,7 +1443,7 @@ var translate = {
1331
1443
  //console.log(translate.nodeQueue[uuid].list[lang][hash].original);
1332
1444
  //var nodename = translate.element.getNodeName(translate.nodeQueue[uuid].list[lang][hash].nodes[0].node);
1333
1445
  //console.log("nodename:"+nodename);
1334
- var analyse = translate.element.nodeAnalyse.get(renderTask.nodes[hash][0]);
1446
+ var analyse = translate.element.nodeAnalyse.get(renderTask.nodes[hash][nodeindex]);
1335
1447
  //analyse.text analyse.node
1336
1448
  var nodeid = nodeuuid.uuid(analyse.node);
1337
1449
 
@@ -1357,7 +1469,15 @@ var translate = {
1357
1469
  //console.log(analyse.node);
1358
1470
  translate.nodeHistory[nodeid] = {};
1359
1471
  translate.nodeHistory[nodeid].node = analyse.node;
1360
- translate.nodeHistory[nodeid].translateText = analyse.text;
1472
+ if(translate.whole.isWhole(analyse.node)){
1473
+ //这个元素使用的是整体翻译的方式,那就直接将API接口返回的翻译内容作为node最终显示的结果。
1474
+ //这样即使在翻译过程中其他js对这个元素的内容有改动了,那下次再触发翻译,还能对改动后的文本正常翻译,不至于使这个元素已被标记翻译过了,造成漏翻译。
1475
+ translate.nodeHistory[nodeid].translateText = renderTask.taskQueue[hash][nodeindex].resultText;
1476
+ }else{
1477
+ //时间差会造成漏翻译情况,见if中的注释
1478
+ translate.nodeHistory[nodeid].translateText = analyse.text;
1479
+ }
1480
+
1361
1481
  }
1362
1482
 
1363
1483
  }
@@ -1392,6 +1512,7 @@ var translate = {
1392
1512
 
1393
1513
  */
1394
1514
  waitingExecute:{
1515
+ use:true, //默认是使用,自有部署场景不担心并发的场景,可以禁用,以提高用户使用体验。
1395
1516
 
1396
1517
  /*
1397
1518
  一维数组形态,存放执行的翻译任务
@@ -1479,11 +1600,14 @@ var translate = {
1479
1600
  如果不传入或者传入null,则是翻译整个网页所有能翻译的元素
1480
1601
  */
1481
1602
  execute:function(docs){
1482
- if(translate.state != 0){
1483
- console.log('当前翻译还未完结,新的翻译任务已加入等待翻译队列中,待翻译结束后便会执行当前翻译任务。');
1484
- translate.waitingExecute.add(docs);
1485
- return;
1603
+ if(translate.waitingExecute.use){
1604
+ if(translate.state != 0){
1605
+ console.log('当前翻译还未完结,新的翻译任务已加入等待翻译队列中,待翻译结束后便会执行当前翻译任务。');
1606
+ translate.waitingExecute.add(docs);
1607
+ return;
1608
+ }
1486
1609
  }
1610
+
1487
1611
  translate.state = 1;
1488
1612
  //console.log('translate.state = 1');
1489
1613
  if(typeof(docs) != 'undefined'){
@@ -2174,6 +2298,10 @@ var translate = {
2174
2298
 
2175
2299
  //将翻译结果以 key:hash value翻译结果的形式缓存
2176
2300
  translate.storage.set('hash_'+data.to+'_'+cacheHash,text);
2301
+ //如果离线翻译启用了全部提取,那么还要存入离线翻译指定存储
2302
+ if(translate.office.fullExtract.isUse){
2303
+ translate.office.fullExtract.set(hash, originalWord, data.to, text);
2304
+ }
2177
2305
  }
2178
2306
  task.execute(); //执行渲染任务
2179
2307
  //translate.temp_executeFinishNumber++; //记录执行完的次数
@@ -2268,9 +2396,12 @@ var translate = {
2268
2396
  originalText 翻译之前的内容文本
2269
2397
  resultText 翻译之后的内容文本
2270
2398
  attribute 存放要替换的属性,比如 a标签的title属性。 如果是直接替换node.nodeValue ,那这个没有
2399
+ 返回结果是一个数组,其中:
2400
+ resultText: 翻译完成之后的text内容文本,注意,如果返回的是空字符串,那么则是翻译结果进行替换时,并没有成功替换,应该是翻译的过程中,这个node的值被其他js又赋予其他内容了。
2401
+ node: 进行翻译的目标node
2271
2402
  */
2272
2403
  set:function(node, originalText, resultText, attribute){
2273
- translate.element.nodeAnalyse.analyse(node,originalText,resultText, attribute);
2404
+ return translate.element.nodeAnalyse.analyse(node,originalText,resultText, attribute);
2274
2405
  },
2275
2406
  /*
2276
2407
 
@@ -2285,6 +2416,11 @@ var translate = {
2285
2416
  则是进行翻译之后的渲染显示
2286
2417
 
2287
2418
  attribute : 进行替换渲染时使用,存放要替换的属性,比如 a标签的title属性。 如果是直接替换node.nodeValue ,那这个没有
2419
+
2420
+ 返回结果是一个数组,其中:
2421
+ resultText: 翻译完成之后的text内容文本。 当使用 translate.element.nodeAnalyse.set 时才会有这个参数返回。 注意,如果返回的是空字符串,那么则是翻译结果进行替换时,并没有成功替换,应该是翻译的过程中,这个node的值被其他js又赋予其他内容了。
2422
+ text : 要进行翻译的text内容文本,当使用 translate.element.nodeAnalyse.get 时才会有这个参数的返回
2423
+ node: 进行翻译的目标node
2288
2424
  */
2289
2425
  analyse:function(node, originalText, resultText, attribute){
2290
2426
  var result = new Array(); //返回的结果
@@ -2301,16 +2437,26 @@ var translate = {
2301
2437
  if(typeof(originalText) != 'undefined' && originalText.length > 0){
2302
2438
  if(typeof(node[attribute]) != 'undefined'){
2303
2439
  //这种是主流框架,像是vue、element、react 都是用这种 DOM Property 的方式,更快
2304
- node[attribute] = translate.util.textReplace(node[attribute], originalText, resultText, translate.to); //2025.4.26 变更为此方式
2305
- //node[attribute] = node[attribute].replace(new RegExp(translate.util.regExp.pattern(originalText),'g'), translate.util.regExp.resultText(resultText));
2440
+ var resultShowText = translate.util.textReplace(node[attribute], originalText, resultText, translate.to);
2441
+ node[attribute] = resultShowText; //2025.4.26 变更为此方式
2442
+ if(resultShowText.indexOf(resultText) > -1){
2443
+ result['resultText'] = resultShowText;
2444
+ }else{
2445
+ result['resultText'] = '';
2446
+ }
2306
2447
  }
2307
2448
 
2308
2449
  //这种 Html Attribute 方式 是 v3.12 版本之前一直使用的方式,速度上要慢于 上面的,为了向前兼容不至于升级出问题,后面可能会优化掉
2309
2450
  var htmlAttributeValue = node.getAttribute(attribute);
2310
2451
  if(htmlAttributeValue != null && typeof(htmlAttributeValue) != 'undefined'){
2452
+ var resultShowText = translate.util.textReplace(htmlAttributeValue, originalText, resultText, translate.to);
2311
2453
  //这个才是在v3.9.2 后要用的,上面的留着只是为了适配以前的
2312
- node.setAttribute(attribute, translate.util.textReplace(htmlAttributeValue, originalText, resultText, translate.to)); //2025.4.26 变更为此方式
2313
- //node.setAttribute(attribute, htmlAttributeValue.replace(new RegExp(translate.util.regExp.pattern(originalText),'g'), translate.util.regExp.resultText(resultText)));
2454
+ node.setAttribute(attribute, resultShowText);
2455
+ if(resultShowText.indexOf(resultText) > -1){
2456
+ result['resultText'] = resultShowText;
2457
+ }else{
2458
+ result['resultText'] = '';
2459
+ }
2314
2460
  }
2315
2461
  }
2316
2462
  return result;
@@ -2357,9 +2503,13 @@ var translate = {
2357
2503
  if(input_value_node != null && typeof(input_value_node) != 'undefined' && typeof(input_value_node.nodeValue) != 'undefined' && input_value_node.nodeValue.length > 0){
2358
2504
  //替换渲染
2359
2505
  if(typeof(originalText) != 'undefined' && originalText.length > 0){
2360
- //this.nodes[hash][task_index].nodeValue = this.nodes[hash][task_index].nodeValue.replace(new RegExp(translate.util.regExp.pattern(task.originalText),'g'), translate.util.regExp.resultText(task.resultText));
2361
- input_value_node.nodeValue = translate.util.textReplace(input_value_node.nodeValue, originalText, resultText, translate.to); //2025.4.26 变更为此方式
2362
- //input_value_node.nodeValue = input_value_node.nodeValue.replace(new RegExp(translate.util.regExp.pattern(originalText),'g'), translate.util.regExp.resultText(resultText));
2506
+ var resultShowText = translate.util.textReplace(input_value_node.nodeValue, originalText, resultText, translate.to);
2507
+ input_value_node.nodeValue = resultShowText; //2025.4.26 变更为此方式
2508
+ if(resultShowText.indexOf(resultText) > -1){
2509
+ result['resultText'] = resultShowText;
2510
+ }else{
2511
+ result['resultText'] = '';
2512
+ }
2363
2513
  }
2364
2514
 
2365
2515
  result['text'] = input_value_node.nodeValue;
@@ -2375,9 +2525,13 @@ var translate = {
2375
2525
  //console.log(node);
2376
2526
  //替换渲染
2377
2527
  if(typeof(originalText) != 'undefined' && originalText.length > 0){
2378
- //this.nodes[hash][task_index].nodeValue = this.nodes[hash][task_index].nodeValue.replace(new RegExp(translate.util.regExp.pattern(task.originalText),'g'), translate.util.regExp.resultText(task.resultText));
2379
- node.attributes['placeholder'].nodeValue = translate.util.textReplace(node.attributes['placeholder'].nodeValue, originalText, resultText, translate.to); //2025.4.26 变更为此方式
2380
- //node.attributes['placeholder'].nodeValue = node.attributes['placeholder'].nodeValue.replace(new RegExp(translate.util.regExp.pattern(originalText),'g'), translate.util.regExp.resultText(resultText));
2528
+ var resultShowText = translate.util.textReplace(node.attributes['placeholder'].nodeValue, originalText, resultText, translate.to);
2529
+ node.attributes['placeholder'].nodeValue = resultShowText; //2025.4.26 变更为此方式
2530
+ if(resultShowText.indexOf(resultText) > -1){
2531
+ result['resultText'] = resultShowText;
2532
+ }else{
2533
+ result['resultText'] = '';
2534
+ }
2381
2535
  }
2382
2536
 
2383
2537
  result['text'] = node.attributes['placeholder'].nodeValue;
@@ -2401,9 +2555,13 @@ var translate = {
2401
2555
  if(nodeAttributeName == 'keywords' || nodeAttributeName == 'description' || nodeAttributeName == 'sharetitle' || nodeAttributeProperty == 'og:title' || nodeAttributeProperty == 'og:description' || nodeAttributeProperty == 'og:site_name' || nodeAttributeProperty == 'og:novel:latest_chapter_name'){
2402
2556
  //替换渲染
2403
2557
  if(typeof(originalText) != 'undefined' && originalText != null && originalText.length > 0){
2404
- //this.nodes[hash][task_index].nodeValue = this.nodes[hash][task_index].nodeValue.replace(new RegExp(translate.util.regExp.pattern(task.originalText),'g'), translate.util.regExp.resultText(task.resultText));
2405
- node.content = translate.util.textReplace(node.content, originalText, resultText, translate.to); //2025.4.26 变更为此方式
2406
- //node.content = node.content.replace(new RegExp(translate.util.regExp.pattern(originalText),'g'), translate.util.regExp.resultText(resultText));
2558
+ var resultShowText = translate.util.textReplace(node.content, originalText, resultText, translate.to);
2559
+ node.content = resultShowText; //2025.4.26 变更为此方式
2560
+ if(resultShowText.indexOf(resultText) > -1){
2561
+ result['resultText'] = resultShowText;
2562
+ }else{
2563
+ result['resultText'] = '';
2564
+ }
2407
2565
  }
2408
2566
 
2409
2567
  result['text'] = node.content;
@@ -2422,9 +2580,13 @@ var translate = {
2422
2580
 
2423
2581
  //替换渲染
2424
2582
  if(typeof(originalText) != 'undefined' && originalText.length > 0){
2425
- //this.nodes[hash][task_index].nodeValue = this.nodes[hash][task_index].nodeValue.replace(new RegExp(translate.util.regExp.pattern(task.originalText),'g'), translate.util.regExp.resultText(task.resultText));
2426
- node.alt = translate.util.textReplace(node.alt, originalText, resultText, translate.to); //2025.4.26 变更为此方式
2427
- //node.alt = node.alt.replace(new RegExp(translate.util.regExp.pattern(originalText),'g'), translate.util.regExp.resultText(resultText));
2583
+ var resultShowText = translate.util.textReplace(node.alt, originalText, resultText, translate.to);
2584
+ node.alt = resultShowText; //2025.4.26 变更为此方式
2585
+ if(resultShowText.indexOf(resultText) > -1){
2586
+ result['resultText'] = resultShowText;
2587
+ }else{
2588
+ result['resultText'] = '';
2589
+ }
2428
2590
  }
2429
2591
  result['text'] = node.alt;
2430
2592
  return result;
@@ -2440,9 +2602,13 @@ var translate = {
2440
2602
  }else{
2441
2603
  //替换渲染
2442
2604
  if(typeof(originalText) != 'undefined' && originalText != null && originalText.length > 0){
2443
- //this.nodes[hash][task_index].nodeValue = this.nodes[hash][task_index].nodeValue.replace(new RegExp(translate.util.regExp.pattern(task.originalText),'g'), translate.util.regExp.resultText(task.resultText));
2444
- node.nodeValue = translate.util.textReplace(node.nodeValue, originalText, resultText, translate.to); //2025.4.26 变更为此方式
2445
- //node.nodeValue = node.nodeValue.replace(new RegExp(translate.util.regExp.pattern(originalText),'g'), translate.util.regExp.resultText(resultText));
2605
+ var resultShowText = translate.util.textReplace(node.nodeValue, originalText, resultText, translate.to);
2606
+ node.nodeValue = resultShowText; //2025.4.26 变更为此方式
2607
+ if(resultShowText.indexOf(resultText) > -1){
2608
+ result['resultText'] = resultShowText;
2609
+ }else{
2610
+ result['resultText'] = '';
2611
+ }
2446
2612
  }
2447
2613
  result['text'] = node.nodeValue;
2448
2614
  }
@@ -2460,8 +2626,17 @@ var translate = {
2460
2626
  }
2461
2627
 
2462
2628
  var nodename = node.nodeName;
2463
- //console.log('nodename:'+nodename+', node:'+node);
2464
- return nodename;
2629
+ if(typeof(node.nodeName) == 'string'){
2630
+ return node.nodeName;
2631
+ }else{
2632
+ if(typeof(node.tagName) == 'string' && node.tagName.length > 0){
2633
+ return node.tagName;
2634
+ }else{
2635
+ console.log('warn : get nodeName is null, this node ignore translate. node : ');
2636
+ console.log(node);
2637
+ return '';
2638
+ }
2639
+ }
2465
2640
  },
2466
2641
  //向下遍历node
2467
2642
  whileNodes:function(uuid, node){
@@ -2526,9 +2701,12 @@ var translate = {
2526
2701
  // //这个tag标签没有这个 attribute,忽略
2527
2702
  // continue
2528
2703
  //}
2529
-
2530
- //加入翻译
2531
- translate.addNodeToQueue(uuid, node, attributeValue, attributeName);
2704
+
2705
+ //判断当前元素是否在ignore忽略的tag、id、class name中 v3.15.7 增加
2706
+ if(!translate.ignore.isIgnore(node)){
2707
+ //加入翻译
2708
+ translate.addNodeToQueue(uuid, node, attributeValue, attributeName);
2709
+ }
2532
2710
  }
2533
2711
 
2534
2712
  }
@@ -3165,6 +3343,10 @@ var translate = {
3165
3343
  */
3166
3344
  isWhole:function(ele){
3167
3345
 
3346
+ if(translate.whole.isEnableAll){
3347
+ return true;
3348
+ }
3349
+
3168
3350
  //如果设置了 class|tag|id 其中某个,或者 all=true ,那么就是启用,反之未启用
3169
3351
  if((translate.whole.class.length == 0 && translate.whole.tag.length == 0 && translate.whole.id.length == 0) && translate.whole.isEnableAll == false){
3170
3352
  //未设置,那么直接返回false
@@ -3173,9 +3355,7 @@ var translate = {
3173
3355
  if(ele == null || typeof(ele) == 'undefined'){
3174
3356
  return false;
3175
3357
  }
3176
- if(translate.whole.isEnableAll){
3177
- return true;
3178
- }
3358
+
3179
3359
 
3180
3360
  var parentNode = ele;
3181
3361
  var maxnumber = 100; //最大循环次数,避免死循环
@@ -3231,7 +3411,7 @@ var translate = {
3231
3411
  }
3232
3412
 
3233
3413
  //赋予判断的元素向上一级
3234
- parentNode = parentNode.parentNode;
3414
+ parentNode = parentNode.parentElement;
3235
3415
  }
3236
3416
 
3237
3417
  return false;
@@ -4059,7 +4239,7 @@ var translate = {
4059
4239
  },
4060
4240
  //希伯来语
4061
4241
  hebrew:function(str){
4062
- return /[\u0590-\u05FF]/u.test(str);
4242
+ return /^[\u0590-\u05FF]$/.test(str);
4063
4243
  },
4064
4244
  //0-9 阿拉伯数字
4065
4245
  number:function(str){
@@ -4301,7 +4481,7 @@ var translate = {
4301
4481
  var text = '你世好word世界';
4302
4482
  var translateOriginal = '世';
4303
4483
  var translateResult = '世杰'; //翻译结果
4304
- translate.language.textTranslateReplace(text, translateOriginal, translateResult, 'english');
4484
+ translate.util.textReplace(text, translateOriginal, translateResult, 'english');
4305
4485
 
4306
4486
  */
4307
4487
  textReplace:function(text, translateOriginal, translateResult, language){
@@ -4329,7 +4509,7 @@ var translate = {
4329
4509
  let replaceOriginalText = '' + translateOriginal;
4330
4510
 
4331
4511
  //根据不同的语种,如果有的语种需要加空格来进行区分单词,那么也要进行空格的判定
4332
- if(translate.language.wordBlankConnector(translate.to)){
4512
+ if(translate.language.wordBlankConnector(language)){
4333
4513
  let originalIndex = text.indexOf(translateOriginal, currentReplaceEndIndex); //翻译之前,翻译的单词在字符串中的起始坐标(0开始)
4334
4514
  //console.log("originalIndex: "+originalIndex);
4335
4515
 
@@ -4345,21 +4525,38 @@ var translate = {
4345
4525
  }else if(/,/.test(char)){
4346
4526
  replaceResultText = replaceResultText + ', ';
4347
4527
  replaceOriginalText = translateOriginal + ',';
4348
- }else if(!(/\s/.test(char))){
4349
- //不是空白字符,补充上一个空格,用于将两个单词隔开
4350
- //text = text.replace(translateOriginal, translateResult+' ');
4528
+ }else if(/:/.test(char)){
4529
+ replaceResultText = replaceResultText + ': ';
4530
+ replaceOriginalText = translateOriginal + ':';
4531
+ }else if([' ', '\n','\t',']','|', '_','-','/'].indexOf(char) !== -1){
4532
+ // 如果后面的字符是 这些字符,那么不用添加空格隔开
4533
+ }else{
4534
+ //补充上一个空格,用于将两个单词隔开
4351
4535
  replaceResultText = replaceResultText + ' ';
4352
4536
  }
4537
+
4353
4538
  }
4354
4539
 
4355
4540
  //判断它前面是否还有文本
4356
4541
  if(originalIndex > 0){
4357
4542
  let char = text.charAt(originalIndex-1);
4358
4543
  //console.log(char);
4359
- if(!(/\s/.test(char))){
4360
- //不是空白字符,补充上一个空格,用于将两个单词隔开
4544
+
4545
+ if(/。/.test(char)){
4546
+ replaceResultText = '. '+replaceResultText;
4547
+ replaceOriginalText = '。'+replaceOriginalText;
4548
+ }else if(/,/.test(char)){
4549
+ replaceResultText = ', '+replaceResultText;
4550
+ replaceOriginalText = ','+replaceOriginalText;
4551
+ }else if(/:/.test(char)){
4552
+ replaceResultText = ': '+replaceResultText;
4553
+ replaceOriginalText = ':'+replaceOriginalText;
4554
+ }else if([' ', '\n','\t','[', '|', '_','-','/'].indexOf(char) !== -1){
4555
+ // 如果前面的字符是 这些字符,那么不用添加空格隔开
4556
+ }else{
4557
+ //补充上一个空格,用于将两个单词隔开
4361
4558
  //text = text.replace(translateOriginal, ' '+translateResult);
4362
- replaceResultText = ' '+replaceResultText;
4559
+ replaceResultText = ' '+replaceResultText;
4363
4560
  }
4364
4561
  }
4365
4562
  }else{
@@ -4577,6 +4774,8 @@ var translate = {
4577
4774
  return false;
4578
4775
  }
4579
4776
  },
4777
+
4778
+ /*js translate.util.loadMsgJs start*/
4580
4779
  //加载 msg.js
4581
4780
  loadMsgJs:function(){
4582
4781
  if(typeof(msg) != 'undefined'){
@@ -4584,6 +4783,7 @@ var translate = {
4584
4783
  }
4585
4784
  translate.util.synchronizesLoadJs('https://res.zvo.cn/msg/msg.js');
4586
4785
  },
4786
+ /*js translate.util.loadMsgJs end*/
4587
4787
  /*
4588
4788
  对一个对象,按照对象的key的长度进行排序,越长越在前面
4589
4789
  */
@@ -5007,6 +5207,7 @@ var translate = {
5007
5207
  */
5008
5208
  name:'translate.service',
5009
5209
 
5210
+ /*js translate.service.use start*/
5010
5211
  /*
5011
5212
  其实就是设置 translate.service.name
5012
5213
  可以设置为:
@@ -5014,19 +5215,27 @@ var translate = {
5014
5215
  translate.service 自行部署的translate.service 翻译API服务,部署参考: https://translate.zvo.cn/391129.html
5015
5216
  client.edge 使用无服务器的翻译,有edge浏览器接口提供翻译服务
5016
5217
  siliconflow 使用指点云提供的服务器、硅基流动提供的AI算力进行大模型翻译
5218
+ giteeAI 使用 giteeAI , 亚洲、美洲、欧洲 网络节点覆盖
5017
5219
 
5018
5220
  */
5019
5221
  use: function(serviceName){
5020
- if(translate.enterprise.isUse == true){
5222
+ if(typeof(translate.enterprise) != 'undefined' && translate.enterprise.isUse == true){
5021
5223
  console.log('您已启用了企业级翻译通道 translate.enterprise.use(); (文档:https://translate.zvo.cn/4087.html) , 所以您设置的 translate.service.use(\''+serviceName+'\'); (文档:https://translate.zvo.cn/4081.html) 将失效不起作用,有企业级翻译通道全部接管。');
5022
5224
  return;
5023
5225
  }
5226
+ //console.log('--'+serviceName);
5024
5227
  if(typeof(serviceName) == 'string'){
5025
5228
  translate.service.name = serviceName;
5026
5229
  if(serviceName != 'translate.service'){
5027
- if(serviceName == 'siliconflow'){
5230
+ if(serviceName.toLowerCase() == 'giteeai'){
5231
+ //设定翻译接口为GiteeAI的
5232
+ translate.request.api.host=['https://giteeai.zvo.cn/','https://deutsch.enterprise.api.translate.zvo.cn:1000/','https://api.translate.zvo.cn:1000/'];
5233
+ return;
5234
+ }
5235
+ if(serviceName.toLowerCase() == 'siliconflow'){
5028
5236
  //设定翻译接口为硅基流动的
5029
5237
  translate.request.api.host=['https://siliconflow.zvo.cn/','https://america.api.translate.zvo.cn:1414/','https://deutsch.enterprise.api.translate.zvo.cn:1414/'];
5238
+ return;
5030
5239
  }
5031
5240
 
5032
5241
  //增加元素整体翻译能力
@@ -5034,6 +5243,9 @@ var translate = {
5034
5243
  }
5035
5244
  }
5036
5245
  },
5246
+ /*js translate.service.use end*/
5247
+
5248
+ /*js translate.service.edge start*/
5037
5249
  //客户端方式的edge提供机器翻译服务
5038
5250
  edge:{
5039
5251
  api:{ //edge浏览器的翻译功能
@@ -5161,6 +5373,7 @@ var translate = {
5161
5373
 
5162
5374
  }
5163
5375
  }
5376
+ /*js translate.service.edge end*/
5164
5377
  },
5165
5378
  //request请求来源于 https://github.com/xnx3/request
5166
5379
  request:{
@@ -5182,6 +5395,34 @@ var translate = {
5182
5395
  connectTest:'connectTest.json', //用于 translate.js 多节点翻译自动检测网络连通情况
5183
5396
  init:'init.json', //获取最新版本号,跟当前版本进行比对,用于提醒版本升级等使用
5184
5397
 
5398
+ },
5399
+ /*
5400
+ 追加参数, v3.15.9.20250527 增加
5401
+ 所有通过 translate.request.send 进行网络请求的,都会追加上这个参数
5402
+ 默认是空,没有任何追加参数。
5403
+
5404
+ 设置方式: https://translate.zvo.cn/471711.html
5405
+ translate.request.appendParams = {
5406
+ key1:'key1',
5407
+ key2:'key2'
5408
+ }
5409
+ */
5410
+ appendParams:{
5411
+
5412
+ },
5413
+ /*
5414
+ 追加header头的参数, v3.15.13 增加
5415
+ 所有通过 translate.request.send 进行网络请求的,都会追加上这个参数
5416
+ 默认是空,没有任何追加参数。
5417
+
5418
+ 设置方式: https://translate.zvo.cn/471711.html
5419
+ translate.request.appendHeaders = {
5420
+ key1:'key1',
5421
+ Aauthorization:'Bearer xxxxxxxxxx'
5422
+ }
5423
+ */
5424
+ appendHeaders:{
5425
+
5185
5426
  },
5186
5427
  /*
5187
5428
  请求后端接口的响应。无论是否成功,都会触发此处。
@@ -5195,6 +5436,7 @@ var translate = {
5195
5436
  //console.log(xhr);
5196
5437
  },
5197
5438
 
5439
+
5198
5440
  /*
5199
5441
  速度检测控制中心, 检测主备翻译接口的响应速度进行排列,真正请求时,按照排列的顺序进行请求
5200
5442
  v2.8.2增加
@@ -5460,8 +5702,10 @@ var translate = {
5460
5702
  }
5461
5703
 
5462
5704
  //企业级翻译自动检测
5463
- translate.enterprise.automaticAdaptationService();
5464
-
5705
+ if(typeof(translate.enterprise) != 'undefined'){
5706
+ translate.enterprise.automaticAdaptationService();
5707
+ }
5708
+
5465
5709
  // ------- edge start --------
5466
5710
  var url = translate.request.getUrl(path);
5467
5711
  //if(url.indexOf('edge') > -1 && path == translate.request.api.translate){
@@ -5512,9 +5756,16 @@ var translate = {
5512
5756
  //加入浏览器默认语种 v3.6.1 增加,以便更好的进行自动切换语种
5513
5757
  data.browserDefaultLanguage = translate.util.browserDefaultLanguage();
5514
5758
 
5515
- //加入key
5516
- if(typeof(translate.enterprise.key) != 'undefined' && typeof(translate.enterprise.key) == 'string' && translate.enterprise.key.length > 0){
5517
- data.key = translate.enterprise.key;
5759
+ //追加附加参数
5760
+ for(var apindex in translate.request.appendParams){
5761
+ data[apindex] = translate.request.appendParams[apindex];
5762
+ }
5763
+
5764
+ if(typeof(translate.enterprise) != 'undefined'){
5765
+ //加入key
5766
+ if(typeof(translate.enterprise.key) != 'undefined' && typeof(translate.enterprise.key) == 'string' && translate.enterprise.key.length > 0){
5767
+ data.key = translate.enterprise.key;
5768
+ }
5518
5769
  }
5519
5770
 
5520
5771
  //组合参数
@@ -5547,6 +5798,12 @@ var translate = {
5547
5798
  xhr.setRequestHeader(index,headers[index]);
5548
5799
  }
5549
5800
  }
5801
+
5802
+ //追加附加参数
5803
+ for(var ahindex in translate.request.appendHeaders){
5804
+ xhr.setRequestHeader(ahindex,translate.request.appendHeaders[ahindex]);
5805
+ }
5806
+
5550
5807
  if(translate.service.name == 'translate.service'){
5551
5808
  xhr.setRequestHeader('currentpage', window.location.href+'');
5552
5809
  }
@@ -5586,8 +5843,8 @@ var translate = {
5586
5843
 
5587
5844
  //判断是否是v2版本的翻译,如果是 translate.service 模式并且没有使用企业级翻译,参会提示
5588
5845
  //2024.3月底开始,翻译使用量增加的太快,开源的翻译服务器有点扛不住经常出故障,所以直接把这个提示加到这里
5589
- if(translate.service.name == 'translate.service' && !translate.enterprise.isUse){
5590
- console.log('----- translate.js 提示 -----\n非常抱歉,translate.service 开源免费的翻译服务器当前并发及流量太大导致阻塞!\n这个情况是时不时发生的,大概一天24小时可能会有一二十分钟这种情况,主要是因为使用量太大了,月使用量能超四十亿次,所以在高发时会出现这种情况。\n解决这种情况可以有两种方案:\n【方案一】:使用采用最新版本 3.8.0及更高版本,js引用文件为 https://cdn.staticfile.net/translate.js/3.8.0/translate.js 并且使用 client.edge 模式 (增加一行设置代码就好,可参考 https://translate.zvo.cn/4081.html ),这样就不会再出现这种情况了。这个是我们的 translate.service 通道阻塞导致,更换别的通道就可以避免这样了。而且这个方案也是完全免费的。 \n【方案二】:采用企业级稳定翻译通道 ,但是这个相比于 方案一 来说,是有一定的收费的,大概一年600,这个就是专门为了高速及高稳定准备的,而相比于这个方案二,方案一则是全免费的。 因为方案二我们是部署了两个集群,而每个集群又下分了数个网络节点,包含中国大陆、香港、美国、欧洲、 等多个州,充分保障稳定、高效,同样也产生了不少成本,所以才需要付费。更多信息说明可以参考: http://translate.zvo.cn/4087.html \n\n-------------');
5846
+ if(translate.service.name == 'translate.service'){
5847
+ console.log('----- translate.js 提示 -----\n翻译服务响应异常,解决这种情况可以有两种方案:\n【方案一】:使用采用最新版本 3.16.0及更高版本,js引用文件为 https://cdn.staticfile.net/translate.js/3.16.0/translate.js 并且使用 client.edge 模式 (增加一行设置代码就好,可参考 https://translate.zvo.cn/4081.html ),这样就不会再出现这种情况了,而且这个方案也是完全免费的。 \n【方案二】:采用企业级稳定翻译通道 ,但是这个相比于 方案一 来说,是有一定的收费的,大概一年600,这个就是专门为了高速及高稳定准备的,而相比于这个方案二,方案一则是全免费的。 因为方案二我们是部署了两个集群,而每个集群又下分了数个网络节点,包含中国大陆、香港、美国、欧洲、 等多个州,充分保障稳定、高效,同样也产生了不少成本,所以才需要付费。更多信息说明可以参考: http://translate.zvo.cn/4087.html \n【方案三】:私有部署你自己的翻译通道,并且启用内存级翻译缓存,毫秒级响应,但是需要依赖一台1核2G服务器,是最推荐的方式。具体参考:https://translate.zvo.cn/391129.html\n-------------');
5591
5848
  }
5592
5849
 
5593
5850
  //console.log(xhr);
@@ -5681,6 +5938,19 @@ var translate = {
5681
5938
  }
5682
5939
  }
5683
5940
 
5941
+ /*
5942
+ for(var i = texts.length - 1; i >= 0; i--){
5943
+ console.log(texts[i]);
5944
+
5945
+ //判断是否在浏览器缓存中出现了
5946
+ var cacheHash = translate.util.hash(texts[i]);
5947
+ var cache = translate.storage.get('hash_'+translate.to+'_'+cacheHash);
5948
+ if(cache != null && cache.length > 0){
5949
+ //缓存中发现了这个得结果,那这个就不需要再进行翻译了
5950
+ }
5951
+ }
5952
+ */
5953
+
5684
5954
 
5685
5955
  var url = translate.request.api.translate;
5686
5956
  var data = {
@@ -5787,6 +6057,10 @@ var translate = {
5787
6057
  }
5788
6058
  }
5789
6059
  }, 100);
6060
+ if(typeof(PerformanceObserver) == 'undefined'){
6061
+ console.log('因浏览器版本较低, translate.request.listener.start() 中 PerformanceObserver 对象不存在,浏览器不支持,所以 translate.request.listener.start() 未生效。');
6062
+ return;
6063
+ }
5790
6064
 
5791
6065
  const observer = new PerformanceObserver((list) => {
5792
6066
  var translateExecute = false; //是否需要执行翻译 true 要执行
@@ -5810,8 +6084,8 @@ var translate = {
5810
6084
  break;
5811
6085
  }
5812
6086
  }
5813
- //client.edge 判断
5814
- if(url.indexOf(translate.service.edge.api.auth) > -1){
6087
+ //client.edge 判断 translate.service.edge可能会被精简translate.js定制时给直接干掉,所以提前加个判断
6088
+ if(typeof(translate.service.edge) != 'undefined' && url.indexOf(translate.service.edge.api.auth) > -1){
5815
6089
  ignoreUrl = true;
5816
6090
  }
5817
6091
  if(url.indexOf('.microsofttranslator.com/translate') > -1){
@@ -5838,19 +6112,157 @@ var translate = {
5838
6112
  translate.request.listener.addExecute();
5839
6113
  }
5840
6114
  });
5841
- // 配置observer观察的类型为"resource"
5842
- observer.observe({ type: "resource", buffered: true });
6115
+
6116
+ //v3.15.14.20250617 增加
6117
+ // 优先使用 entryTypes 兼容 ES5 的写法
6118
+
6119
+ var supportedTypes = PerformanceObserver.supportedEntryTypes;
6120
+ if (supportedTypes) {
6121
+ var hasResource = false;
6122
+ for (var i = 0; i < supportedTypes.length; i++) {
6123
+ if (supportedTypes[i] === "resource") {
6124
+ hasResource = true;
6125
+ break;
6126
+ }
6127
+ }
6128
+ if (hasResource) {
6129
+ try {
6130
+ observer.observe({ entryTypes: ["resource"] });
6131
+ return;
6132
+ } catch (e) {
6133
+ console.log("PerformanceObserver entryTypes 失败,尝试 type 参数");
6134
+ }
6135
+ }
6136
+ }
6137
+
6138
+
6139
+ // 回退到 type 参数
6140
+ try {
6141
+ observer.observe({ type: "resource", buffered: true });
6142
+ console.log("使用 PerformanceObserver type");
6143
+ } catch (e) {
6144
+ console.log("当前浏览器不支持 PerformanceObserver 的任何参数, translate.request.listener.start() 未启动");
6145
+ }
6146
+
5843
6147
  }
5844
6148
  }
5845
6149
  },
5846
6150
  //存储,本地缓存
5847
6151
  storage:{
6152
+ /*js translate.storage.IndexedDB start*/
6153
+ //对浏览器的 IndexedDB 操作
6154
+ IndexedDB:{
6155
+ db: null,
6156
+ // 初始化数据库
6157
+ initDB: function () {
6158
+ const self = this;
6159
+ return new Promise((resolve, reject) => {
6160
+ const DB_NAME = 'translate.js';
6161
+ const STORE_NAME = 'kvStore';
6162
+ const DB_VERSION = 1;
6163
+
6164
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
6165
+
6166
+ request.onupgradeneeded = function(event) {
6167
+ const upgradedDb = event.target.result;
6168
+ if (!upgradedDb.objectStoreNames.contains(STORE_NAME)) {
6169
+ upgradedDb.createObjectStore(STORE_NAME, { keyPath: 'key' });
6170
+ }
6171
+ };
6172
+
6173
+ request.onsuccess = function(event) {
6174
+ self.db = event.target.result;
6175
+ resolve();
6176
+ };
6177
+
6178
+ request.onerror = function(event) {
6179
+ reject('IndexedDB 打开失败');
6180
+ };
6181
+ });
6182
+ },
6183
+ /*
6184
+ 存储键值对
6185
+ 使用方式:
6186
+ await translate.storage.indexedDB.set("user_001", { name: "Alice" });
6187
+ */
6188
+ set: async function (key, value) {
6189
+ if (!this.db) await this.initDB();
6190
+
6191
+ return new Promise((resolve, reject) => {
6192
+ const tx = this.db.transaction('kvStore', 'readwrite');
6193
+ const store = tx.objectStore('kvStore');
6194
+ const item = { key, value };
6195
+ const request = store.put(item);
6196
+
6197
+ request.onsuccess = () => resolve();
6198
+ request.onerror = () => reject('写入失败');
6199
+ });
6200
+ },
6201
+ /*
6202
+ 获取键对应的值
6203
+ 使用方式:
6204
+ var user = await translate.storage.indexedDB.get("user_001");
6205
+ */
6206
+ get: async function (key) {
6207
+ if (!this.db) await this.initDB();
6208
+
6209
+ return new Promise((resolve, reject) => {
6210
+ const tx = this.db.transaction('kvStore', 'readonly');
6211
+ const store = tx.objectStore('kvStore');
6212
+ const request = store.get(key);
6213
+
6214
+ request.onsuccess = () => {
6215
+ const result = request.result;
6216
+ resolve(result ? result.value : undefined);
6217
+ };
6218
+
6219
+ request.onerror = () => reject('读取失败');
6220
+ });
6221
+ },
6222
+ /*
6223
+ 列出针对key进行模糊匹配的所有键值对
6224
+ 使用方式:
6225
+ const users = await translate.storage.indexedDB.list("*us*r*");
6226
+ 其中传入的key可以模糊搜索,其中的 * 标识另个或多个
6227
+ */
6228
+ list: async function (key = '') {
6229
+ if (!this.db) await this.initDB();
6230
+
6231
+ return new Promise((resolve, reject) => {
6232
+ const tx = this.db.transaction('kvStore', 'readonly');
6233
+ const store = tx.objectStore('kvStore');
6234
+ const request = store.openCursor();
6235
+ const results = [];
6236
+
6237
+ // 将通配符 pattern 转换为正则表达式
6238
+ const regexStr = '^' + key.replace(/\*/g, '.*') + '$';
6239
+ const regex = new RegExp(regexStr);
6240
+
6241
+ request.onsuccess = (event) => {
6242
+ const cursor = event.target.result;
6243
+ if (cursor) {
6244
+ if (regex.test(cursor.key)) {
6245
+ results.push({ key: cursor.key, value: cursor.value.value });
6246
+ }
6247
+ cursor.continue();
6248
+ } else {
6249
+ resolve(results);
6250
+ }
6251
+ };
6252
+
6253
+ request.onerror = () => reject('游标读取失败');
6254
+ });
6255
+ }
6256
+ },
6257
+ /*js translate.storage.IndexedDB end*/
6258
+
5848
6259
  set:function(key,value){
5849
6260
  localStorage.setItem(key,value);
5850
6261
  },
5851
6262
  get:function(key){
5852
6263
  return localStorage.getItem(key);
5853
6264
  }
6265
+
5854
6266
  },
5855
6267
  //针对图片进行相关的语种图片替换
5856
6268
  images:{
@@ -5971,6 +6383,7 @@ var translate = {
5971
6383
  return str;
5972
6384
  }
5973
6385
  },
6386
+ /*js translate.reset start*/
5974
6387
  //对翻译结果进行复原。比如当前网页是简体中文的,被翻译为了英文,执行此方法即可复原为网页本身简体中文的状态,而无需在通过刷新页面来实现
5975
6388
  reset:function(){
5976
6389
  var currentLanguage = translate.language.getCurrent(); //获取当前翻译至的语种
@@ -6024,7 +6437,9 @@ var translate = {
6024
6437
  //重新渲染select
6025
6438
  translate.selectLanguageTag.render();
6026
6439
  },
6440
+ /*js translate.reset end*/
6027
6441
 
6442
+ /*js translate.selectionTranslate start*/
6028
6443
  /*
6029
6444
  划词翻译,鼠标在网页中选中一段文字,会自动出现对应翻译后的文本
6030
6445
  有网友 https://gitee.com/huangguishen 提供。
@@ -6071,7 +6486,9 @@ var translate = {
6071
6486
  document.addEventListener('click', (event)=>{ document.querySelector('#translateTooltip').style.display = "none"}, false);
6072
6487
  }
6073
6488
  },
6489
+ /*js translate.selectionTranslate end*/
6074
6490
 
6491
+ /*js translate.enterprise start*/
6075
6492
  /*
6076
6493
  企业级翻译服务
6077
6494
  注意,这个企业级翻译中的不在开源免费之中,企业级翻译服务追求的是高稳定,这个是收费的!详情可参考:http://translate.zvo.cn/43262.html
@@ -6113,6 +6530,7 @@ var translate = {
6113
6530
  /* 企业级翻译通道的key, v3.12.3.20250107 增加,针对打包成APP的场景 */
6114
6531
  key:'',
6115
6532
  },
6533
+ /*js translate.enterprise end*/
6116
6534
 
6117
6535
  /*
6118
6536
  如果使用的是 translate.service 翻译通道,那么翻译后的语种会自动以小写的方式进行显示。
@@ -6126,6 +6544,7 @@ var translate = {
6126
6544
  },
6127
6545
  */
6128
6546
 
6547
+ /*js translate.init start*/
6129
6548
  /*
6130
6549
  初始化,如版本检测、初始数据加载等。 v2.11.11.20240124 增加
6131
6550
  会自动在 translate.js 加载后的 200毫秒后 执行,进行初始化。同时也是节点测速
@@ -6167,12 +6586,66 @@ var translate = {
6167
6586
  }catch(e){
6168
6587
  }
6169
6588
  },
6589
+ /*js translate.init end*/
6170
6590
 
6591
+ /*js translate.progress start*/
6171
6592
  /*
6172
6593
  翻译执行的进展相关
6173
6594
  比如,浏览器本地缓存没有,需要走API接口的文本所在的元素区域,出现 记载中的动画蒙版,给用户以友好的使用提示
6174
6595
  */
6175
6596
  progress:{
6597
+ style: `
6598
+ /* CSS部分 */
6599
+ /* 灰色水平加载动画 */
6600
+ .translate_api_in_progress {
6601
+ position: relative;
6602
+ overflow: hidden; /* 隐藏超出部分的动画 */
6603
+ }
6604
+
6605
+ /* 蒙版层 */
6606
+ .translate_api_in_progress::after {
6607
+ content: '';
6608
+ position: absolute;
6609
+ top: 0;
6610
+ left: 0;
6611
+ width: 100%;
6612
+ height: 100%;
6613
+ background: rgba(255, 255, 255, 1); /* 半透明白色遮罩 */
6614
+ z-index: 2;
6615
+ }
6616
+
6617
+ /* 水平加载条动画 */
6618
+ .translate_api_in_progress::before {
6619
+ content: '';
6620
+ position: absolute;
6621
+ top: 50%;
6622
+ left: 0;
6623
+ width: 100%;
6624
+ height:100%; /* 细线高度 */
6625
+ background: linear-gradient(
6626
+ 90deg,
6627
+ transparent 0%,
6628
+ #e8e8e8 25%, /* 浅灰色 */
6629
+ #d0d0d0 50%, /* 中灰色 */
6630
+ #e8e8e8 75%, /* 浅灰色 */
6631
+ transparent 100%
6632
+ );
6633
+ background-size: 200% 100%;
6634
+ animation: translate_api_in_progress_horizontal-loader 3.5s linear infinite;
6635
+ z-index: 3;
6636
+ transform: translateY(-50%);
6637
+ }
6638
+
6639
+ @keyframes translate_api_in_progress_horizontal-loader {
6640
+ 0% {
6641
+ background-position: 200% 0;
6642
+ }
6643
+ 100% {
6644
+ background-position: -200% 0;
6645
+ }
6646
+ }
6647
+ `,
6648
+
6176
6649
  /*
6177
6650
  通过文本翻译API进行的
6178
6651
  */
@@ -6185,57 +6658,7 @@ var translate = {
6185
6658
  // 创建一个 style 元素
6186
6659
  const style = document.createElement('style');
6187
6660
  // 设置 style 元素的文本内容为要添加的 CSS 规则
6188
- style.textContent = `
6189
- /* CSS部分 */
6190
- /* 灰色水平加载动画 */
6191
- .translate_api_in_progress {
6192
- position: relative;
6193
- overflow: hidden; /* 隐藏超出部分的动画 */
6194
- }
6195
-
6196
- /* 蒙版层 */
6197
- .translate_api_in_progress::after {
6198
- content: '';
6199
- position: absolute;
6200
- top: 0;
6201
- left: 0;
6202
- width: 100%;
6203
- height: 100%;
6204
- background: rgba(255, 255, 255, 1); /* 半透明白色遮罩 */
6205
- z-index: 2;
6206
- }
6207
-
6208
- /* 水平加载条动画 */
6209
- .translate_api_in_progress::before {
6210
- content: '';
6211
- position: absolute;
6212
- top: 50%;
6213
- left: 0;
6214
- width: 100%;
6215
- height:100%; /* 细线高度 */
6216
- background: linear-gradient(
6217
- 90deg,
6218
- transparent 0%,
6219
- #e8e8e8 25%, /* 浅灰色 */
6220
- #d0d0d0 50%, /* 中灰色 */
6221
- #e8e8e8 75%, /* 浅灰色 */
6222
- transparent 100%
6223
- );
6224
- background-size: 200% 100%;
6225
- animation: translate_api_in_progress_horizontal-loader 3.5s linear infinite;
6226
- z-index: 3;
6227
- transform: translateY(-50%);
6228
- }
6229
-
6230
- @keyframes translate_api_in_progress_horizontal-loader {
6231
- 0% {
6232
- background-position: 200% 0;
6233
- }
6234
- 100% {
6235
- background-position: -200% 0;
6236
- }
6237
- }
6238
- `;
6661
+ style.textContent = translate.progress.style;
6239
6662
  // 将 style 元素插入到 head 元素中
6240
6663
  document.head.appendChild(style);
6241
6664
 
@@ -6317,6 +6740,7 @@ var translate = {
6317
6740
  }
6318
6741
  }
6319
6742
  },
6743
+ /*js translate.progress end*/
6320
6744
 
6321
6745
  /*js dispose start*/
6322
6746
  /*
@@ -6483,7 +6907,415 @@ var translate = {
6483
6907
  },
6484
6908
  /*js dispose end*/
6485
6909
 
6910
+ /*js translate.network start*/
6911
+ /*
6912
+ 网络请求数据拦截并翻译
6913
+ 当用户触发ajax请求时,它可以针对ajax请求中的某个参数,进行获取,并进行翻译,将翻译后的文本赋予这个参数,然后再放开请求。
6914
+
6915
+ 使用场景如:
6916
+ 搜索场景,原本是中文的页面,翻译为英文后,给美国人使用,美国人使用时,进行搜索,输入的是英文,然后点击搜索按钮,发起搜索。
6917
+ 然后此会拦截网络请求,将请求中用户输入的搜索文本的内容提取出来,识别它输入的是中文还是英文,如果不是本地的语种中文,那就将其翻译为中文,然后再赋予此请求的这个参数中,然后再放开这次请求。
6918
+ 这样请求真正到达服务端接口时,服务端接受到的搜索的文本内容实际就是翻译后的中文文本,而不是用户输入的英文文本。
6919
+
6920
+ 何时自动进行翻译:
6921
+ 1. 当前用户没有进行切换语言
6922
+ 2. 切换语言了,但是输入的文本的语言是不需要进行翻译的, 输入的文本本身就是本地的语言
6923
+ 这两种情况那就不需要拦截翻译
6924
+
6486
6925
 
6926
+ */
6927
+ network: {
6928
+ // 原始方法保存
6929
+ originalOpen: XMLHttpRequest.prototype.open,
6930
+ originalSend: XMLHttpRequest.prototype.send,
6931
+ setRequestHeaderOriginal: XMLHttpRequest.prototype.setRequestHeader,
6932
+
6933
+ // 规则配置
6934
+ rules: [
6935
+ {
6936
+ url: /https:\/\/www\.guanleiming\.com\/a\/b\/.html/,
6937
+ methods: ['GET', 'POST'],
6938
+ params: ['a','b1']
6939
+ }
6940
+ ],
6941
+ //根据 当前请求的url 跟 method 来判断当前请求是否符合规则,
6942
+ //如果符合,则返回符合的 rule 规则,也就是 translate.network.rules 中配置的某个。
6943
+ //如果没有找到符合的,则返回 null
6944
+ getRuleMatch:function(url, method){
6945
+ for (let i = 0; i < translate.network.rules.length; i++) {
6946
+ const rule = translate.network.rules[i];
6947
+
6948
+ // 检查 URL 是否匹配
6949
+ if(typeof(rule.url) == 'undefined' && rule.url == ''){
6950
+ console.log('WARINNG : translate.network.rule find url is null:');
6951
+ console.log(rule);
6952
+ continue;
6953
+ }
6954
+ //console.log(rule);
6955
+ const isUrlMatch = rule.url.test(url);
6956
+ if(!isUrlMatch){
6957
+ continue;
6958
+ }
6959
+
6960
+ // 检查方法是否匹配(忽略大小写)
6961
+ const isMethodMatch = rule.methods.includes(method.toUpperCase());
6962
+ if(!isMethodMatch){
6963
+ continue;
6964
+ }
6965
+
6966
+ return rule;
6967
+ }
6968
+
6969
+ return null;
6970
+ },
6971
+ use:function(){
6972
+ // 应用Hook
6973
+ XMLHttpRequest.prototype.open = function(...args) {
6974
+ return translate.network.hookOpen.apply(this, args);
6975
+ };
6976
+
6977
+ XMLHttpRequest.prototype.send = function(...args) {
6978
+ return translate.network.hookSend.apply(this, args);
6979
+ };
6980
+
6981
+ // 劫持 setRequestHeader 方法
6982
+ XMLHttpRequest.prototype.setRequestHeader = function(...args) {
6983
+ return translate.network.setRequestHeader.apply(this, args);
6984
+ };
6985
+
6986
+ translate.network.fetch.use();
6987
+ },
6988
+ // 私有工具方法
6989
+ _translateText(text) {
6990
+ if(translate.language.getLocal() == translate.language.getCurrent() || (typeof(text) == 'string' && text.length > 0 && translate.language.recognition(text).languageName == translate.language.getLocal())){
6991
+ /*
6992
+ 1. 没有进行切换语言
6993
+ 2. 切换语言了,但是输入的文本的语言是不需要进行翻译的, 输入的文本本身就是本地的语言
6994
+
6995
+ 这两种情况那就不需要拦截翻译
6996
+ */
6997
+
6998
+ return new Promise((resolve, reject) => {
6999
+ const obj = {
7000
+ from: 'auto',
7001
+ to: translate.language.getLocal(),
7002
+ text: [text]
7003
+ };
7004
+
7005
+ resolve(obj);
7006
+ });
7007
+ }else{
7008
+ //有进行切换了,那进行翻译,将其他语种翻译为当前的本地语种
7009
+ return new Promise((resolve, reject) => {
7010
+ const obj = {
7011
+ from: 'auto',
7012
+ to: translate.language.getLocal(),
7013
+ texts: [text]
7014
+ };
7015
+
7016
+ //console.log('翻译请求:', obj);
7017
+ translate.request.translateText(obj, function(data) {
7018
+ if (data.result === 1) {
7019
+ resolve(data);
7020
+ } else {
7021
+ reject(data);
7022
+ }
7023
+ });
7024
+ });
7025
+ }
7026
+
7027
+ },
7028
+ //劫持 setRequestHeader
7029
+ setRequestHeader: function(header, value) {
7030
+ if (this._requestContext) {
7031
+ this._requestContext.headers = this._requestContext.headers || {};
7032
+ this._requestContext.headers[header] = value;
7033
+ }
7034
+
7035
+ return translate.network.setRequestHeaderOriginal.call(this, header, value);
7036
+ },
7037
+ // 请求处理工具
7038
+ RequestHandler: {
7039
+ async handleGet(url, rule) {
7040
+ //console.log(url);
7041
+ //console.log(rule);
7042
+ if(typeof(rule.params) == 'undefined' && typeof(rule.params.length) == 'undefined' && rule.params.length < 1){
7043
+ console.log('WARINNG: rule not find params , rule : ');
7044
+ console.log(rule);
7045
+ rule.params = [];
7046
+ }
7047
+
7048
+
7049
+ try {
7050
+ const urlObj = new URL(url, window.location.origin);
7051
+ const params = urlObj.searchParams;
7052
+ //console.log(rule.params);
7053
+
7054
+ //for (const paramName in rule.params) {
7055
+ for(var p = 0; p < rule.params.length; p++){
7056
+ var paramName = rule.params[p];
7057
+ //console.log(paramName);
7058
+ if (params.has(paramName)) {
7059
+ const original = params.get(paramName);
7060
+ const translateResultData = await translate.network._translateText(original);
7061
+
7062
+ if(typeof(translateResultData) == 'undefined'){
7063
+ console.log('WARINNG: translateResultData is undefined');
7064
+ }else if(typeof(translateResultData.result) == 'undefined'){
7065
+ console.log('WARINNG: translateResultData.result is undefined');
7066
+ }else if(translateResultData.result != 1){
7067
+ console.log('WARINNG: translateResultData.result failure : '+translateResultData.info);
7068
+ }else{
7069
+ params.set(paramName, decodeURIComponent(translateResultData.text[0]));
7070
+ }
7071
+
7072
+ }
7073
+ }
7074
+
7075
+ return urlObj.toString();
7076
+ } catch (e) {
7077
+ console.warn('GET处理失败:', e);
7078
+ return url;
7079
+ }
7080
+ },
7081
+
7082
+ async handleForm(body, rule) {
7083
+ try {
7084
+ const params = new URLSearchParams(body);
7085
+ const modified = {...params};
7086
+
7087
+ for (const paramName of rule.params) {
7088
+ if (params.has(paramName)) {
7089
+ const original = params.get(paramName);
7090
+ const translated = await translate.network._translateText(original);
7091
+ modified[paramName] = translated;
7092
+ }
7093
+ }
7094
+
7095
+ return new URLSearchParams(modified).toString();
7096
+ } catch (e) {
7097
+ console.warn('表单处理失败:', e);
7098
+ return body;
7099
+ }
7100
+ },
7101
+
7102
+ async handleJson(body, rule) {
7103
+ try {
7104
+ const json = JSON.parse(body);
7105
+ const modified = {...json};
7106
+
7107
+ for (const paramName of rule.params) {
7108
+ if (modified.hasOwnProperty(paramName)) {
7109
+ const original = modified[paramName];
7110
+ modified[paramName] = await translate.network._translateText(original);
7111
+ }
7112
+ }
7113
+
7114
+ return JSON.stringify(modified);
7115
+ } catch (e) {
7116
+ console.warn('JSON处理失败:', e);
7117
+ return body;
7118
+ }
7119
+ }
7120
+ },
7121
+
7122
+ // 请求上下文管理
7123
+ _requestContext: null,
7124
+
7125
+
7126
+
7127
+ // Hook open 方法
7128
+ hookOpen(method, url, async, user, password) {
7129
+ let matchedRule = null;
7130
+ this._requestContext = {
7131
+ method: method.toUpperCase(),
7132
+ originalUrl: url,
7133
+ async: async,
7134
+ user: user,
7135
+ password: password,
7136
+ matchedRule: translate.network.getRuleMatch(url, method)
7137
+ };
7138
+
7139
+ return translate.network.originalOpen.call(this, method, url, async, user, password);
7140
+ },
7141
+
7142
+ // Hook send 方法
7143
+ hookSend(body) {
7144
+ const ctx = this._requestContext;
7145
+ if (!ctx || !ctx.matchedRule) {
7146
+ return translate.network.originalSend.call(this, body);
7147
+ }
7148
+
7149
+ const processRequest = async () => {
7150
+ let modifiedBody = body;
7151
+ const method = ctx.method;
7152
+
7153
+ try {
7154
+ // 处理GET请求
7155
+ //if (method === 'GET') {
7156
+ const newUrl = await translate.network.RequestHandler.handleGet(ctx.originalUrl, ctx.matchedRule);
7157
+ translate.network.originalOpen.call(this, method, newUrl, ctx.async, ctx.user, ctx.password);
7158
+ //}
7159
+
7160
+ // 恢复请求头
7161
+ if (ctx.headers) {
7162
+ for (const header in ctx.headers) {
7163
+ translate.network.setRequestHeaderOriginal.call(this, header, ctx.headers[header]);
7164
+ }
7165
+ }
7166
+
7167
+ // 处理POST请求
7168
+ if (method === 'POST') {
7169
+ if(typeof(body) != 'undefined' && body != null && body.length < 2000){
7170
+ var isJsonBody = false; //是否是json格式的数据,是否json已经处理了, true 是
7171
+ if(body.trim().indexOf('[') == 0 || body.trim().indexOf('{') == 0){
7172
+ //可能是json
7173
+ try{
7174
+ modifiedBody = await translate.network.RequestHandler.handleJson(body, ctx.matchedRule);
7175
+ isJsonBody = true;
7176
+ }catch(je){
7177
+ isJsonBody = false;
7178
+ }
7179
+ }
7180
+ if(!isJsonBody){
7181
+ try{
7182
+ modifiedBody = await translate.network.RequestHandler.handleForm(body, ctx.matchedRule);
7183
+ }catch(je){
7184
+ }
7185
+ }
7186
+ }
7187
+ }
7188
+ } catch (e) {
7189
+ console.warn('请求处理异常:', e);
7190
+ }
7191
+
7192
+ translate.network.originalSend.call(this, modifiedBody);
7193
+ };
7194
+
7195
+ // 异步处理
7196
+ if (ctx.async !== false) {
7197
+ processRequest.call(this);
7198
+ } else {
7199
+ console.warn('同步请求不支持翻译拦截');
7200
+ translate.network.originalSend.call(this, body);
7201
+ }
7202
+ },
7203
+ //fetch请求
7204
+ fetch:{
7205
+ originalFetch: window.fetch,
7206
+
7207
+ // 保存原始 fetch 方法
7208
+ use: function () {
7209
+ const self = this;
7210
+ window.fetch = function (...args) {
7211
+ return self.hookFetch.apply(self, args);
7212
+ };
7213
+ },
7214
+
7215
+ // 拦截 fetch 请求
7216
+ hookFetch: async function (input, init) {
7217
+ const request = new Request(input, init);
7218
+ const url = request.url;
7219
+ const method = request.method;
7220
+
7221
+ // 获取匹配规则
7222
+ const rule = translate.network.getRuleMatch(url, method);
7223
+ if (!rule) {
7224
+ return this.originalFetch.call(window, request);
7225
+ }
7226
+
7227
+ // 初始化请求上下文
7228
+ const ctx = {
7229
+ method,
7230
+ url,
7231
+ headers: {},
7232
+ rule,
7233
+ isModified: false
7234
+ };
7235
+
7236
+ // 保存请求头
7237
+ request.headers.forEach((value, key) => {
7238
+ ctx.headers[key] = value;
7239
+ });
7240
+
7241
+ this._requestContext = ctx;
7242
+
7243
+ try {
7244
+ const newUrl = await translate.network.RequestHandler.handleGet(url, rule);
7245
+ // 处理 GET 请求
7246
+ if (method === 'GET') {
7247
+
7248
+ const newRequest = new Request(newUrl, {
7249
+ method,
7250
+ headers: new Headers(ctx.headers),
7251
+ mode: request.mode,
7252
+ credentials: request.credentials,
7253
+ cache: request.cache,
7254
+ redirect: request.redirect,
7255
+ referrer: request.referrer,
7256
+ referrerPolicy: request.referrerPolicy
7257
+ });
7258
+ return this.originalFetch.call(window, newRequest);
7259
+ }
7260
+
7261
+ // 处理 POST 请求
7262
+ if (method === 'POST') {
7263
+ let body = null;
7264
+ if (request.body) {
7265
+ body = await request.clone().text();
7266
+ }
7267
+
7268
+ const contentType = request.headers.get('Content-Type');
7269
+ let modifiedBody = body;
7270
+
7271
+ if(typeof(body) != 'undefined' && body != null && body.length < 2000){
7272
+ var isJsonBody = false; //是否是json格式的数据,是否json已经处理了, true 是
7273
+ if(body.trim().indexOf('[') == 0 || body.trim().indexOf('{') == 0){
7274
+ //可能是json
7275
+ try{
7276
+ modifiedBody = await translate.network.RequestHandler.handleJson(body, rule);
7277
+ isJsonBody = true;
7278
+ }catch(je){
7279
+ isJsonBody = false;
7280
+ }
7281
+ }
7282
+ if(!isJsonBody){
7283
+ try{
7284
+ modifiedBody = await translate.network.RequestHandler.handleForm(body, rule);
7285
+ }catch(je){
7286
+ }
7287
+ }
7288
+ }
7289
+
7290
+ const newRequest = new Request(newUrl, {
7291
+ method,
7292
+ headers: new Headers(ctx.headers),
7293
+ body: modifiedBody,
7294
+ mode: request.mode,
7295
+ credentials: request.credentials,
7296
+ cache: request.cache,
7297
+ redirect: request.redirect,
7298
+ referrer: request.referrer,
7299
+ referrerPolicy: request.referrerPolicy
7300
+ });
7301
+
7302
+ return this.originalFetch.call(window, newRequest);
7303
+ }
7304
+
7305
+ // 其他方法直接返回原始请求
7306
+ return this.originalFetch.call(window, request);
7307
+ } catch (e) {
7308
+ console.warn('fetch 请求处理异常:', e);
7309
+ return this.originalFetch.call(window, request);
7310
+ }
7311
+ },
7312
+ // 请求上下文管理
7313
+ _requestContext: null
7314
+
7315
+ }
7316
+ }
7317
+
7318
+ /*js translate.network end*/
6487
7319
 
6488
7320
  }
6489
7321
  /*
@@ -6494,7 +7326,7 @@ var translate = {
6494
7326
  */
6495
7327
  var nodeuuid = {
6496
7328
  index:function(node){
6497
- var parent = node.parentNode;
7329
+ var parent = node.parentElement;
6498
7330
  if(parent == null){
6499
7331
  return '';
6500
7332
  }
@@ -6534,29 +7366,18 @@ var nodeuuid = {
6534
7366
  uuid = id + uuid;
6535
7367
  }
6536
7368
  //console.log(uuid)
6537
- n = n.parentNode;
7369
+ n = n.parentElement;
6538
7370
  }
6539
7371
  return uuid;
6540
- },
6541
- /*
6542
- 开启远程调试能力,当你使用中遇到问题,需要向开源作者求助时,可以在你项目中主动调用这个方法,也就是 translate.enableRemoteDebug(); 即可启动远程调试能力
6543
- 开源项目作者就可以对你当前出错的页面进行远程调试排查问题所在。当然前提是你的页面要保持别关闭。
6544
-
6545
- 这个能力是通过开源项目 https://github.com/HuolalaTech/page-spy-web 来实现的
6546
-
6547
- */
6548
- enableRemoteDebug:function(){
6549
-
6550
- /*
6551
-
6552
- 待同事实现
6553
- */
7372
+ }
7373
+ }
6554
7374
 
6555
- },
6556
7375
 
6557
- }
7376
+ /*js copyright-notice start*/
6558
7377
  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
+ /*js copyright-notice end*/
6559
7379
 
7380
+ /*js amd-cmd-commonjs start*/
6560
7381
  /*兼容 AMD、CMD、CommonJS 规范 - start*/
6561
7382
  /**
6562
7383
  * 兼容 AMD、CMD、CommonJS 规范
@@ -6573,4 +7394,5 @@ console.log('------ translate.js ------\nTwo lines of js html automatic translat
6573
7394
  })(this, function () {
6574
7395
  return translate;
6575
7396
  });
6576
- /*兼容 AMD、CMD、CommonJS 规范 - end*/
7397
+ /*兼容 AMD、CMD、CommonJS 规范 - end*/
7398
+ /*js amd-cmd-commonjs end*/