MediaWiki:CommonTranslateUpdate.js: differenze tra le versioni
Nessun oggetto della modifica |
Nessun oggetto della modifica |
||
| Riga 4: | Riga 4: | ||
if (window.CommonTranslateUpdateLoaded) { | if (window.CommonTranslateUpdateLoaded) { | ||
console.warn("⚠️ CommonTranslateUpdate.js è già stato caricato."); | |||
} else { | } else { | ||
window.CommonTranslateUpdateLoaded = true; | |||
window.linguaOrigine = | |||
localStorage.getItem("linguaOrigine") || | |||
document.documentElement.lang || | |||
"en"; | |||
// 🔒 usiamo solo il cameriere | |||
window.OPENAI_PROXY_URL = "/dashboard/api/openai_project_gpt.php"; | |||
console.log("✅ CommonTranslateUpdate.js caricato correttamente!"); | |||
} | } | ||
/* ------- | /* ======================= UTIL: rimuovi marcatori e righe sentinella ======================= */ | ||
function stripMarkers(t) { | |||
return (t || "") | |||
// righe che contengono solo i marcatori (varie grafie) | |||
.replace(/^\s*[-–—]*\s*(TESTO\s*INIZIO|TEXT\s*START)\s*[-–—]*\s*$/gmi, "") | |||
.replace(/^\s*[-–—]*\s*(TESTO\s*FINE|TEXT\s*END)\s*[-–—]*\s*$/gmi, "") | |||
// marcatori inline stile --- TEXT START --- | |||
.replace(/---\s*(TESTO\s*INIZIO|TEXT\s*START)\s*---/gi, "") | |||
.replace(/---\s*(TESTO\s*FINE|TEXT\s*END)\s*---/gi, "") | |||
.trim(); | |||
} | |||
/* ======================= Segmentazione ======================= */ | |||
function segmentaBloccoMediaWiki(wikitesto) { | function segmentaBloccoMediaWiki(wikitesto) { | ||
// 1) proteggi blocchi “pericolosi” | // 1) proteggi blocchi “pericolosi” | ||
const blocchiProtetti = [ | const blocchiProtetti = [ | ||
/{{Tooltip\|[^{}]*?\|[^{}]*?\|<small>[\s\S]*?<\/small>\|?}}/g, | /{{Tooltip\|[^{}]*?\|[^{}]*?\|<small>[\s\S]*?<\/small>\|?}}/g, | ||
| Riga 29: | Riga 44: | ||
blocchiProtetti.forEach(rx => { | blocchiProtetti.forEach(rx => { | ||
wikitesto = wikitesto.replace(rx, m => { | wikitesto = wikitesto.replace(rx, m => { | ||
const key = `%%SEGMENTO_${idx++}%%`; placeholderMap[key] = m; return key; | const key = `%%SEGMENTO_${idx++}%%`; | ||
placeholderMap[key] = m; | |||
return key; | |||
}); | }); | ||
}); | }); | ||
| Riga 36: | Riga 53: | ||
const parts = wikitesto.split(/\n(?=^={2,5}[^=\n]+={2,5}\s*$)/m); | const parts = wikitesto.split(/\n(?=^={2,5}[^=\n]+={2,5}\s*$)/m); | ||
// 3) ricostruisci sezioni, trattando | // 3) ricostruisci sezioni, trattando “Bibliography/References/Riferimenti” voce-per-voce | ||
const out = []; | const out = []; | ||
const isBiblioHeader = (s) => /^={2,5}\s*(bibliography|references|riferimenti)\b/i.test(s.trim()); | const isBiblioHeader = (s) => | ||
/^={2,5}\s*(bibliography|references|riferimenti)\b/i.test(s.trim()); | |||
parts.forEach(section => { | parts.forEach(section => { | ||
// reintegra i placeholder | // reintegra i placeholder | ||
Object.entries(placeholderMap).forEach(([k, v]) => { section = section.replaceAll(k, v); }); | Object.entries(placeholderMap).forEach(([k, v]) => { | ||
section = section.replaceAll(k, v); | |||
}); | |||
if (isBiblioHeader(section)) { | if (isBiblioHeader(section)) { | ||
const [header, ...rest] = section.split(/\n/); | const [header, ...rest] = section.split(/\n/); | ||
const body = rest.join("\n"); | const body = rest.join("\n"); | ||
| Riga 60: | Riga 79: | ||
}); | }); | ||
return out.filter(Boolean); | return out.filter(Boolean); | ||
} | } | ||
/* ======================= Ripristina segmenti salvati ======================= */ | /* ======================= Ripristina segmenti salvati ======================= */ | ||
function ripristinaSegmenti(text) { | function ripristinaSegmenti(text) { | ||
if (!window.segmentiSalvati) return text; | |||
return text.replace(/%%SEGMENTO_(\d+)%%/g, function (_m, index) { | |||
const segmento = window.segmentiSalvati[parseInt(index, 10)]; | |||
return segmento ? segmento : _m; | |||
}); | |||
} | } | ||
/* ======================= AVVIO TRADUZIONE ======================= */ | /* ======================= AVVIO TRADUZIONE ======================= */ | ||
async function traduciTestoUpdate() { | async function traduciTestoUpdate() { | ||
const conferma = confirm("⚡ Vuoi tradurre il capitolo?"); | |||
if (!conferma) return; | |||
const linguaScelta = prompt( | |||
"🌍 Scegli la lingua:\nit = Italiano\nen = Inglese\nfr = Francese\nes = Spagnolo\nde = Tedesco\ntutte = Tutte le lingue", | |||
"en" | |||
); | |||
if (!linguaScelta) return; | |||
const lingue = ["it", "en", "fr", "es", "de"]; | |||
const lingueDaTradurre = linguaScelta === "tutte" ? lingue : [linguaScelta]; | |||
localStorage.setItem("lingueDaTradurre", JSON.stringify(lingueDaTradurre)); | |||
verificaURLCapitoli(lingueDaTradurre); | |||
} | } | ||
function verificaURLCapitoli(lingueDaTradurre) { | function verificaURLCapitoli(lingueDaTradurre) { | ||
let formHtml = `<form id="urlForm">`; | |||
for (let lang of lingueDaTradurre) { | |||
formHtml += ` | |||
<label for="url_${lang}">${lang.toUpperCase()}:</label> | |||
<input type="text" id="url_${lang}" value="${localStorage.getItem( | |||
`urlCapitolo_${lang}` | |||
) || ""}" style="width:100%;margin-bottom:5px;"><br>`; | |||
} | |||
formHtml += `</form>`; | |||
let popup = document.createElement("div"); | |||
popup.innerHTML = ` | |||
<div id="popup-url-traduzione" style="position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); | <div id="popup-url-traduzione" style="position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); | ||
background:white; padding:20px; border-radius:10px; box-shadow:0px 4px 6px rgba(0,0,0,0.2); z-index:2000; font-family:Arial, sans-serif;"> | background:white; padding:20px; border-radius:10px; box-shadow:0px 4px 6px rgba(0,0,0,0.2); z-index:2000; font-family:Arial, sans-serif;"> | ||
<h3>🔍 Imposta gli URL per la traduzione</h3> | |||
<p> | |||
🧠 <b>Parole:</b> <span id="conteggio-parole">0</span><br> | |||
🔢 <b>Token stimati:</b> <span id="conteggio-token">0</span><br> | |||
💰 <b>Costo stimato:</b> <span id="costo-stimato">$0.0000</span> | |||
</p> | |||
${formHtml} | |||
<div style="margin-top:10px; text-align:right;"> | |||
<button id="salvaUrlBtn" type="button">✅ Salva URL</button> | |||
<button id="chiudiPopup" type="button">❌ Annulla</button> | |||
</div> | |||
</div>`; | |||
document.body.appendChild(popup); | |||
const testo = ottieniTestoDaEditor(); | |||
const parole = testo.trim().split(/\s+/).length; | |||
const tokenStimati = Math.ceil(parole / 0.75); | |||
const costo = (tokenStimati * 0.00002).toFixed(4); | |||
try { | |||
document.getElementById("conteggio-parole").innerText = parole; | |||
document.getElementById("conteggio-token").innerText = tokenStimati; | |||
document.getElementById("costo-stimato").innerText = `$${costo}`; | |||
} catch (e) { | |||
console.warn("⚠️ Conteggio parole/token non aggiornato:", e); | |||
} | |||
setTimeout(() => { | |||
const salvaBtn = document.getElementById("salvaUrlBtn"); | |||
const chiudiBtn = document.getElementById("chiudiPopup"); | |||
document.body. | if (salvaBtn) { | ||
salvaBtn.onclick = function () { | |||
lingueDaTradurre.forEach((lang) => { | |||
const newUrl = document.getElementById(`url_${lang}`).value.trim(); | |||
localStorage.setItem(`urlCapitolo_${lang}`, newUrl); | |||
}); | |||
document.body.removeChild(popup); | |||
avviaTraduzioneConSegmentazione(lingueDaTradurre); | |||
}; | |||
} | |||
if (chiudiBtn) { | |||
chiudiBtn.onclick = function () { | |||
document.body.removeChild(popup); | |||
}; | |||
document. | |||
} | } | ||
}, 100); | |||
} | } | ||
function avviaTraduzioneConSegmentazione(lingueDaTradurre) { | function avviaTraduzioneConSegmentazione(lingueDaTradurre) { | ||
const testo = ottieniTestoDaEditor(); | |||
const blocchi = segmentaBloccoMediaWiki(testo); | |||
const errori = analizzaErroriSegmentazione(blocchi); | |||
sostituisciTestoSegmentato(blocchi, errori); | |||
mostraPopupConferma(blocchi, errori, lingueDaTradurre); | |||
} | } | ||
/*========= Errori ed invio ==========*/ | /* ======================= Errori ed invio ======================= */ | ||
function ottieniTestoDaEditor() { | function ottieniTestoDaEditor() { | ||
const area = document.getElementById("wpTextbox1"); | |||
return area ? area.value.trim() : ""; | |||
} | } | ||
function analizzaErroriSegmentazione(blocchi) { | function analizzaErroriSegmentazione(blocchi) { | ||
let errori = []; | |||
blocchi.forEach((b, i) => { | |||
let err = []; | |||
[["[", "]"], ["{", "}"], ["<", ">"]].forEach(([o, c]) => { | |||
const countO = (b.match(new RegExp(`\\${o}`, "g")) || []).length; | |||
const countC = (b.match(new RegExp(`\\${c}`, "g")) || []).length; | |||
if (countO !== countC) err.push(`${o}${c} non bilanciate`); | |||
}); | }); | ||
return errori; | if (err.length) errori.push({ index: i + 1, dettagli: err }); | ||
}); | |||
return errori; | |||
} | } | ||
function sostituisciTestoSegmentato(blocchi, errori) { | function sostituisciTestoSegmentato(blocchi, errori) { | ||
const area = document.getElementById("wpTextbox1"); | |||
const finale = blocchi.map((b, i) => { | |||
const e = errori.find((x) => x.index === i + 1); | |||
const evidenza = e ? `⚠️ ERRORI:\n- ${e.dettagli.join("\n- ")}\n\n` : ""; | |||
return `${evidenza}🔹 BLOCCO ${i + 1}\n${b}`; | |||
}); | |||
area.value = finale.join("\n\n==============================\n\n"); | |||
} | } | ||
function mostraPopupConferma(blocchi, errori, lingueDaTradurre) { | function mostraPopupConferma(blocchi, errori, lingueDaTradurre) { | ||
const popup = document.createElement("div"); | |||
popup.style = ` | |||
position: fixed; | |||
bottom: 20px; | |||
right: 20px; | |||
background: white; | |||
padding: 15px; | |||
border: 2px solid #444; | |||
box-shadow: 4px 4px 10px rgba(0,0,0,0.3); | |||
z-index: 9999; | |||
resize: both; | |||
overflow: auto; | |||
cursor: move;`; | |||
popup.innerHTML = ` | |||
<b>✅ Segmentazione completata</b><br> | |||
🧠 Token stimati: ~${blocchi.length * 3000}<br> | |||
💰 Costo approssimativo: ~$${(blocchi.length * 3000 * 0.00002).toFixed(2)}<br> | |||
${errori.length > 0 | |||
? `❗ <span style='color:red;'>${errori.length} errori rilevati. Procedere?</span><br>` | |||
: `<span style='color:green;'>✅ Nessun errore rilevato</span><br>`} | |||
<button id="btn-invia-traduzioni">✅ Invia Traduzioni</button> | |||
<button id="btn-salva-segmentato">💾 Salva localmente</button> | |||
<button onclick="this.parentNode.remove()">❌ Annulla</button>`; | |||
document.body.appendChild(popup); | |||
dragElement(popup); | |||
document.getElementById("btn-invia-traduzioni").onclick = () => { | |||
popup.remove(); | |||
lingueDaTradurre.forEach((lang) => inviaTraduzione(lang, blocchi)); | |||
}; | |||
document.getElementById("btn-salva-segmentato").onclick = () => { | |||
const testoSegmentato = document.getElementById("wpTextbox1").value.trim(); | |||
localStorage.setItem("segmentatoDaInviare", testoSegmentato); | |||
alert("✅ Testo segmentato salvato localmente!"); | |||
}; | |||
} | } | ||
function ricostruisciTestoSegmentato(blocchi) { | function ricostruisciTestoSegmentato(blocchi) { | ||
return blocchi | |||
.map((b) => | |||
b.trim().match(/^={2,5}[^=]+={2,5}$/) | |||
? `\n\n${b.trim()}\n\n` | |||
: b.trim() | |||
) | |||
.join("\n\n"); | |||
} | } | ||
/* ======================= INVIO AL CAMERIERE ( | /* ======================= INVIO AL CAMERIERE (MediaWiki API) ======================= */ | ||
async function inviaTraduzione(lingua, blocchi) { | async function inviaTraduzione(lingua, blocchi) { | ||
const urlAPI = "https://staging.masticationpedia.org/api.php"; | |||
const urlDestinazione = localStorage.getItem(`urlCapitolo_${lingua}`); | |||
if (!urlDestinazione) return; | |||
// CSRF token | |||
const tokenRes = await fetch( | |||
`${urlAPI}?action=query&meta=tokens&type=csrf&format=json`, | |||
{ credentials: "include" } | |||
); | |||
const tokenData = await tokenRes.json(); | |||
const csrfToken = tokenData?.query?.tokens?.csrftoken; | |||
// traduci ogni blocco via proxy, poi ricostruisci | |||
let testoTradotto = ricostruisciTestoSegmentato( | |||
await Promise.all(blocchi.map((b) => traduciBloccoProxy(b, lingua))) | |||
); | |||
// ripristina placeholder e ripulisci marcatori residuali | |||
testoTradotto = ripristinaSegmenti(testoTradotto); | |||
testoTradotto = stripMarkers(testoTradotto); | |||
// invia modifica | |||
const formData = new URLSearchParams(); | |||
formData.append("action", "edit"); | |||
formData.append("title", urlDestinazione.split("/").pop()); | |||
formData.append("text", testoTradotto); | |||
formData.append("format", "json"); | |||
formData.append("token", csrfToken); | |||
const response = await fetch(urlAPI, { | |||
method: "POST", | |||
headers: { "Content-Type": "application/x-www-form-urlencoded" }, | |||
body: formData, | |||
credentials: "include", | |||
}); | |||
const data = await response.json(); | |||
if (data.edit && data.edit.result === "Success") { | |||
alert(`✅ Traduzione inviata con successo a: ${urlDestinazione}`); | |||
} else { | |||
console.error("❌ Errore invio:", data); | |||
} | |||
} | } | ||
/* ======================= | /* ======================= Traduzione BLOCCO via cameriere ======================= */ | ||
async function traduciBloccoProxy(blocco, lingua) { | async function traduciBloccoProxy(blocco, lingua) { | ||
try { | |||
const res = await fetch(window.OPENAI_PROXY_URL, { | |||
method: "POST", | |||
headers: { "Content-Type": "application/json" }, | |||
body: JSON.stringify({ | |||
model: "gpt-4o", | |||
prompt: | |||
`Translate this MediaWiki text into ${lingua}. ` + | |||
`Preserve ALL formatting, templates {{...}}, links [[...]], <math>, <ref>, etc. ` + | |||
`Do not add explanations, comments or change syntax.\n` + | |||
`--- TEXT START ---\n${blocco}\n--- TEXT END ---`, | |||
}), | |||
}); | |||
const data = await res.json(); | |||
if (data.status !== "ok") throw new Error(data.error || "Errore proxy"); | |||
// 🧹 rimuovi eventuali marcatori riecheggiati | |||
return stripMarkers(data.result); | |||
} catch (e) { | |||
console.error("❌ Traduzione fallita:", e); | |||
return "[Errore Traduzione]"; | |||
} | |||
} | } | ||
/* ======================= Drag helper ======================= */ | |||
function dragElement(elmnt) { | function dragElement(elmnt) { | ||
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; | |||
elmnt.onmousedown = dragMouseDown; | |||
function dragMouseDown(e) { | |||
e = e || window.event; e.preventDefault(); | |||
pos3 = e.clientX; pos4 = e.clientY; | |||
document.onmouseup = closeDragElement; | |||
document.onmousemove = elementDrag; | |||
} | |||
function elementDrag(e) { | |||
e = e || window.event; e.preventDefault(); | |||
pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; | |||
pos3 = e.clientX; pos4 = e.clientY; | |||
elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; | |||
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; | |||
} | |||
function closeDragElement() { | |||
document.onmouseup = null; | |||
document.onmousemove = null; | |||
} | |||
} | } | ||
Versione delle 14:55, 6 set 2025
/* ======================= CommonTranslateUpdate.js (INTEGRAZIONE COMPLETA con proxy) ======================= */
console.log("🔄 Caricamento CommonTranslateUpdate.js...");
if (window.CommonTranslateUpdateLoaded) {
console.warn("⚠️ CommonTranslateUpdate.js è già stato caricato.");
} else {
window.CommonTranslateUpdateLoaded = true;
window.linguaOrigine =
localStorage.getItem("linguaOrigine") ||
document.documentElement.lang ||
"en";
// 🔒 usiamo solo il cameriere
window.OPENAI_PROXY_URL = "/dashboard/api/openai_project_gpt.php";
console.log("✅ CommonTranslateUpdate.js caricato correttamente!");
}
/* ======================= UTIL: rimuovi marcatori e righe sentinella ======================= */
function stripMarkers(t) {
return (t || "")
// righe che contengono solo i marcatori (varie grafie)
.replace(/^\s*[-–—]*\s*(TESTO\s*INIZIO|TEXT\s*START)\s*[-–—]*\s*$/gmi, "")
.replace(/^\s*[-–—]*\s*(TESTO\s*FINE|TEXT\s*END)\s*[-–—]*\s*$/gmi, "")
// marcatori inline stile --- TEXT START ---
.replace(/---\s*(TESTO\s*INIZIO|TEXT\s*START)\s*---/gi, "")
.replace(/---\s*(TESTO\s*FINE|TEXT\s*END)\s*---/gi, "")
.trim();
}
/* ======================= Segmentazione ======================= */
function segmentaBloccoMediaWiki(wikitesto) {
// 1) proteggi blocchi “pericolosi”
const blocchiProtetti = [
/{{Tooltip\|[^{}]*?\|[^{}]*?\|<small>[\s\S]*?<\/small>\|?}}/g,
/{{Tooltip\|(?:[^{}]|{{[^{}]*}}|<[^>]*>|\[\[[^\]]+\]\]|%%[^%]+%%)+}}/g,
/<ref[^>]*>[\s\S]*?<\/ref>/g,
/<math>[\s\S]*?<\/math>/g,
/<gallery[\s\S]*?<\/gallery>/g,
/{{(?:[^{}]|\{\{[^{}]*\}\})*}}/g
];
let placeholderMap = {}, idx = 0;
blocchiProtetti.forEach(rx => {
wikitesto = wikitesto.replace(rx, m => {
const key = `%%SEGMENTO_${idx++}%%`;
placeholderMap[key] = m;
return key;
});
});
// 2) split per intestazioni vere: == H2/H3/H4 ==
const parts = wikitesto.split(/\n(?=^={2,5}[^=\n]+={2,5}\s*$)/m);
// 3) ricostruisci sezioni, trattando “Bibliography/References/Riferimenti” voce-per-voce
const out = [];
const isBiblioHeader = (s) =>
/^={2,5}\s*(bibliography|references|riferimenti)\b/i.test(s.trim());
parts.forEach(section => {
// reintegra i placeholder
Object.entries(placeholderMap).forEach(([k, v]) => {
section = section.replaceAll(k, v);
});
if (isBiblioHeader(section)) {
const [header, ...rest] = section.split(/\n/);
const body = rest.join("\n");
const items = body.match(/^\s*\d+\.\s[\s\S]*?(?=(?:^\s*\d+\.\s)|\Z)/gm);
if (items && items.length) {
out.push(header.trim());
items.forEach(it => out.push(it.trim()));
} else {
out.push(section.trim());
}
} else {
out.push(section.trim());
}
});
return out.filter(Boolean);
}
/* ======================= Ripristina segmenti salvati ======================= */
function ripristinaSegmenti(text) {
if (!window.segmentiSalvati) return text;
return text.replace(/%%SEGMENTO_(\d+)%%/g, function (_m, index) {
const segmento = window.segmentiSalvati[parseInt(index, 10)];
return segmento ? segmento : _m;
});
}
/* ======================= AVVIO TRADUZIONE ======================= */
async function traduciTestoUpdate() {
const conferma = confirm("⚡ Vuoi tradurre il capitolo?");
if (!conferma) return;
const linguaScelta = prompt(
"🌍 Scegli la lingua:\nit = Italiano\nen = Inglese\nfr = Francese\nes = Spagnolo\nde = Tedesco\ntutte = Tutte le lingue",
"en"
);
if (!linguaScelta) return;
const lingue = ["it", "en", "fr", "es", "de"];
const lingueDaTradurre = linguaScelta === "tutte" ? lingue : [linguaScelta];
localStorage.setItem("lingueDaTradurre", JSON.stringify(lingueDaTradurre));
verificaURLCapitoli(lingueDaTradurre);
}
function verificaURLCapitoli(lingueDaTradurre) {
let formHtml = `<form id="urlForm">`;
for (let lang of lingueDaTradurre) {
formHtml += `
<label for="url_${lang}">${lang.toUpperCase()}:</label>
<input type="text" id="url_${lang}" value="${localStorage.getItem(
`urlCapitolo_${lang}`
) || ""}" style="width:100%;margin-bottom:5px;"><br>`;
}
formHtml += `</form>`;
let popup = document.createElement("div");
popup.innerHTML = `
<div id="popup-url-traduzione" style="position:fixed; top:50%; left:50%; transform:translate(-50%, -50%);
background:white; padding:20px; border-radius:10px; box-shadow:0px 4px 6px rgba(0,0,0,0.2); z-index:2000; font-family:Arial, sans-serif;">
<h3>🔍 Imposta gli URL per la traduzione</h3>
<p>
🧠 <b>Parole:</b> <span id="conteggio-parole">0</span><br>
🔢 <b>Token stimati:</b> <span id="conteggio-token">0</span><br>
💰 <b>Costo stimato:</b> <span id="costo-stimato">$0.0000</span>
</p>
${formHtml}
<div style="margin-top:10px; text-align:right;">
<button id="salvaUrlBtn" type="button">✅ Salva URL</button>
<button id="chiudiPopup" type="button">❌ Annulla</button>
</div>
</div>`;
document.body.appendChild(popup);
const testo = ottieniTestoDaEditor();
const parole = testo.trim().split(/\s+/).length;
const tokenStimati = Math.ceil(parole / 0.75);
const costo = (tokenStimati * 0.00002).toFixed(4);
try {
document.getElementById("conteggio-parole").innerText = parole;
document.getElementById("conteggio-token").innerText = tokenStimati;
document.getElementById("costo-stimato").innerText = `$${costo}`;
} catch (e) {
console.warn("⚠️ Conteggio parole/token non aggiornato:", e);
}
setTimeout(() => {
const salvaBtn = document.getElementById("salvaUrlBtn");
const chiudiBtn = document.getElementById("chiudiPopup");
if (salvaBtn) {
salvaBtn.onclick = function () {
lingueDaTradurre.forEach((lang) => {
const newUrl = document.getElementById(`url_${lang}`).value.trim();
localStorage.setItem(`urlCapitolo_${lang}`, newUrl);
});
document.body.removeChild(popup);
avviaTraduzioneConSegmentazione(lingueDaTradurre);
};
}
if (chiudiBtn) {
chiudiBtn.onclick = function () {
document.body.removeChild(popup);
};
}
}, 100);
}
function avviaTraduzioneConSegmentazione(lingueDaTradurre) {
const testo = ottieniTestoDaEditor();
const blocchi = segmentaBloccoMediaWiki(testo);
const errori = analizzaErroriSegmentazione(blocchi);
sostituisciTestoSegmentato(blocchi, errori);
mostraPopupConferma(blocchi, errori, lingueDaTradurre);
}
/* ======================= Errori ed invio ======================= */
function ottieniTestoDaEditor() {
const area = document.getElementById("wpTextbox1");
return area ? area.value.trim() : "";
}
function analizzaErroriSegmentazione(blocchi) {
let errori = [];
blocchi.forEach((b, i) => {
let err = [];
[["[", "]"], ["{", "}"], ["<", ">"]].forEach(([o, c]) => {
const countO = (b.match(new RegExp(`\\${o}`, "g")) || []).length;
const countC = (b.match(new RegExp(`\\${c}`, "g")) || []).length;
if (countO !== countC) err.push(`${o}${c} non bilanciate`);
});
if (err.length) errori.push({ index: i + 1, dettagli: err });
});
return errori;
}
function sostituisciTestoSegmentato(blocchi, errori) {
const area = document.getElementById("wpTextbox1");
const finale = blocchi.map((b, i) => {
const e = errori.find((x) => x.index === i + 1);
const evidenza = e ? `⚠️ ERRORI:\n- ${e.dettagli.join("\n- ")}\n\n` : "";
return `${evidenza}🔹 BLOCCO ${i + 1}\n${b}`;
});
area.value = finale.join("\n\n==============================\n\n");
}
function mostraPopupConferma(blocchi, errori, lingueDaTradurre) {
const popup = document.createElement("div");
popup.style = `
position: fixed;
bottom: 20px;
right: 20px;
background: white;
padding: 15px;
border: 2px solid #444;
box-shadow: 4px 4px 10px rgba(0,0,0,0.3);
z-index: 9999;
resize: both;
overflow: auto;
cursor: move;`;
popup.innerHTML = `
<b>✅ Segmentazione completata</b><br>
🧠 Token stimati: ~${blocchi.length * 3000}<br>
💰 Costo approssimativo: ~$${(blocchi.length * 3000 * 0.00002).toFixed(2)}<br>
${errori.length > 0
? `❗ <span style='color:red;'>${errori.length} errori rilevati. Procedere?</span><br>`
: `<span style='color:green;'>✅ Nessun errore rilevato</span><br>`}
<button id="btn-invia-traduzioni">✅ Invia Traduzioni</button>
<button id="btn-salva-segmentato">💾 Salva localmente</button>
<button onclick="this.parentNode.remove()">❌ Annulla</button>`;
document.body.appendChild(popup);
dragElement(popup);
document.getElementById("btn-invia-traduzioni").onclick = () => {
popup.remove();
lingueDaTradurre.forEach((lang) => inviaTraduzione(lang, blocchi));
};
document.getElementById("btn-salva-segmentato").onclick = () => {
const testoSegmentato = document.getElementById("wpTextbox1").value.trim();
localStorage.setItem("segmentatoDaInviare", testoSegmentato);
alert("✅ Testo segmentato salvato localmente!");
};
}
function ricostruisciTestoSegmentato(blocchi) {
return blocchi
.map((b) =>
b.trim().match(/^={2,5}[^=]+={2,5}$/)
? `\n\n${b.trim()}\n\n`
: b.trim()
)
.join("\n\n");
}
/* ======================= INVIO AL CAMERIERE (MediaWiki API) ======================= */
async function inviaTraduzione(lingua, blocchi) {
const urlAPI = "https://staging.masticationpedia.org/api.php";
const urlDestinazione = localStorage.getItem(`urlCapitolo_${lingua}`);
if (!urlDestinazione) return;
// CSRF token
const tokenRes = await fetch(
`${urlAPI}?action=query&meta=tokens&type=csrf&format=json`,
{ credentials: "include" }
);
const tokenData = await tokenRes.json();
const csrfToken = tokenData?.query?.tokens?.csrftoken;
// traduci ogni blocco via proxy, poi ricostruisci
let testoTradotto = ricostruisciTestoSegmentato(
await Promise.all(blocchi.map((b) => traduciBloccoProxy(b, lingua)))
);
// ripristina placeholder e ripulisci marcatori residuali
testoTradotto = ripristinaSegmenti(testoTradotto);
testoTradotto = stripMarkers(testoTradotto);
// invia modifica
const formData = new URLSearchParams();
formData.append("action", "edit");
formData.append("title", urlDestinazione.split("/").pop());
formData.append("text", testoTradotto);
formData.append("format", "json");
formData.append("token", csrfToken);
const response = await fetch(urlAPI, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: formData,
credentials: "include",
});
const data = await response.json();
if (data.edit && data.edit.result === "Success") {
alert(`✅ Traduzione inviata con successo a: ${urlDestinazione}`);
} else {
console.error("❌ Errore invio:", data);
}
}
/* ======================= Traduzione BLOCCO via cameriere ======================= */
async function traduciBloccoProxy(blocco, lingua) {
try {
const res = await fetch(window.OPENAI_PROXY_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "gpt-4o",
prompt:
`Translate this MediaWiki text into ${lingua}. ` +
`Preserve ALL formatting, templates {{...}}, links [[...]], <math>, <ref>, etc. ` +
`Do not add explanations, comments or change syntax.\n` +
`--- TEXT START ---\n${blocco}\n--- TEXT END ---`,
}),
});
const data = await res.json();
if (data.status !== "ok") throw new Error(data.error || "Errore proxy");
// 🧹 rimuovi eventuali marcatori riecheggiati
return stripMarkers(data.result);
} catch (e) {
console.error("❌ Traduzione fallita:", e);
return "[Errore Traduzione]";
}
}
/* ======================= Drag helper ======================= */
function dragElement(elmnt) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
elmnt.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event; e.preventDefault();
pos3 = e.clientX; pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event; e.preventDefault();
pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY;
pos3 = e.clientX; pos4 = e.clientY;
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}