Auto commit: 2025-11-20T12:17:08.078Z

This commit is contained in:
Flatlogic Bot 2025-11-20 12:17:08 +00:00
parent 2c882d18a4
commit 28bed0ead6
3 changed files with 160 additions and 22 deletions

View File

@ -68,38 +68,100 @@ document.addEventListener('DOMContentLoaded', function () {
}) })
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); // Try to get error message from JSON response, otherwise use status text
return response.json().then(err => {
throw new Error(err.message || response.statusText);
}).catch(() => {
throw new Error(response.statusText);
});
} }
return response.json(); return response.json();
}) })
.then(data => { .then(data => {
if(processingMessage) { processingMessage.style.display = 'none';
processingMessage.style.display = 'none';
} if (data.status === 'success') {
if(responseMessageDiv){ const translatedText = data.data.translatedText;
const alertClass = data.status === 'success' ? 'alert-success' : 'alert-danger'; const outputContainer = document.getElementById('translation-output-container');
responseMessageDiv.innerHTML = `<div class="alert ${alertClass}" role="alert">${data.message}</div>`; const outputElement = document.getElementById('translation-output');
if (outputElement && outputContainer) {
outputElement.textContent = translatedText;
outputContainer.style.display = 'block';
}
responseMessageDiv.innerHTML = `<div class="alert alert-success" role="alert">${data.message}</div>`;
responseMessageDiv.style.display = 'block';
} else {
responseMessageDiv.innerHTML = `<div class="alert alert-danger" role="alert"><strong>Error:</strong> ${data.message}</div>`;
responseMessageDiv.style.display = 'block'; responseMessageDiv.style.display = 'block';
}
if(translateBtn) {
translateBtn.disabled = false;
translateBtn.innerHTML = 'Translate';
} }
}) })
.catch(error => { .catch(error => {
if(processingMessage) { processingMessage.style.display = 'none';
processingMessage.style.display = 'none'; responseMessageDiv.innerHTML = `<div class="alert alert-danger" role="alert">An unexpected error occurred: ${error.message}</div>`;
} responseMessageDiv.style.display = 'block';
if(responseMessageDiv){ console.error('Fetch Error:', error);
responseMessageDiv.innerHTML = `<div class="alert alert-danger" role="alert">An error occurred during the upload. Please try again.</div>`; })
responseMessageDiv.style.display = 'block'; .finally(() => {
}
if(translateBtn) { if(translateBtn) {
translateBtn.disabled = false; translateBtn.disabled = false;
translateBtn.innerHTML = 'Translate'; translateBtn.innerHTML = '<i class="bi bi-stars"></i> Translate';
} }
console.error('Error:', error);
}); });
}); });
} }
// Handle copy and download buttons
const copyBtn = document.getElementById('copy-btn');
const downloadBtn = document.getElementById('download-btn');
const translationOutput = document.getElementById('translation-output');
if (copyBtn) {
copyBtn.addEventListener('click', () => {
if (translationOutput && navigator.clipboard) {
navigator.clipboard.writeText(translationOutput.textContent).then(() => {
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="bi bi-check-lg"></i> Copied!';
copyBtn.classList.add('btn-success');
copyBtn.classList.remove('btn-secondary');
setTimeout(() => {
copyBtn.innerHTML = originalText;
copyBtn.classList.remove('btn-success');
copyBtn.classList.add('btn-secondary');
}, 2000);
}).catch(err => {
console.error('Failed to copy text: ', err);
alert('Failed to copy text. Please try again.');
});
}
});
}
if (downloadBtn) {
downloadBtn.addEventListener('click', () => {
if (translationOutput && window.jspdf) {
try {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
// Set font that supports a wide range of characters
doc.setFont('Helvetica');
doc.setFontSize(12);
const text = translationOutput.textContent;
const lines = doc.splitTextToSize(text, 180);
doc.text(lines, 15, 20);
doc.save('translation.pdf');
} catch (error) {
console.error('Failed to generate PDF:', error);
alert('Failed to generate PDF. An error occurred.');
}
} else if (!window.jspdf) {
alert('The PDF generation library is not loaded.');
}
});
}
}); });

View File

@ -58,11 +58,20 @@
<label for="source-lang" class="form-label">2. Source Language</label> <label for="source-lang" class="form-label">2. Source Language</label>
<select class="form-select" id="source-lang" required> <select class="form-select" id="source-lang" required>
<option selected disabled value="">Choose...</option> <option selected disabled value="">Choose...</option>
<option value="auto">Auto-detect</option>
<option value="en">English</option> <option value="en">English</option>
<option value="es">Spanish</option> <option value="es">Spanish</option>
<option value="fr">French</option> <option value="fr">French</option>
<option value="de">German</option> <option value="de">German</option>
<option value="auto">Auto-detect</option> <option value="it">Italian</option>
<option value="pt">Portuguese</option>
<option value="nl">Dutch</option>
<option value="ru">Russian</option>
<option value="ja">Japanese</option>
<option value="ko">Korean</option>
<option value="zh">Chinese (Simplified)</option>
<option value="ar">Arabic</option>
<option value="hi">Hindi</option>
</select> </select>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
@ -73,6 +82,15 @@
<option value="es">Spanish</option> <option value="es">Spanish</option>
<option value="fr">French</option> <option value="fr">French</option>
<option value="de">German</option> <option value="de">German</option>
<option value="it">Italian</option>
<option value="pt">Portuguese</option>
<option value="nl">Dutch</option>
<option value="ru">Russian</option>
<option value="ja">Japanese</option>
<option value="ko">Korean</option>
<option value="zh">Chinese (Simplified)</option>
<option value="ar">Arabic</option>
<option value="hi">Hindi</option>
</select> </select>
</div> </div>
</div> </div>
@ -92,6 +110,19 @@
<span>Processing your document... This may take a moment.</span> <span>Processing your document... This may take a moment.</span>
</div> </div>
</div> </div>
<div id="translation-output-container" class="card mt-4" style="display: none;">
<div class="card-header fw-bold">
<i class="bi bi-check-circle-fill text-success me-2"></i>Translation Result
</div>
<div class="card-body">
<pre id="translation-output" class="mb-0" style="white-space: pre-wrap; font-family: inherit; font-size: 1rem;"></pre>
</div>
<div class="card-footer bg-light text-end">
<button id="copy-btn" class="btn btn-secondary btn-sm"><i class="bi bi-clipboard me-1"></i> Copy Text</button>
<button id="download-btn" class="btn btn-primary btn-sm"><i class="bi bi-download me-1"></i> Download PDF</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -104,6 +135,7 @@
</footer> </footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script> <script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body> </body>
</html> </html>

View File

@ -47,8 +47,52 @@ $target_path = $upload_dir . $file_name;
// Move the file to the uploads directory // Move the file to the uploads directory
if (move_uploaded_file($file['tmp_name'], $target_path)) { if (move_uploaded_file($file['tmp_name'], $target_path)) {
json_response('success', 'File uploaded successfully.', ['filePath' => 'uploads/' . $file_name]); // AI Translation Step
try {
require_once __DIR__ . '/ai/LocalAIApi.php';
$source_lang = $_POST['source_lang'] ?? 'auto-detect';
$target_lang = $_POST['target_lang'] ?? 'English';
$file_path_for_ai = 'uploads/' . $file_name; // The path relative to the project root
$prompt = "You are an expert document translator. Please perform the following tasks:\n"
. "1. **OCR Extraction:** Analyze the document located at the following path: `{$file_path_for_ai}`. Extract all visible text from it.\n"
. "2. **Translation:** Translate the extracted text from `{$source_lang}` to `{$target_lang}`.\n"
. "3. **Output:** Return ONLY the translated text as a single block of plain text. Do not include any explanations, apologies, or introductory phrases. Just the translation.";
$resp = LocalAIApi::createResponse(
[
'input' => [
['role' => 'system', 'content' => 'You are a document translation service.'],
['role' => 'user', 'content' => $prompt],
],
]
);
if (!empty($resp['success'])) {
$translated_text = LocalAIApi::extractText($resp);
if ($translated_text === '') {
// Handle cases where the AI returns a non-text response or empty text
$decoded = LocalAIApi::decodeJsonFromResponse($resp);
$error_details = $decoded ? json_encode($decoded, JSON_PRETTY_PRINT) : 'AI returned an empty or invalid response.';
json_response('error', 'AI processing failed. Details: ' . $error_details);
} else {
json_response('success', 'File translated successfully.', [
'filePath' => $file_path_for_ai,
'translatedText' => $translated_text
]);
}
} else {
$error_message = $resp['error'] ?? 'Unknown AI error.';
json_response('error', 'Failed to get response from AI service: ' . $error_message);
}
} catch (Exception $e) {
json_response('error', 'An exception occurred during AI processing: ' . $e->getMessage());
}
} else { } else {
json_response('error', 'Failed to move uploaded file.'); json_response('error', 'Failed to move uploaded file.');
} }
?> ?>