let modoCaptura = false; let overlay = null; let selecao = null; let inicioX, inicioY; let traduzindo = false; const shadowRoots = new Map(); // Intercepta attachShadow para capturar shadow roots fechados 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); // 1. Título da página await traduzirTitulo(); // 2. Documento principal await traduzirDocumento(document); // 3. Shadow DOMs (abertos e capturados) await traduzirTodosShadowDOM(); // 4. Web Components customizados 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) { // Traduz QUALQUER elemento que tenha texto chinês direto const todos = doc.querySelectorAll('*'); for (const el of todos) { // Ignora scripts e styles 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() { // Shadow DOMs abertos 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); } // Shadow DOMs capturados (fechados) 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; } } // Também traduz atributos dentro do shadow 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)); } } } // Verifica shadow aninhados const todosNoShadow = shadow.querySelectorAll('*'); for (const el of todosNoShadow) { if (el.shadowRoot) { await traduzirShadowDOM(el.shadowRoot); } } } async function traduzirWebComponents() { // Componentes customizados conhecidos do Alibaba/1688 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); // Tenta acessar shadow const shadow = el.shadowRoot || shadowRoots.get(el); if (shadow) { await traduzirShadowDOM(shadow); } // Traduz innerHTML diretamente se não tiver 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); } } // Auto-executa ao carregar console.log("Tradutor CN carregado! Frame:", window.location.href); // Se estiver em um iframe, traduz automaticamente if (window !== window.top) { console.log("Executando em iframe, traduzindo..."); setTimeout(() => traduzirPaginaCompleta(), 500); }