tradu-o / index.html
lufespi's picture
undefined - Initial Deployment
69302ca verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Convertisseur et Générateur de Rapport</title>
<style>
body { font-family: sans-serif; margin: 20px; background-color: #f0f0f0; }
.container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); max-width: 800px; margin: auto; }
#htmlOutputContainer { border: 1px solid #ccc; margin-top: 20px; padding: 10px; min-height: 300px; background-color: #f9f9f9; overflow: auto; white-space: pre-wrap; font-family: monospace; }
#loading { display: none; text-align: center; padding: 20px; font-style: italic; }
button:disabled { background-color: #ccc; cursor: not-allowed; }
.input-group { margin-bottom: 15px; }
.input-group label { margin-right: 10px; font-weight: bold; }
</style>
<!-- Inclusão da biblioteca Mammoth.js para converter .docx em HTML -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script>
<!-- Importation de la bibliothèque cliente Google AI -->
<script type="module">
// Attention : Ce code est pour DÉMONSTRATION et expose la clé API.
// NE PAS UTILISER EN PRODUCTION.
import { GoogleGenerativeAI, HarmCategory, HarmBlockThreshold } from "https://esm.run/@google/generative-ai";
const fileInput = document.getElementById('fileInput');
const languageSelect = document.getElementById('languageSelect');
const convertToHtmlButton = document.getElementById('convertToHtmlButton');
const htmlOutputContainer = document.getElementById('htmlOutputContainer');
const downloadButton = document.getElementById('downloadButton');
const generateReportButton = document.getElementById('generateReportButton');
const loadingDiv = document.getElementById('loading');
const previewHeader = document.getElementById('previewHeader');
const backToHtmlButton = document.getElementById('backToHtmlButton');
const applySuggestionsButton = document.getElementById('applySuggestionsButton');
let generatedWordHtml = '';
let reportText = '';
let correctedHtml = '';
// !!! ATTENTION : NE JAMAIS METTRE VOTRE VRAIE CLÉ API ICI DANS UN CODE PUBLIC !!!
const API_KEY = "AIzaSyAWwAhioiuTMAIHTyj9J3jpyVFmQ3OCoM0"; // SUBSTITUA PELA SUA CHAVE DE API
function cleanApiResponse(rawResponse) {
const markdownMatch = rawResponse.match(/```html\s*([\s\S]*?)\s*```/);
if (markdownMatch && markdownMatch[1]) {
return markdownMatch[1].trim();
}
const htmlMatch = rawResponse.match(/<!DOCTYPE html>[\s\S]*?<\/html>/i);
if (htmlMatch && htmlMatch[0]) {
// Extrai apenas o conteúdo dentro do <body> para evitar duplicação de estrutura
const bodyMatch = htmlMatch[0].match(/<body[^>]*>([\s\S]*)<\/body>/i);
return bodyMatch ? bodyMatch[1].trim() : htmlMatch[0];
}
return rawResponse;
}
convertToHtmlButton.addEventListener('click', async () => {
const file = fileInput.files[0];
if (!file) {
alert("Veuillez sélectionner un fichier Word (.docx).");
return;
}
if (API_KEY.startsWith("AIzaSy") && API_KEY.length < 30) {
alert("Veuillez remplacer la clé API de démonstration par votre véritable clé API Gemini.");
return;
}
loadingDiv.style.display = 'block';
htmlOutputContainer.innerHTML = '<p>Étape 1/2 : Conversion du document en HTML...</p>';
downloadButton.style.display = 'none';
generateReportButton.style.display = 'none';
backToHtmlButton.style.display = 'none';
applySuggestionsButton.style.display = 'none';
previewHeader.textContent = 'Aperçu HTML:';
convertToHtmlButton.disabled = true;
generatedWordHtml = '';
reportText = '';
correctedHtml = '';
try {
const arrayBuffer = await file.arrayBuffer();
const mammothResult = await mammoth.convertToHtml({ arrayBuffer: arrayBuffer });
const initialHtml = mammothResult.value;
htmlOutputContainer.innerHTML = '<p>Étape 2/2 : Traduction du contenu via l\'API...</p>';
const genAI = new GoogleGenerativeAI(API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-2.5-pro-preview-06-05" });
const selectedLanguage = languageSelect.value;
// ############# PROMPT DE TRADUÇÃO APRIMORADO #############
const prompt = `
Traduisez le contenu textuel de ce code HTML en ${selectedLanguage}.
RÈGLE FONDAMENTALE : Vous devez préserver **ABSOLUMENT TOUTES** les balises HTML de l'original, y compris les balises de mise en forme de texte comme <em>, <i>, <strong>, et <b>. Ne traduisez que le texte qui se trouve entre les balises.
Exemple de comportement attendu :
- HTML D'ENTRÉE : <p>Ce texte est <em>vraiment</em> important.</p>
- HTML DE SORTIE ATTENDU (pour l'anglais) : <p>This text is <em>really</em> important.</p>
Ne modifiez, ne supprimez et n'ajoutez aucune balise HTML. Retournez UNIQUEMENT le code HTML complet et traduit, sans aucun commentaire ou texte explicatif.`;
const parts = [
{ text: prompt },
{ text: initialHtml }
];
const result = await model.generateContent({ contents: [{ role: "user", parts }] });
const response = result.response;
generatedWordHtml = cleanApiResponse(response.text());
htmlOutputContainer.innerHTML = generatedWordHtml;
if (generatedWordHtml) {
downloadButton.style.display = 'inline-block';
generateReportButton.style.display = 'inline-block';
}
} catch (error) {
console.error('Erreur lors du processus:', error);
htmlOutputContainer.innerHTML = `<p style="color:red;">Erreur: ${error.message}</p>`;
} finally {
loadingDiv.style.display = 'none';
convertToHtmlButton.disabled = false;
}
});
downloadButton.addEventListener('click', () => {
const htmlToDownload = correctedHtml || generatedWordHtml;
if (!htmlToDownload) return;
const filename = correctedHtml ? 'document_corrige.doc' : 'document_converti.doc';
const fullHtml = `
<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'>
<head><meta charset='utf-8'><title>Titre</title></head>
<body>${htmlToDownload}<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=lufespi/tradu-o" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>`;
const blob = new Blob([fullHtml], { type: 'application/msword' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
generateReportButton.addEventListener('click', async () => {
if (!generatedWordHtml) {
alert("Aucun contenu à analyser. Veuillez d'abord convertir un document.");
return;
}
generateReportButton.disabled = true;
downloadButton.disabled = true;
generateReportButton.textContent = 'Génération du rapport...';
try {
const genAI = new GoogleGenerativeAI(API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash-latest" });
// ############# PROMPT DE RELATÓRIO APRIMORADO #############
const reportPrompt = `Analysez le texte contenu dans le code HTML suivant. Identifiez les phrases ou termes qui pourraient être améliorés dans la traduction.
Pour chaque correction suggérée, formatez votre réponse EXACTEMENT comme suit, sur trois lignes :
Ligne 1 : [Texte Original] - (IMPORTANT : incluez les balises HTML comme <em> ou <strong> si elles sont présentes dans le texte original)
Ligne 2 : [Texte Corrigé]
Ligne 3 : [Explication de la correction]
Ne modifiez pas le code HTML global. Fournissez uniquement le rapport textuel en suivant ce format. Analysez le document entier.`;
const fullPromptForReport = `${reportPrompt}\n\nVoici le code HTML à analyser:\n\n${generatedWordHtml}`;
const parts = [{ text: fullPromptForReport }];
const result = await model.generateContent({ contents: [{ role: "user", parts }] });
const response = result.response;
reportText = response.text();
previewHeader.textContent = 'Rapport de Traduction:';
htmlOutputContainer.innerHTML = reportText.replace(/\n/g, '<br>');
generateReportButton.style.display = 'none';
downloadButton.style.display = 'none';
backToHtmlButton.style.display = 'inline-block';
applySuggestionsButton.style.display = 'inline-block';
} catch (error) {
console.error("Erreur lors de la génération du rapport:", error);
alert("Erreur lors de la génération du rapport: " + error.message);
} finally {
generateReportButton.disabled = false;
downloadButton.disabled = false;
generateReportButton.textContent = '2. Générer un rapport de traduction';
}
});
backToHtmlButton.addEventListener('click', () => {
previewHeader.textContent = 'Aperçu HTML:';
htmlOutputContainer.innerHTML = generatedWordHtml;
generateReportButton.style.display = 'inline-block';
downloadButton.style.display = 'inline-block';
backToHtmlButton.style.display = 'none';
applySuggestionsButton.style.display = 'none';
});
applySuggestionsButton.addEventListener('click', async () => {
if (!generatedWordHtml || !reportText) {
alert("Aucun rapport ou HTML original à utiliser. Une erreur est survenue.");
return;
}
applySuggestionsButton.disabled = true;
backToHtmlButton.disabled = true;
applySuggestionsButton.textContent = 'Application en cours...';
loadingDiv.style.display = 'block';
try {
const genAI = new GoogleGenerativeAI(API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash-latest" });
// ############# PROMPT DE APLICAÇÃO DE SUGESTÕES APRIMORADO #############
const applyPrompt = `
En te basant sur le rapport d'analyse suivant :
--- RAPPORT ---
${reportText}
--- FIN DU RAPPORT ---
Modifie le code HTML original ci-dessous pour appliquer PRÉCISÉMENT ces suggestions.
RÈGLE FONDAMENTALE : La structure HTML, y compris **TOUTES** les balises de mise en forme comme <em>, <i>, <strong>, etc., doit être conservée intacte. Appliquez les corrections de texte SANS supprimer ces balises.
Exemple de comportement attendu :
- HTML ORIGINAL : <p>Le statut est <em>en attente</em>.</p>
- RAPPORT SUGGÈRE : "en attente" -> "en cours"
- HTML CORRIGÉ ATTENDU : <p>Le statut est <em>en cours</em>.</p>
--- HTML ORIGINAL ---
${generatedWordHtml}
--- FIN DU HTML ORIGINAL ---
Retourne UNIQUEMENT le code HTML complet et corrigé, sans aucun commentaire ou texte explicatif supplémentaire.
`;
const parts = [{ text: applyPrompt }];
const result = await model.generateContent({ contents: [{ role: "user", parts }] });
const response = result.response;
correctedHtml = cleanApiResponse(response.text());
previewHeader.textContent = 'Aperçu HTML (Version Corrigée):';
htmlOutputContainer.innerHTML = correctedHtml;
applySuggestionsButton.style.display = 'none';
backToHtmlButton.style.display = 'none';
downloadButton.style.display = 'inline-block';
} catch (error) {
console.error("Erreur lors de l'application des suggestions:", error);
alert("Erreur lors de l'application des suggestions: " + error.message);
htmlOutputContainer.innerHTML = reportText.replace(/\n/g, '<br>');
} finally {
applySuggestionsButton.disabled = false;
backToHtmlButton.disabled = false;
applySuggestionsButton.textContent = "Appliquer les suggestions";
loadingDiv.style.display = 'none';
}
});
</script>
</head>
<body>
<div class="container">
<h1>Convertisseur de Document et Générateur de Rapport</h1>
<p>
Téléversez un document Word (.docx) pour le convertir en HTML et le traduire.
</p>
<div class="input-group">
<input type="file" id="fileInput" accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document">
</div>
<div class="input-group">
<label for="languageSelect">Langue cible :</label>
<select id="languageSelect">
<option value="français" selected>Français</option>
<option value="anglais">Anglais</option>
<option value="espagnol">Espagnol</option>
<option value="portugais">Portugais (Brésil)</option>
<option value="allemand">Allemand</option>
<option value="italien">Italien</option>
<option value="japonais">Japonais</option>
</select>
</div>
<button id="convertToHtmlButton">1. Convertir et Traduire</button>
<div id="loading">Opération en cours, veuillez patienter...</div>
<h2 id="previewHeader">Aperçu HTML:</h2>
<div id="htmlOutputContainer">
<p>L'aperçu de l'HTML généré apparaîtra ici.</p>
</div>
<div style="text-align: right; margin-top: 20px;">
<button id="applySuggestionsButton" style="display:none;">Appliquer les suggestions</button>
<button id="backToHtmlButton" style="display:none; margin-left: 10px;">Retour à l'aperçu HTML</button>
<button id="generateReportButton" style="display:none; margin-left: 10px;">2. Générer un rapport de traduction</button>
<button id="downloadButton" style="display:none;">Télécharger en .doc</button>
</div>
</div>
</body>
</html>