meccatronis commited on
Commit
c9b0db0
·
verified ·
1 Parent(s): 07ac561

Upload content.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. content.js +480 -0
content.js ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let modoCaptura = false;
2
+ let overlay = null;
3
+ let selecao = null;
4
+ let inicioX, inicioY;
5
+ let traduzindo = false;
6
+ const shadowRoots = new Map();
7
+
8
+ // Intercepta attachShadow para capturar shadow roots fechados
9
+ const originalAttachShadow = Element.prototype.attachShadow;
10
+ Element.prototype.attachShadow = function(options) {
11
+ const shadow = originalAttachShadow.call(this, options);
12
+ shadowRoots.set(this, shadow);
13
+ console.log("Shadow DOM capturado em:", this.tagName);
14
+ return shadow;
15
+ };
16
+
17
+ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
18
+ console.log("Mensagem recebida:", request.action);
19
+ if (request.action === "capturar-area") {
20
+ iniciarCaptura();
21
+ }
22
+ if (request.action === "traduzir-pagina") {
23
+ traduzirPaginaCompleta();
24
+ }
25
+ if (request.action === "modo-automatico") {
26
+ ativarModoAutomatico();
27
+ }
28
+ });
29
+
30
+ function iniciarCaptura() {
31
+ modoCaptura = true;
32
+ overlay = document.createElement('div');
33
+ overlay.id = 'tradutor-overlay';
34
+ document.body.appendChild(overlay);
35
+
36
+ selecao = document.createElement('div');
37
+ selecao.id = 'tradutor-selecao';
38
+ document.body.appendChild(selecao);
39
+
40
+ document.addEventListener('mousedown', inicioSelecao);
41
+ document.addEventListener('mousemove', duranteSelecao);
42
+ document.addEventListener('mouseup', fimSelecao);
43
+ }
44
+
45
+ function inicioSelecao(e) {
46
+ if (!modoCaptura) return;
47
+ inicioX = e.clientX;
48
+ inicioY = e.clientY;
49
+ selecao.style.left = inicioX + 'px';
50
+ selecao.style.top = inicioY + 'px';
51
+ selecao.style.display = 'block';
52
+ }
53
+
54
+ function duranteSelecao(e) {
55
+ if (!modoCaptura || !inicioX) return;
56
+ const largura = e.clientX - inicioX;
57
+ const altura = e.clientY - inicioY;
58
+ selecao.style.width = Math.abs(largura) + 'px';
59
+ selecao.style.height = Math.abs(altura) + 'px';
60
+ selecao.style.left = (largura < 0 ? e.clientX : inicioX) + 'px';
61
+ selecao.style.top = (altura < 0 ? e.clientY : inicioY) + 'px';
62
+ }
63
+
64
+ async function fimSelecao(e) {
65
+ if (!modoCaptura) return;
66
+ modoCaptura = false;
67
+
68
+ const rect = {
69
+ x: parseInt(selecao.style.left),
70
+ y: parseInt(selecao.style.top),
71
+ width: parseInt(selecao.style.width),
72
+ height: parseInt(selecao.style.height)
73
+ };
74
+
75
+ document.removeEventListener('mousedown', inicioSelecao);
76
+ document.removeEventListener('mousemove', duranteSelecao);
77
+ document.removeEventListener('mouseup', fimSelecao);
78
+
79
+ overlay.remove();
80
+ selecao.remove();
81
+ }
82
+
83
+ function temChines(texto) {
84
+ if (!texto || typeof texto !== 'string') return false;
85
+ return /[\u4e00-\u9fa5]/.test(texto);
86
+ }
87
+
88
+ async function traduzirTexto(texto) {
89
+ if (!texto || !temChines(texto)) return texto;
90
+ return new Promise((resolve) => {
91
+ chrome.runtime.sendMessage({ action: "traduzir-texto", texto: texto }, (traduzido) => {
92
+ console.log("Traduzido:", texto.substring(0,30), "->", traduzido ? traduzido.substring(0,30) : "null");
93
+ resolve(traduzido || texto);
94
+ });
95
+ });
96
+ }
97
+
98
+ async function traduzirPaginaCompleta() {
99
+ if (traduzindo) return;
100
+ traduzindo = true;
101
+ console.log("=== INICIANDO TRADUÇÃO COMPLETA ===");
102
+ console.log("URL:", window.location.href);
103
+
104
+ // 1. Título da página
105
+ await traduzirTitulo();
106
+
107
+ // 2. Documento principal
108
+ await traduzirDocumento(document);
109
+
110
+ // 3. Shadow DOMs (abertos e capturados)
111
+ await traduzirTodosShadowDOM();
112
+
113
+ // 4. Web Components customizados
114
+ await traduzirWebComponents();
115
+
116
+ console.log("=== TRADUÇÃO COMPLETA FINALIZADA ===");
117
+ traduzindo = false;
118
+ }
119
+
120
+ async function traduzirTitulo() {
121
+ if (temChines(document.title)) {
122
+ console.log("Traduzindo título da página...");
123
+ document.title = await traduzirTexto(document.title);
124
+ }
125
+ }
126
+
127
+ async function traduzirDocumento(doc) {
128
+ console.log("Traduzindo documento...");
129
+
130
+ await traduzirTextosNormais(doc);
131
+ await traduzirFormularios(doc);
132
+ await traduzirSelects(doc);
133
+ await traduzirAtributos(doc);
134
+ await traduzirSVG(doc);
135
+ await traduzirLabels(doc);
136
+ await traduzirTabelas(doc);
137
+ await traduzirLinks(doc);
138
+ await traduzirListas(doc);
139
+ await traduzirSpansDivs(doc);
140
+ await traduzirTodosElementos(doc);
141
+ }
142
+
143
+ async function traduzirTextosNormais(doc) {
144
+ const body = doc.body || doc;
145
+ const walker = document.createTreeWalker(
146
+ body,
147
+ NodeFilter.SHOW_TEXT,
148
+ {
149
+ acceptNode: function(node) {
150
+ const tag = node.parentNode.tagName;
151
+ if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT') {
152
+ return NodeFilter.FILTER_REJECT;
153
+ }
154
+ return NodeFilter.FILTER_ACCEPT;
155
+ }
156
+ },
157
+ false
158
+ );
159
+
160
+ const nodos = [];
161
+ let node;
162
+ while (node = walker.nextNode()) {
163
+ const texto = node.textContent.trim();
164
+ if (texto && temChines(texto)) {
165
+ nodos.push(node);
166
+ }
167
+ }
168
+
169
+ console.log("Textos normais encontrados: " + nodos.length);
170
+
171
+ for (const nodo of nodos) {
172
+ const texto = nodo.textContent;
173
+ const traduzido = await traduzirTexto(texto);
174
+ if (traduzido) {
175
+ nodo.textContent = traduzido;
176
+ }
177
+ }
178
+ }
179
+
180
+ async function traduzirFormularios(doc) {
181
+ const placeholders = doc.querySelectorAll('[placeholder]');
182
+ for (const el of placeholders) {
183
+ const val = el.getAttribute('placeholder');
184
+ if (temChines(val)) {
185
+ el.setAttribute('placeholder', await traduzirTexto(val));
186
+ }
187
+ }
188
+
189
+ const buttons = doc.querySelectorAll('input[type="submit"], input[type="button"], button');
190
+ for (const btn of buttons) {
191
+ if (btn.value && temChines(btn.value)) {
192
+ btn.value = await traduzirTexto(btn.value);
193
+ }
194
+ if (btn.textContent && temChines(btn.textContent)) {
195
+ btn.textContent = await traduzirTexto(btn.textContent);
196
+ }
197
+ }
198
+
199
+ console.log("Formulários traduzidos");
200
+ }
201
+
202
+ async function traduzirSelects(doc) {
203
+ const selects = doc.querySelectorAll('select');
204
+ for (const select of selects) {
205
+ const options = select.querySelectorAll('option');
206
+ for (const opt of options) {
207
+ if (temChines(opt.textContent)) {
208
+ opt.textContent = await traduzirTexto(opt.textContent);
209
+ }
210
+ }
211
+ }
212
+ console.log("Selects traduzidos");
213
+ }
214
+
215
+ async function traduzirAtributos(doc) {
216
+ const atributos = ['title', 'alt', 'aria-label', 'aria-placeholder', 'data-title', 'data-text', 'data-content', 'data-tooltip'];
217
+
218
+ for (const attr of atributos) {
219
+ const elementos = doc.querySelectorAll('[' + attr + ']');
220
+ for (const el of elementos) {
221
+ const val = el.getAttribute(attr);
222
+ if (temChines(val)) {
223
+ el.setAttribute(attr, await traduzirTexto(val));
224
+ }
225
+ }
226
+ }
227
+ console.log("Atributos traduzidos");
228
+ }
229
+
230
+ async function traduzirSVG(doc) {
231
+ const textosSVG = doc.querySelectorAll('svg text, svg tspan');
232
+ for (const el of textosSVG) {
233
+ if (temChines(el.textContent)) {
234
+ el.textContent = await traduzirTexto(el.textContent);
235
+ }
236
+ }
237
+ console.log("SVG traduzido");
238
+ }
239
+
240
+ async function traduzirLabels(doc) {
241
+ const labels = doc.querySelectorAll('label');
242
+ for (const label of labels) {
243
+ if (temChines(label.textContent)) {
244
+ const filhos = label.childNodes;
245
+ for (const filho of filhos) {
246
+ if (filho.nodeType === 3 && temChines(filho.textContent)) {
247
+ filho.textContent = await traduzirTexto(filho.textContent);
248
+ }
249
+ }
250
+ }
251
+ }
252
+ console.log("Labels traduzidos");
253
+ }
254
+
255
+ async function traduzirTabelas(doc) {
256
+ const celulas = doc.querySelectorAll('th, td');
257
+ for (const cel of celulas) {
258
+ if (temChines(cel.textContent)) {
259
+ const filhos = cel.childNodes;
260
+ for (const filho of filhos) {
261
+ if (filho.nodeType === 3 && temChines(filho.textContent)) {
262
+ filho.textContent = await traduzirTexto(filho.textContent);
263
+ }
264
+ }
265
+ }
266
+ }
267
+ console.log("Tabelas traduzidas");
268
+ }
269
+
270
+ async function traduzirLinks(doc) {
271
+ const links = doc.querySelectorAll('a');
272
+ for (const link of links) {
273
+ if (temChines(link.textContent)) {
274
+ const filhos = link.childNodes;
275
+ for (const filho of filhos) {
276
+ if (filho.nodeType === 3 && temChines(filho.textContent)) {
277
+ filho.textContent = await traduzirTexto(filho.textContent);
278
+ }
279
+ }
280
+ }
281
+ }
282
+ console.log("Links traduzidos");
283
+ }
284
+
285
+ async function traduzirListas(doc) {
286
+ const itens = doc.querySelectorAll('li');
287
+ for (const item of itens) {
288
+ if (temChines(item.textContent)) {
289
+ const filhos = item.childNodes;
290
+ for (const filho of filhos) {
291
+ if (filho.nodeType === 3 && temChines(filho.textContent)) {
292
+ filho.textContent = await traduzirTexto(filho.textContent);
293
+ }
294
+ }
295
+ }
296
+ }
297
+ console.log("Listas traduzidas");
298
+ }
299
+
300
+ async function traduzirSpansDivs(doc) {
301
+ const elementos = doc.querySelectorAll('span, div, p, h1, h2, h3, h4, h5, h6, strong, em, b, i, small');
302
+ for (const el of elementos) {
303
+ const filhos = el.childNodes;
304
+ for (const filho of filhos) {
305
+ if (filho.nodeType === 3 && temChines(filho.textContent)) {
306
+ filho.textContent = await traduzirTexto(filho.textContent);
307
+ }
308
+ }
309
+ }
310
+ console.log("Spans/Divs traduzidos");
311
+ }
312
+
313
+ async function traduzirTodosElementos(doc) {
314
+ // Traduz QUALQUER elemento que tenha texto chinês direto
315
+ const todos = doc.querySelectorAll('*');
316
+ for (const el of todos) {
317
+ // Ignora scripts e styles
318
+ if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE' || el.tagName === 'NOSCRIPT') continue;
319
+
320
+ for (const filho of el.childNodes) {
321
+ if (filho.nodeType === 3 && temChines(filho.textContent)) {
322
+ filho.textContent = await traduzirTexto(filho.textContent);
323
+ }
324
+ }
325
+ }
326
+ console.log("Todos elementos verificados");
327
+ }
328
+
329
+ async function traduzirTodosShadowDOM() {
330
+ // Shadow DOMs abertos
331
+ const todos = document.querySelectorAll('*');
332
+ for (const el of todos) {
333
+ if (el.shadowRoot) {
334
+ console.log("Shadow DOM aberto encontrado em: " + el.tagName);
335
+ await traduzirShadowDOM(el.shadowRoot);
336
+ }
337
+ // Shadow DOMs capturados (fechados)
338
+ if (shadowRoots.has(el)) {
339
+ console.log("Shadow DOM capturado encontrado em: " + el.tagName);
340
+ await traduzirShadowDOM(shadowRoots.get(el));
341
+ }
342
+ }
343
+ }
344
+
345
+ async function traduzirShadowDOM(shadow) {
346
+ if (!shadow) return;
347
+
348
+ const walker = document.createTreeWalker(
349
+ shadow,
350
+ NodeFilter.SHOW_TEXT,
351
+ null,
352
+ false
353
+ );
354
+
355
+ const nodos = [];
356
+ let node;
357
+ while (node = walker.nextNode()) {
358
+ if (temChines(node.textContent)) {
359
+ nodos.push(node);
360
+ }
361
+ }
362
+
363
+ console.log("Textos em Shadow DOM: " + nodos.length);
364
+
365
+ for (const nodo of nodos) {
366
+ const traduzido = await traduzirTexto(nodo.textContent);
367
+ if (traduzido) {
368
+ nodo.textContent = traduzido;
369
+ }
370
+ }
371
+
372
+ // Também traduz atributos dentro do shadow
373
+ const elementos = shadow.querySelectorAll('[placeholder], [title], [alt], [aria-label]');
374
+ for (const el of elementos) {
375
+ for (const attr of ['placeholder', 'title', 'alt', 'aria-label']) {
376
+ const val = el.getAttribute(attr);
377
+ if (val && temChines(val)) {
378
+ el.setAttribute(attr, await traduzirTexto(val));
379
+ }
380
+ }
381
+ }
382
+
383
+ // Verifica shadow aninhados
384
+ const todosNoShadow = shadow.querySelectorAll('*');
385
+ for (const el of todosNoShadow) {
386
+ if (el.shadowRoot) {
387
+ await traduzirShadowDOM(el.shadowRoot);
388
+ }
389
+ }
390
+ }
391
+
392
+ async function traduzirWebComponents() {
393
+ // Componentes customizados conhecidos do Alibaba/1688
394
+ const componentes = ['ali-bar', 'ali-footer', 'ali-header', 'ali-nav', 'ali-menu', 'aliww-container'];
395
+
396
+ for (const nome of componentes) {
397
+ const elementos = document.querySelectorAll(nome);
398
+ for (const el of elementos) {
399
+ console.log("Web Component encontrado: " + nome);
400
+
401
+ // Tenta acessar shadow
402
+ const shadow = el.shadowRoot || shadowRoots.get(el);
403
+ if (shadow) {
404
+ await traduzirShadowDOM(shadow);
405
+ }
406
+
407
+ // Traduz innerHTML diretamente se não tiver shadow
408
+ if (!shadow && el.innerHTML && temChines(el.innerHTML)) {
409
+ const filhos = el.childNodes;
410
+ for (const filho of filhos) {
411
+ if (filho.nodeType === 3 && temChines(filho.textContent)) {
412
+ filho.textContent = await traduzirTexto(filho.textContent);
413
+ }
414
+ }
415
+ }
416
+ }
417
+ }
418
+ console.log("Web Components verificados");
419
+ }
420
+
421
+ function ativarModoAutomatico() {
422
+ console.log("Modo automático ativado");
423
+ traduzirPaginaCompleta();
424
+
425
+ const observer = new MutationObserver(async (mutations) => {
426
+ for (const mutation of mutations) {
427
+ if (mutation.type === 'childList') {
428
+ for (const node of mutation.addedNodes) {
429
+ if (node.nodeType === 1) {
430
+ await traduzirElementoNovo(node);
431
+ } else if (node.nodeType === 3 && temChines(node.textContent)) {
432
+ node.textContent = await traduzirTexto(node.textContent);
433
+ }
434
+ }
435
+ }
436
+ }
437
+ });
438
+
439
+ observer.observe(document.body, {
440
+ childList: true,
441
+ subtree: true,
442
+ characterData: true
443
+ });
444
+ }
445
+
446
+ async function traduzirElementoNovo(elemento) {
447
+ const walker = document.createTreeWalker(
448
+ elemento,
449
+ NodeFilter.SHOW_TEXT,
450
+ null,
451
+ false
452
+ );
453
+
454
+ let node;
455
+ while (node = walker.nextNode()) {
456
+ if (temChines(node.textContent)) {
457
+ node.textContent = await traduzirTexto(node.textContent);
458
+ }
459
+ }
460
+
461
+ const atributos = ['placeholder', 'title', 'alt', 'aria-label', 'value'];
462
+ for (const attr of atributos) {
463
+ if (elemento.getAttribute && elemento.getAttribute(attr) && temChines(elemento.getAttribute(attr))) {
464
+ elemento.setAttribute(attr, await traduzirTexto(elemento.getAttribute(attr)));
465
+ }
466
+ }
467
+
468
+ if (elemento.shadowRoot) {
469
+ await traduzirShadowDOM(elemento.shadowRoot);
470
+ }
471
+ }
472
+
473
+ // Auto-executa ao carregar
474
+ console.log("Tradutor CN carregado! Frame:", window.location.href);
475
+
476
+ // Se estiver em um iframe, traduz automaticamente
477
+ if (window !== window.top) {
478
+ console.log("Executando em iframe, traduzindo...");
479
+ setTimeout(() => traduzirPaginaCompleta(), 500);
480
+ }