extensao_tradutor / content.js
meccatronis's picture
Upload content.js with huggingface_hub
c9b0db0 verified
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);
}