Auto commit: 2025-11-20T12:17:08.078Z
This commit is contained in:
parent
2c882d18a4
commit
28bed0ead6
@ -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.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
34
index.php
34
index.php
@ -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>
|
||||||
46
upload.php
46
upload.php
@ -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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
Loading…
x
Reference in New Issue
Block a user