| let modoCaptura = false; |
| let overlay = null; |
| let selecao = null; |
| let inicioX, inicioY; |
| let traduzindo = false; |
| const shadowRoots = new Map(); |
|
|
| |
| const originalAttachShadow = Element.prototype.attachShadow; |
| Element.prototype.attachShadow = function(options) { |
| const shadow = originalAttachShadow.call(this, options); |
| shadowRoots.set(this, shadow); |
| console.log("Shadow DOM capturado em:", this.tagName); |
| return shadow; |
| }; |
|
|
| chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { |
| console.log("Mensagem recebida:", request.action); |
| if (request.action === "capturar-area") { |
| iniciarCaptura(); |
| } |
| if (request.action === "traduzir-pagina") { |
| traduzirPaginaCompleta(); |
| } |
| if (request.action === "modo-automatico") { |
| ativarModoAutomatico(); |
| } |
| }); |
|
|
| function iniciarCaptura() { |
| modoCaptura = true; |
| overlay = document.createElement('div'); |
| overlay.id = 'tradutor-overlay'; |
| document.body.appendChild(overlay); |
| |
| selecao = document.createElement('div'); |
| selecao.id = 'tradutor-selecao'; |
| document.body.appendChild(selecao); |
| |
| document.addEventListener('mousedown', inicioSelecao); |
| document.addEventListener('mousemove', duranteSelecao); |
| document.addEventListener('mouseup', fimSelecao); |
| } |
|
|
| function inicioSelecao(e) { |
| if (!modoCaptura) return; |
| inicioX = e.clientX; |
| inicioY = e.clientY; |
| selecao.style.left = inicioX + 'px'; |
| selecao.style.top = inicioY + 'px'; |
| selecao.style.display = 'block'; |
| } |
|
|
| function duranteSelecao(e) { |
| if (!modoCaptura || !inicioX) return; |
| const largura = e.clientX - inicioX; |
| const altura = e.clientY - inicioY; |
| selecao.style.width = Math.abs(largura) + 'px'; |
| selecao.style.height = Math.abs(altura) + 'px'; |
| selecao.style.left = (largura < 0 ? e.clientX : inicioX) + 'px'; |
| selecao.style.top = (altura < 0 ? e.clientY : inicioY) + 'px'; |
| } |
|
|
| async function fimSelecao(e) { |
| if (!modoCaptura) return; |
| modoCaptura = false; |
| |
| const rect = { |
| x: parseInt(selecao.style.left), |
| y: parseInt(selecao.style.top), |
| width: parseInt(selecao.style.width), |
| height: parseInt(selecao.style.height) |
| }; |
| |
| document.removeEventListener('mousedown', inicioSelecao); |
| document.removeEventListener('mousemove', duranteSelecao); |
| document.removeEventListener('mouseup', fimSelecao); |
| |
| overlay.remove(); |
| selecao.remove(); |
| } |
|
|
| function temChines(texto) { |
| if (!texto || typeof texto !== 'string') return false; |
| return /[\u4e00-\u9fa5]/.test(texto); |
| } |
|
|
| async function traduzirTexto(texto) { |
| if (!texto || !temChines(texto)) return texto; |
| return new Promise((resolve) => { |
| chrome.runtime.sendMessage({ action: "traduzir-texto", texto: texto }, (traduzido) => { |
| console.log("Traduzido:", texto.substring(0,30), "->", traduzido ? traduzido.substring(0,30) : "null"); |
| resolve(traduzido || texto); |
| }); |
| }); |
| } |
|
|
| async function traduzirPaginaCompleta() { |
| if (traduzindo) return; |
| traduzindo = true; |
| console.log("=== INICIANDO TRADUÇÃO COMPLETA ==="); |
| console.log("URL:", window.location.href); |
| |
| |
| await traduzirTitulo(); |
| |
| |
| await traduzirDocumento(document); |
| |
| |
| await traduzirTodosShadowDOM(); |
| |
| |
| await traduzirWebComponents(); |
| |
| console.log("=== TRADUÇÃO COMPLETA FINALIZADA ==="); |
| traduzindo = false; |
| } |
|
|
| async function traduzirTitulo() { |
| if (temChines(document.title)) { |
| console.log("Traduzindo título da página..."); |
| document.title = await traduzirTexto(document.title); |
| } |
| } |
|
|
| async function traduzirDocumento(doc) { |
| console.log("Traduzindo documento..."); |
| |
| await traduzirTextosNormais(doc); |
| await traduzirFormularios(doc); |
| await traduzirSelects(doc); |
| await traduzirAtributos(doc); |
| await traduzirSVG(doc); |
| await traduzirLabels(doc); |
| await traduzirTabelas(doc); |
| await traduzirLinks(doc); |
| await traduzirListas(doc); |
| await traduzirSpansDivs(doc); |
| await traduzirTodosElementos(doc); |
| } |
|
|
| async function traduzirTextosNormais(doc) { |
| const body = doc.body || doc; |
| const walker = document.createTreeWalker( |
| body, |
| NodeFilter.SHOW_TEXT, |
| { |
| acceptNode: function(node) { |
| const tag = node.parentNode.tagName; |
| if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT') { |
| return NodeFilter.FILTER_REJECT; |
| } |
| return NodeFilter.FILTER_ACCEPT; |
| } |
| }, |
| false |
| ); |
| |
| const nodos = []; |
| let node; |
| while (node = walker.nextNode()) { |
| const texto = node.textContent.trim(); |
| if (texto && temChines(texto)) { |
| nodos.push(node); |
| } |
| } |
| |
| console.log("Textos normais encontrados: " + nodos.length); |
| |
| for (const nodo of nodos) { |
| const texto = nodo.textContent; |
| const traduzido = await traduzirTexto(texto); |
| if (traduzido) { |
| nodo.textContent = traduzido; |
| } |
| } |
| } |
|
|
| async function traduzirFormularios(doc) { |
| const placeholders = doc.querySelectorAll('[placeholder]'); |
| for (const el of placeholders) { |
| const val = el.getAttribute('placeholder'); |
| if (temChines(val)) { |
| el.setAttribute('placeholder', await traduzirTexto(val)); |
| } |
| } |
| |
| const buttons = doc.querySelectorAll('input[type="submit"], input[type="button"], button'); |
| for (const btn of buttons) { |
| if (btn.value && temChines(btn.value)) { |
| btn.value = await traduzirTexto(btn.value); |
| } |
| if (btn.textContent && temChines(btn.textContent)) { |
| btn.textContent = await traduzirTexto(btn.textContent); |
| } |
| } |
| |
| console.log("Formulários traduzidos"); |
| } |
|
|
| async function traduzirSelects(doc) { |
| const selects = doc.querySelectorAll('select'); |
| for (const select of selects) { |
| const options = select.querySelectorAll('option'); |
| for (const opt of options) { |
| if (temChines(opt.textContent)) { |
| opt.textContent = await traduzirTexto(opt.textContent); |
| } |
| } |
| } |
| console.log("Selects traduzidos"); |
| } |
|
|
| async function traduzirAtributos(doc) { |
| const atributos = ['title', 'alt', 'aria-label', 'aria-placeholder', 'data-title', 'data-text', 'data-content', 'data-tooltip']; |
| |
| for (const attr of atributos) { |
| const elementos = doc.querySelectorAll('[' + attr + ']'); |
| for (const el of elementos) { |
| const val = el.getAttribute(attr); |
| if (temChines(val)) { |
| el.setAttribute(attr, await traduzirTexto(val)); |
| } |
| } |
| } |
| console.log("Atributos traduzidos"); |
| } |
|
|
| async function traduzirSVG(doc) { |
| const textosSVG = doc.querySelectorAll('svg text, svg tspan'); |
| for (const el of textosSVG) { |
| if (temChines(el.textContent)) { |
| el.textContent = await traduzirTexto(el.textContent); |
| } |
| } |
| console.log("SVG traduzido"); |
| } |
|
|
| async function traduzirLabels(doc) { |
| const labels = doc.querySelectorAll('label'); |
| for (const label of labels) { |
| if (temChines(label.textContent)) { |
| const filhos = label.childNodes; |
| for (const filho of filhos) { |
| if (filho.nodeType === 3 && temChines(filho.textContent)) { |
| filho.textContent = await traduzirTexto(filho.textContent); |
| } |
| } |
| } |
| } |
| console.log("Labels traduzidos"); |
| } |
|
|
| async function traduzirTabelas(doc) { |
| const celulas = doc.querySelectorAll('th, td'); |
| for (const cel of celulas) { |
| if (temChines(cel.textContent)) { |
| const filhos = cel.childNodes; |
| for (const filho of filhos) { |
| if (filho.nodeType === 3 && temChines(filho.textContent)) { |
| filho.textContent = await traduzirTexto(filho.textContent); |
| } |
| } |
| } |
| } |
| console.log("Tabelas traduzidas"); |
| } |
|
|
| async function traduzirLinks(doc) { |
| const links = doc.querySelectorAll('a'); |
| for (const link of links) { |
| if (temChines(link.textContent)) { |
| const filhos = link.childNodes; |
| for (const filho of filhos) { |
| if (filho.nodeType === 3 && temChines(filho.textContent)) { |
| filho.textContent = await traduzirTexto(filho.textContent); |
| } |
| } |
| } |
| } |
| console.log("Links traduzidos"); |
| } |
|
|
| async function traduzirListas(doc) { |
| const itens = doc.querySelectorAll('li'); |
| for (const item of itens) { |
| if (temChines(item.textContent)) { |
| const filhos = item.childNodes; |
| for (const filho of filhos) { |
| if (filho.nodeType === 3 && temChines(filho.textContent)) { |
| filho.textContent = await traduzirTexto(filho.textContent); |
| } |
| } |
| } |
| } |
| console.log("Listas traduzidas"); |
| } |
|
|
| async function traduzirSpansDivs(doc) { |
| const elementos = doc.querySelectorAll('span, div, p, h1, h2, h3, h4, h5, h6, strong, em, b, i, small'); |
| for (const el of elementos) { |
| const filhos = el.childNodes; |
| for (const filho of filhos) { |
| if (filho.nodeType === 3 && temChines(filho.textContent)) { |
| filho.textContent = await traduzirTexto(filho.textContent); |
| } |
| } |
| } |
| console.log("Spans/Divs traduzidos"); |
| } |
|
|
| async function traduzirTodosElementos(doc) { |
| |
| const todos = doc.querySelectorAll('*'); |
| for (const el of todos) { |
| |
| if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE' || el.tagName === 'NOSCRIPT') continue; |
| |
| for (const filho of el.childNodes) { |
| if (filho.nodeType === 3 && temChines(filho.textContent)) { |
| filho.textContent = await traduzirTexto(filho.textContent); |
| } |
| } |
| } |
| console.log("Todos elementos verificados"); |
| } |
|
|
| async function traduzirTodosShadowDOM() { |
| |
| const todos = document.querySelectorAll('*'); |
| for (const el of todos) { |
| if (el.shadowRoot) { |
| console.log("Shadow DOM aberto encontrado em: " + el.tagName); |
| await traduzirShadowDOM(el.shadowRoot); |
| } |
| |
| if (shadowRoots.has(el)) { |
| console.log("Shadow DOM capturado encontrado em: " + el.tagName); |
| await traduzirShadowDOM(shadowRoots.get(el)); |
| } |
| } |
| } |
|
|
| async function traduzirShadowDOM(shadow) { |
| if (!shadow) return; |
| |
| const walker = document.createTreeWalker( |
| shadow, |
| NodeFilter.SHOW_TEXT, |
| null, |
| false |
| ); |
| |
| const nodos = []; |
| let node; |
| while (node = walker.nextNode()) { |
| if (temChines(node.textContent)) { |
| nodos.push(node); |
| } |
| } |
| |
| console.log("Textos em Shadow DOM: " + nodos.length); |
| |
| for (const nodo of nodos) { |
| const traduzido = await traduzirTexto(nodo.textContent); |
| if (traduzido) { |
| nodo.textContent = traduzido; |
| } |
| } |
| |
| |
| const elementos = shadow.querySelectorAll('[placeholder], [title], [alt], [aria-label]'); |
| for (const el of elementos) { |
| for (const attr of ['placeholder', 'title', 'alt', 'aria-label']) { |
| const val = el.getAttribute(attr); |
| if (val && temChines(val)) { |
| el.setAttribute(attr, await traduzirTexto(val)); |
| } |
| } |
| } |
| |
| |
| const todosNoShadow = shadow.querySelectorAll('*'); |
| for (const el of todosNoShadow) { |
| if (el.shadowRoot) { |
| await traduzirShadowDOM(el.shadowRoot); |
| } |
| } |
| } |
|
|
| async function traduzirWebComponents() { |
| |
| const componentes = ['ali-bar', 'ali-footer', 'ali-header', 'ali-nav', 'ali-menu', 'aliww-container']; |
| |
| for (const nome of componentes) { |
| const elementos = document.querySelectorAll(nome); |
| for (const el of elementos) { |
| console.log("Web Component encontrado: " + nome); |
| |
| |
| const shadow = el.shadowRoot || shadowRoots.get(el); |
| if (shadow) { |
| await traduzirShadowDOM(shadow); |
| } |
| |
| |
| if (!shadow && el.innerHTML && temChines(el.innerHTML)) { |
| const filhos = el.childNodes; |
| for (const filho of filhos) { |
| if (filho.nodeType === 3 && temChines(filho.textContent)) { |
| filho.textContent = await traduzirTexto(filho.textContent); |
| } |
| } |
| } |
| } |
| } |
| console.log("Web Components verificados"); |
| } |
|
|
| function ativarModoAutomatico() { |
| console.log("Modo automático ativado"); |
| traduzirPaginaCompleta(); |
| |
| const observer = new MutationObserver(async (mutations) => { |
| for (const mutation of mutations) { |
| if (mutation.type === 'childList') { |
| for (const node of mutation.addedNodes) { |
| if (node.nodeType === 1) { |
| await traduzirElementoNovo(node); |
| } else if (node.nodeType === 3 && temChines(node.textContent)) { |
| node.textContent = await traduzirTexto(node.textContent); |
| } |
| } |
| } |
| } |
| }); |
| |
| observer.observe(document.body, { |
| childList: true, |
| subtree: true, |
| characterData: true |
| }); |
| } |
|
|
| async function traduzirElementoNovo(elemento) { |
| const walker = document.createTreeWalker( |
| elemento, |
| NodeFilter.SHOW_TEXT, |
| null, |
| false |
| ); |
| |
| let node; |
| while (node = walker.nextNode()) { |
| if (temChines(node.textContent)) { |
| node.textContent = await traduzirTexto(node.textContent); |
| } |
| } |
| |
| const atributos = ['placeholder', 'title', 'alt', 'aria-label', 'value']; |
| for (const attr of atributos) { |
| if (elemento.getAttribute && elemento.getAttribute(attr) && temChines(elemento.getAttribute(attr))) { |
| elemento.setAttribute(attr, await traduzirTexto(elemento.getAttribute(attr))); |
| } |
| } |
| |
| if (elemento.shadowRoot) { |
| await traduzirShadowDOM(elemento.shadowRoot); |
| } |
| } |
|
|
| |
| console.log("Tradutor CN carregado! Frame:", window.location.href); |
|
|
| |
| if (window !== window.top) { |
| console.log("Executando em iframe, traduzindo..."); |
| setTimeout(() => traduzirPaginaCompleta(), 500); |
| } |
|
|