Nessun oggetto della modifica
Etichetta: Annullato
Nessun oggetto della modifica
Etichetta: Annullato
Riga 1: Riga 1:
/* ======================= CommonTranslate.js (server-proxy) ======================= */
/* ======================= CommonTranslate.js (solo via proxy) ======================= */
console.log("🔄 Caricamento CommonTranslate.js…");
console.log("🔄 Caricamento CommonTranslate.js…");


/* Evita doppi caricamenti */
// Evita ricarichi multipli
if (window.CommonTranslateLoaded) {
if (window.__CommonTranslateLoaded) {
   console.warn("⚠️ CommonTranslate.js è già stato caricato.");
   console.warn("⚠️ CommonTranslate.js è già stato caricato.");
} else {
} else {
   window.CommonTranslateLoaded = true;
   window.__CommonTranslateLoaded = true;


   /* === Config di base (senza API key nel client) === */
   // URL del proxy lato server (non mettere la chiave nel browser!)
  window.linguaOrigine = document.documentElement.lang || "en";
   const PROXY_URL = "/w/proxy.php";
   const API_PROXY = "/dashboard/api/openai_project_gpt.php";  // il “cameriere”
 
   const MODEL_DEFAULT = "gpt-4o";                              // il server userà questo
   // Modello di default (puoi cambiarlo qui o da window.openaiModel)
  console.log("✅ CommonTranslate.js pronto (via proxy).");
   const DEFAULT_MODEL = (window.openaiModel || "gpt-4o");
}


/* ======================= Azione principale ======================= */
  // Lingua di origine (fallback)
async function traduciTesto() {
   const LINGUA_ORIGINE = document.documentElement.lang || "en";
   const area = document.getElementById("wpTextbox1");
  if (!area) { alert("Campo editor non trovato."); return; }


   const start = area.selectionStart;
   console.log("✅ CommonTranslate.js pronto (via proxy). Modello:", DEFAULT_MODEL);
  const end  = area.selectionEnd;
  const sel  = area.value.slice(start, end).trim();
  const tutto = !sel;


   // 1) cosa tradurre
   /* ======================= UTILITIES ======================= */
  const onlySel = confirm("📝 Vuoi tradurre SOLO il testo selezionato?\n\n✅ OK = solo selezione\n❌ Annulla = tutto il testo");
   function getEditor() {
   if (onlySel && !sel) {
     return document.getElementById("wpTextbox1");
     alert("⚠️ Nessuna selezione. Premi Annulla per tradurre tutto.");
    return;
   }
   }
   const testo = tutto ? area.value : sel;
   function getSelectedTextInfo() {
 
    const area = getEditor();
  // 2) lingua di destinazione
     if (!area) return { text: "", start: 0, end: 0 };
  const lingua = prompt(
     const start = area.selectionStart;
     "🌍 Scegli la lingua di traduzione (it, en, fr, es, de):",
    const end = area.selectionEnd;
     "it"
    const text = area.value.slice(start, end);
  );
    return { text, start, end };
  if (!lingua) { console.warn("⚠️ Lingua non indicata."); return; }
   }
 
   function segmentaTesto(testo, maxChars = 3000) {
   // 3) segmentazione e (se tutto) pre-check
    // Segmenti “morbidi” a limite caratteri su spazio
   const blocchi = segmentaTesto(testo);
    const re = new RegExp(`.{1,${maxChars}}(?=\\s|$)`, "gs");
  console.log(`🧩 Segmentazione: ${blocchi.length} blocchi`);
     return testo.match(re) || [];
 
  if (tutto) {
    const errori = analizzaErroriSegmentazione(blocchi);
    sostituisciTestoSegmentato(blocchi, errori);
     mostraPopupConferma(blocchi, errori, lingua, tutto);
    return; // la traduzione parte dal popup
   }
   }


   // 4) traduzione diretta (solo selezione)
   // Chiamata singolo blocco al proxy
  const tradotto = await inviaTraduzione(blocchi, lingua, tutto);
  async function traduciBloccoProxy(blocco, lingua, model = DEFAULT_MODEL) {
    try {
      const res = await fetch(PROXY_URL, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          model,
          messages: [
            {
              role: "system",
              content:
                "You are a professional MediaWiki translator. Translate preserving templates, links, headings, lists and any wiki syntax. Do not add explanations."
            },
            {
              role: "user",
              content: `Translate the following text from ${LINGUA_ORIGINE} to ${lingua}:\n\n${blocco}`
            }
          ],
          temperature: 0
        })
      });


  // 5) inserimento nel campo editor
      const data = await res.json();
  if (tutto) {
      if (!res.ok) {
    area.value = tradotto;
        const msg = data?.error || `HTTP ${res.status}`;
  } else {
        throw new Error(msg);
    const prima = area.value.slice(0, start);
      }
     const dopo  = area.value.slice(end);
      const content = data?.choices?.[0]?.message?.content ?? "";
    area.value  = prima + tradotto + dopo;
      return content || "[Traduzione vuota]";
    area.selectionStart = start;
     } catch (err) {
     area.selectionEnd  = start + tradotto.length;
      console.error("❌ Errore da proxy:", err);
      return "[Errore Traduzione via proxy]";
     }
   }
   }


   // 6) notifica a MW
   async function inviaTraduzione(blocchi, lingua, sostituisciTutto) {
  area.dispatchEvent(new Event("input",  { bubbles: true }));
    const area = getEditor();
  area.dispatchEvent(new Event("change", { bubbles: true }));
    if (!area) {
      alert("❌ Campo editor non trovato (wpTextbox1).");
      return;
    }


  console.log("✅ Traduzione completata e inserita.");
    let risultato = "";
}
    for (let i = 0; i < blocchi.length; i++) {
      console.log(`🚀 Invio blocco ${i + 1}/${blocchi.length}…`);
      const tradotto = await traduciBloccoProxy(blocchi[i], lingua);
      risultato += tradotto.trim() + "\n\n";
    }
    risultato = risultato.trim();


/* ======================= Utility ======================= */
    if (sostituisciTutto) {
function segmentaTesto(testo, maxChars = 6000) {
      area.value = risultato;
  // segmentazione semplice per blocchi “morbidi” su spazi
    } else {
  const re = new RegExp(`.{1,${maxChars}}(?=\\s|$)`, "gs");
      const { start, end } = getSelectedTextInfo();
  return testo.match(re) || [testo];
      const prima = area.value.slice(0, start);
}
      const dopo = area.value.slice(end);
      area.value = prima + risultato + dopo;
      area.selectionStart = start;
      area.selectionEnd = start + risultato.length;
    }


function analizzaErroriSegmentazione(blocchi) {
    // Notifica a MediaWiki
  const errors = [];
    area.dispatchEvent(new Event("input", { bubbles: true }));
  const count = (s, ch) => (s.match(new RegExp(`\\${ch}`, "g")) || []).length;
     area.dispatchEvent(new Event("change", { bubbles: true }));
  const braces = [["[", "]"], ["{", "}"], ["<", ">"]];
  blocchi.forEach((b, i) => {
    const e = [];
     braces.forEach(([o, c]) => {
      const co = count(b, o), cc = count(b, c);
      if (co !== cc) e.push(`Parentesi ${o}${c} non bilanciate: ${co} ${o}, ${cc} ${c}`);
    });
    if (e.length) errors.push({ index: i + 1, dettagli: e });
  });
  return errors;
}


function sostituisciTestoSegmentato(blocchi, errori) {
     console.log("✅ Traduzione completata e inserita.");
  const area = document.getElementById("wpTextbox1");
  }
  if (!area) return;
  const text = blocchi.map((b, i) => {
     const err = errori.find(x => x.index === i + 1);
    const head = err ? `⚠️ ERRORI:\n- ${err.dettagli.join("\n- ")}\n\n` : "";
    return `${head}🔹 BLOCCO ${i + 1}\n` + b;
  }).join("\n\n==============================\n\n");
  area.value = text;
  console.log("✍️ Testo segmentato inserito nel campo.");
}


function mostraPopupConferma(blocchi, errori, lingua, tutto) {
  /* ======================= FLOW PRINCIPALE ======================= */
  const popup = document.createElement("div");
  async function traduciTesto() {
  popup.id = "popup-conferma-traduzione";
    const area = getEditor();
  popup.style = `
     if (!area) {
     position: fixed; bottom: 20px; right: 20px;
      alert("❌ Editor non trovato.");
    background: #fff; padding: 15px; border: 2px solid #444;
      return;
    box-shadow: 4px 4px 10px rgba(0,0,0,.3); z-index: 9999;
     }
  `;
  popup.innerHTML = `
    <b>✅ Segmentazione completata</b><br>
     Blocchi: ${blocchi.length}<br>
    ${errori.length ? `<span style="color:#c00;">❗ ${errori.length} blocchi con parentesi non bilanciate</span><br>` : `✅ Nessun errore strutturale<br>`}
    <button id="btn-invia-traduzione">✅ Invia traduzione</button>
    <button id="btn-close-popup">❌ Annulla</button>
  `;
  document.body.appendChild(popup);


  document.getElementById("btn-close-popup").onclick = () => popup.remove();
    // Solo selezione o tutto?
  document.getElementById("btn-invia-traduzione").onclick = async () => {
    const { text: selezione } = getSelectedTextInfo();
     popup.remove();
    const conferma = confirm(
     try { await inviaTraduzione(blocchi, lingua, tutto); }
      "📝 Vuoi tradurre SOLO il testo selezionato?\n\n✅ OK = solo selezione\n❌ Annulla = tutto il testo"
     catch (e) {
     );
       console.error("❌ Errore invio traduzione:", e);
     if (conferma && !selezione) {
       alert("Errore durante l’invio:\n" + (e?.message || e));
      alert("⚠️ Nessuna selezione. Premi Annulla per tradurre tutto il testo.");
      return;
    }
     const testo = conferma ? selezione : area.value;
    if (!testo.trim()) {
       alert("⚠️ Niente da tradurre.");
       return;
     }
     }
  };
}


/* ======================= Traduzione via “cameriere” ======================= */
    // Lingua destinazione
async function inviaTraduzione(blocchi, lingua, tutto = true) {
    const lingua = prompt(
  const area = document.getElementById("wpTextbox1");
      "🌍 Scegli la lingua di traduzione (es: it, en, fr, es, de):",
  if (!area) return "";
      "it"
    );
    if (!lingua) return;


  let risultato = "";
    // Segmentazione e invio
  console.log("🧪 inviaTraduzione() → blocchi:", blocchi.length, "lingua:", lingua);
    const blocchi = segmentaTesto(testo);
 
    console.log("🧩 Segmentazione:", blocchi.length, "blocchi");
  for (let i = 0; i < blocchi.length; i++) {
     await inviaTraduzione(blocchi, lingua, !conferma);
    console.log(`🚀 Invio blocco ${i + 1}/${blocchi.length}…`);
     const trad = await traduciBloccoProxy(blocchi[i], lingua);
    risultato += (trad || "[Errore Traduzione]") + "\n\n";
   }
   }


   const tradotto = risultato.trim();
   /* ======================= UI: PULSANTE ======================= */
   if (tutto) {
  // Aggiunge un bottone sotto l’editor
    area.value = tradotto;
   function addTranslateButton() {
  } else {
     const area = getEditor();
     const start = area.selectionStart;
     if (!area) return;
    const end  = area.selectionEnd;
     if (document.getElementById("btnCommonTranslate")) return;
     const prima = area.value.slice(0, start);
     const dopo  = area.value.slice(end);
    area.value  = prima + tradotto + dopo;
    area.selectionStart = start;
    area.selectionEnd  = start + tradotto.length;
  }


  area.dispatchEvent(new Event("input",  { bubbles: true }));
    const btn = document.createElement("button");
  area.dispatchEvent(new Event("change", { bubbles: true }));
    btn.id = "btnCommonTranslate";
    btn.className = "mw-ui-button";
    btn.textContent = "🧠 Traduci contenuto (via proxy)";
    btn.style.margin = "8px 0";
    btn.addEventListener("click", traduciTesto);


  console.log("✅ Traduzione completata.");
    area.parentNode.insertBefore(btn, area.nextSibling);
   return tradotto;
   }
}


async function traduciBloccoProxy(blocco, lingua) {
   // jQuery ready + fallback
   // Prompt “sicuro”: il server riceve solo il prompt, non la key
   if (typeof $ !== "undefined") {
   const system = "You are a professional MediaWiki translator. Preserve all templates, links, tags and wiki syntax exactly; translate only human-readable text. Keep headings and parameters unchanged.";
    $(addTranslateButton);
  const user  = `Translate the following MediaWiki wikitext into ${lingua}. Keep formatting/templates/links untouched:\n\n${blocco}`;
   } else {
 
     document.addEventListener("DOMContentLoaded", addTranslateButton);
  const prompt = [
    { role: "system", content: system },
    { role: "user",  content: user  }
  ];
 
  try {
    const res = await fetch(API_PROXY, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        prompt: JSON.stringify(prompt), // il “cameriere” inoltra come chat
        model:  MODEL_DEFAULT
      })
    });
 
    const data = await res.json().catch(() => ({}));
    if (!res.ok || data.status !== "ok") {
      const msg = data?.error || `HTTP ${res.status}`;
      throw new Error(msg);
    }
 
    // Il cameriere risponde con {status:"ok", result:"…"}
    return (data.result || "").trim();
   } catch (err) {
     console.error("❌ Errore da proxy:", err);
    return "[Errore Traduzione]";
   }
   }
}
}
/* ======================= UI: pulsante in editor ======================= */
$(function () {
  const area = $("#wpTextbox1");
  if (!area.length) return;
  if (!$("#pulsanteTraduci").length) {
    area.after('<button id="pulsanteTraduci" class="btn">🧠 Traduci contenuto</button>');
    $("#pulsanteTraduci").on("click", traduciTesto);
  }
});

Versione delle 14:12, 18 ago 2025

/* ======================= CommonTranslate.js (solo via proxy) ======================= */
console.log("🔄 Caricamento CommonTranslate.js…");

// Evita ricarichi multipli
if (window.__CommonTranslateLoaded) {
  console.warn("⚠️ CommonTranslate.js è già stato caricato.");
} else {
  window.__CommonTranslateLoaded = true;

  // URL del proxy lato server (non mettere la chiave nel browser!)
  const PROXY_URL = "/w/proxy.php";

  // Modello di default (puoi cambiarlo qui o da window.openaiModel)
  const DEFAULT_MODEL = (window.openaiModel || "gpt-4o");

  // Lingua di origine (fallback)
  const LINGUA_ORIGINE = document.documentElement.lang || "en";

  console.log("✅ CommonTranslate.js pronto (via proxy). Modello:", DEFAULT_MODEL);

  /* ======================= UTILITIES ======================= */
  function getEditor() {
    return document.getElementById("wpTextbox1");
  }
  function getSelectedTextInfo() {
    const area = getEditor();
    if (!area) return { text: "", start: 0, end: 0 };
    const start = area.selectionStart;
    const end = area.selectionEnd;
    const text = area.value.slice(start, end);
    return { text, start, end };
  }
  function segmentaTesto(testo, maxChars = 3000) {
    // Segmenti “morbidi” a limite caratteri su spazio
    const re = new RegExp(`.{1,${maxChars}}(?=\\s|$)`, "gs");
    return testo.match(re) || [];
  }

  // Chiamata singolo blocco al proxy
  async function traduciBloccoProxy(blocco, lingua, model = DEFAULT_MODEL) {
    try {
      const res = await fetch(PROXY_URL, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          model,
          messages: [
            {
              role: "system",
              content:
                "You are a professional MediaWiki translator. Translate preserving templates, links, headings, lists and any wiki syntax. Do not add explanations."
            },
            {
              role: "user",
              content: `Translate the following text from ${LINGUA_ORIGINE} to ${lingua}:\n\n${blocco}`
            }
          ],
          temperature: 0
        })
      });

      const data = await res.json();
      if (!res.ok) {
        const msg = data?.error || `HTTP ${res.status}`;
        throw new Error(msg);
      }
      const content = data?.choices?.[0]?.message?.content ?? "";
      return content || "[Traduzione vuota]";
    } catch (err) {
      console.error("❌ Errore da proxy:", err);
      return "[Errore Traduzione via proxy]";
    }
  }

  async function inviaTraduzione(blocchi, lingua, sostituisciTutto) {
    const area = getEditor();
    if (!area) {
      alert("❌ Campo editor non trovato (wpTextbox1).");
      return;
    }

    let risultato = "";
    for (let i = 0; i < blocchi.length; i++) {
      console.log(`🚀 Invio blocco ${i + 1}/${blocchi.length}…`);
      const tradotto = await traduciBloccoProxy(blocchi[i], lingua);
      risultato += tradotto.trim() + "\n\n";
    }
    risultato = risultato.trim();

    if (sostituisciTutto) {
      area.value = risultato;
    } else {
      const { start, end } = getSelectedTextInfo();
      const prima = area.value.slice(0, start);
      const dopo = area.value.slice(end);
      area.value = prima + risultato + dopo;
      area.selectionStart = start;
      area.selectionEnd = start + risultato.length;
    }

    // Notifica a MediaWiki
    area.dispatchEvent(new Event("input", { bubbles: true }));
    area.dispatchEvent(new Event("change", { bubbles: true }));

    console.log("✅ Traduzione completata e inserita.");
  }

  /* ======================= FLOW PRINCIPALE ======================= */
  async function traduciTesto() {
    const area = getEditor();
    if (!area) {
      alert("❌ Editor non trovato.");
      return;
    }

    // Solo selezione o tutto?
    const { text: selezione } = getSelectedTextInfo();
    const conferma = confirm(
      "📝 Vuoi tradurre SOLO il testo selezionato?\n\n✅ OK = solo selezione\n❌ Annulla = tutto il testo"
    );
    if (conferma && !selezione) {
      alert("⚠️ Nessuna selezione. Premi Annulla per tradurre tutto il testo.");
      return;
    }
    const testo = conferma ? selezione : area.value;
    if (!testo.trim()) {
      alert("⚠️ Niente da tradurre.");
      return;
    }

    // Lingua destinazione
    const lingua = prompt(
      "🌍 Scegli la lingua di traduzione (es: it, en, fr, es, de):",
      "it"
    );
    if (!lingua) return;

    // Segmentazione e invio
    const blocchi = segmentaTesto(testo);
    console.log("🧩 Segmentazione:", blocchi.length, "blocchi");
    await inviaTraduzione(blocchi, lingua, !conferma);
  }

  /* ======================= UI: PULSANTE ======================= */
  // Aggiunge un bottone sotto l’editor
  function addTranslateButton() {
    const area = getEditor();
    if (!area) return;
    if (document.getElementById("btnCommonTranslate")) return;

    const btn = document.createElement("button");
    btn.id = "btnCommonTranslate";
    btn.className = "mw-ui-button";
    btn.textContent = "🧠 Traduci contenuto (via proxy)";
    btn.style.margin = "8px 0";
    btn.addEventListener("click", traduciTesto);

    area.parentNode.insertBefore(btn, area.nextSibling);
  }

  // jQuery ready + fallback
  if (typeof $ !== "undefined") {
    $(addTranslateButton);
  } else {
    document.addEventListener("DOMContentLoaded", addTranslateButton);
  }
}