amine_dubs
commited on
Commit
·
a583ad7
1
Parent(s):
b7f5097
- backend/main.py +15 -7
- static/script.js +104 -128
backend/main.py
CHANGED
|
@@ -182,14 +182,15 @@ def translate_text(text, source_lang, target_lang):
|
|
| 182 |
return use_fallback_translation(text, source_lang, target_lang)
|
| 183 |
|
| 184 |
try:
|
| 185 |
-
#
|
| 186 |
-
text_to_translate = text
|
|
|
|
| 187 |
|
| 188 |
# Use a more reliable timeout approach with concurrent.futures
|
| 189 |
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 190 |
future = executor.submit(
|
| 191 |
lambda: translator(
|
| 192 |
-
text_to_translate,
|
| 193 |
max_length=768
|
| 194 |
)[0]["translation_text"]
|
| 195 |
)
|
|
@@ -198,17 +199,17 @@ def translate_text(text, source_lang, target_lang):
|
|
| 198 |
# Set a reasonable timeout
|
| 199 |
result = future.result(timeout=10)
|
| 200 |
|
| 201 |
-
# Post-process the result for Arabic cultural adaptation
|
| 202 |
if target_lang == "ar":
|
| 203 |
result = culturally_adapt_arabic(result)
|
| 204 |
|
|
|
|
| 205 |
return result
|
| 206 |
except concurrent.futures.TimeoutError:
|
| 207 |
print(f"Model inference timed out after 10 seconds, falling back to online translation")
|
| 208 |
return use_fallback_translation(text, source_lang, target_lang)
|
| 209 |
except Exception as e:
|
| 210 |
print(f"Error during model inference: {e}")
|
| 211 |
-
|
| 212 |
# If the model failed during inference, try to re-initialize it for next time
|
| 213 |
# but use fallback for this request
|
| 214 |
initialize_model()
|
|
@@ -440,27 +441,34 @@ async def translate_document_endpoint(
|
|
| 440 |
target_lang: str = Form("ar")
|
| 441 |
):
|
| 442 |
"""Translates text extracted from an uploaded document."""
|
|
|
|
| 443 |
try:
|
| 444 |
# Extract text directly from the uploaded file
|
|
|
|
| 445 |
extracted_text = await extract_text_from_file(file)
|
| 446 |
|
| 447 |
if not extracted_text:
|
|
|
|
| 448 |
raise HTTPException(status_code=400, detail="Could not extract any text from the document.")
|
| 449 |
|
| 450 |
-
# Translate the extracted text
|
|
|
|
| 451 |
translated_text = translate_text(extracted_text, source_lang, target_lang)
|
| 452 |
|
|
|
|
| 453 |
return JSONResponse(content={
|
| 454 |
"original_filename": file.filename,
|
| 455 |
-
"detected_source_lang": source_lang,
|
| 456 |
"translated_text": translated_text
|
| 457 |
})
|
| 458 |
|
| 459 |
except HTTPException as http_exc:
|
|
|
|
| 460 |
raise http_exc
|
| 461 |
except Exception as e:
|
| 462 |
print(f"Document translation error: {e}")
|
| 463 |
traceback.print_exc()
|
|
|
|
| 464 |
raise HTTPException(status_code=500, detail=f"Document translation error: {str(e)}")
|
| 465 |
|
| 466 |
# --- Run the server (for local development) ---
|
|
|
|
| 182 |
return use_fallback_translation(text, source_lang, target_lang)
|
| 183 |
|
| 184 |
try:
|
| 185 |
+
# Ensure only the raw text is sent to the Helsinki model
|
| 186 |
+
text_to_translate = text
|
| 187 |
+
print(f"Translating text (first 50 chars): {text_to_translate[:50]}...") # Log the actual text being sent
|
| 188 |
|
| 189 |
# Use a more reliable timeout approach with concurrent.futures
|
| 190 |
with concurrent.futures.ThreadPoolExecutor() as executor:
|
| 191 |
future = executor.submit(
|
| 192 |
lambda: translator(
|
| 193 |
+
text_to_translate, # Pass only the raw text
|
| 194 |
max_length=768
|
| 195 |
)[0]["translation_text"]
|
| 196 |
)
|
|
|
|
| 199 |
# Set a reasonable timeout
|
| 200 |
result = future.result(timeout=10)
|
| 201 |
|
| 202 |
+
# Post-process the result for Arabic cultural adaptation if needed
|
| 203 |
if target_lang == "ar":
|
| 204 |
result = culturally_adapt_arabic(result)
|
| 205 |
|
| 206 |
+
print(f"Translation successful (first 50 chars): {result[:50]}...")
|
| 207 |
return result
|
| 208 |
except concurrent.futures.TimeoutError:
|
| 209 |
print(f"Model inference timed out after 10 seconds, falling back to online translation")
|
| 210 |
return use_fallback_translation(text, source_lang, target_lang)
|
| 211 |
except Exception as e:
|
| 212 |
print(f"Error during model inference: {e}")
|
|
|
|
| 213 |
# If the model failed during inference, try to re-initialize it for next time
|
| 214 |
# but use fallback for this request
|
| 215 |
initialize_model()
|
|
|
|
| 441 |
target_lang: str = Form("ar")
|
| 442 |
):
|
| 443 |
"""Translates text extracted from an uploaded document."""
|
| 444 |
+
print("[DEBUG] /translate/document endpoint called (Updated Code Check)") # Added log
|
| 445 |
try:
|
| 446 |
# Extract text directly from the uploaded file
|
| 447 |
+
print(f"[DEBUG] Processing file: {file.filename}, Source: {source_lang}, Target: {target_lang}")
|
| 448 |
extracted_text = await extract_text_from_file(file)
|
| 449 |
|
| 450 |
if not extracted_text:
|
| 451 |
+
print("[DEBUG] No text extracted from document.")
|
| 452 |
raise HTTPException(status_code=400, detail="Could not extract any text from the document.")
|
| 453 |
|
| 454 |
+
# Translate the extracted text using the updated translate_text function
|
| 455 |
+
print("[DEBUG] Calling translate_text for document content...")
|
| 456 |
translated_text = translate_text(extracted_text, source_lang, target_lang)
|
| 457 |
|
| 458 |
+
print(f"[DEBUG] Document translation successful. Returning result for {file.filename}")
|
| 459 |
return JSONResponse(content={
|
| 460 |
"original_filename": file.filename,
|
| 461 |
+
"detected_source_lang": source_lang, # Assuming source_lang is detected or provided correctly
|
| 462 |
"translated_text": translated_text
|
| 463 |
})
|
| 464 |
|
| 465 |
except HTTPException as http_exc:
|
| 466 |
+
# Re-raise HTTPExceptions directly
|
| 467 |
raise http_exc
|
| 468 |
except Exception as e:
|
| 469 |
print(f"Document translation error: {e}")
|
| 470 |
traceback.print_exc()
|
| 471 |
+
# Return a generic error response
|
| 472 |
raise HTTPException(status_code=500, detail=f"Document translation error: {str(e)}")
|
| 473 |
|
| 474 |
# --- Run the server (for local development) ---
|
static/script.js
CHANGED
|
@@ -2,142 +2,119 @@
|
|
| 2 |
window.onload = function() {
|
| 3 |
console.log('Window fully loaded, initializing translation app');
|
| 4 |
|
| 5 |
-
// Get form elements
|
| 6 |
const textTranslationForm = document.querySelector('#text-translation-form');
|
| 7 |
const docTranslationForm = document.querySelector('#doc-translation-form');
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
// Set up text translation form
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
//
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
}
|
| 55 |
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
// Debugging: Log which specific elements are missing
|
| 59 |
-
console.error('Missing elements:', {
|
| 60 |
-
textInput: textInput,
|
| 61 |
-
sourceLang: sourceLang,
|
| 62 |
-
targetLang: targetLang
|
| 63 |
-
});
|
| 64 |
-
showError('Form elements not found. Please check the HTML structure and console logs.');
|
| 65 |
-
return;
|
| 66 |
}
|
| 67 |
|
| 68 |
-
//
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
return;
|
| 73 |
}
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
if (
|
| 81 |
-
|
| 82 |
-
// Create payload
|
| 83 |
-
const payload = {
|
| 84 |
-
text: text,
|
| 85 |
-
source_lang: sourceLangValue,
|
| 86 |
-
target_lang: targetLangValue
|
| 87 |
-
};
|
| 88 |
-
|
| 89 |
-
console.log('Sending payload:', payload);
|
| 90 |
-
|
| 91 |
-
// Send API request
|
| 92 |
-
fetch('/translate/text', {
|
| 93 |
-
method: 'POST',
|
| 94 |
-
headers: { 'Content-Type': 'application/json' },
|
| 95 |
-
body: JSON.stringify(payload)
|
| 96 |
-
})
|
| 97 |
-
.then(function(response) {
|
| 98 |
-
if (!response.ok) throw new Error('Server error: ' + response.status);
|
| 99 |
-
return response.text();
|
| 100 |
-
})
|
| 101 |
-
.then(function(responseText) {
|
| 102 |
-
console.log('Response received:', responseText);
|
| 103 |
-
|
| 104 |
-
// Try to parse JSON
|
| 105 |
-
let data;
|
| 106 |
-
try {
|
| 107 |
-
data = JSON.parse(responseText);
|
| 108 |
-
} catch (error) {
|
| 109 |
-
throw new Error('Invalid response from server');
|
| 110 |
-
}
|
| 111 |
-
|
| 112 |
-
// Check for translation
|
| 113 |
-
if (!data.translated_text) {
|
| 114 |
-
throw new Error('No translation returned');
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
// Show result
|
| 118 |
-
const resultBox = document.querySelector('#text-result');
|
| 119 |
-
const outputElement = document.querySelector('#text-output');
|
| 120 |
-
|
| 121 |
-
if (resultBox && outputElement) {
|
| 122 |
-
outputElement.textContent = data.translated_text;
|
| 123 |
-
resultBox.style.display = 'block';
|
| 124 |
-
}
|
| 125 |
-
})
|
| 126 |
-
.catch(function(error) {
|
| 127 |
-
showError(error.message || 'Translation failed');
|
| 128 |
-
console.error('Error:', error);
|
| 129 |
-
})
|
| 130 |
-
.finally(function() {
|
| 131 |
-
if (loadingElement) loadingElement.style.display = 'none';
|
| 132 |
-
});
|
| 133 |
});
|
| 134 |
-
}
|
| 135 |
-
console.error('Text translation form not found in the document!');
|
| 136 |
-
}
|
| 137 |
|
| 138 |
// Document translation handler
|
| 139 |
if (docTranslationForm) {
|
| 140 |
-
console.log('Document translation form found');
|
| 141 |
docTranslationForm.addEventListener('submit', function(event) {
|
| 142 |
event.preventDefault();
|
| 143 |
console.log('Document translation form submitted');
|
|
@@ -201,16 +178,15 @@ window.onload = function() {
|
|
| 201 |
});
|
| 202 |
});
|
| 203 |
} else {
|
| 204 |
-
console.error('Document translation form not found
|
| 205 |
}
|
| 206 |
|
| 207 |
// Helper function to show errors
|
| 208 |
function showError(message) {
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
errorDiv.style.display = 'block';
|
| 213 |
}
|
| 214 |
-
console.error('Error:', message);
|
| 215 |
}
|
| 216 |
};
|
|
|
|
| 2 |
window.onload = function() {
|
| 3 |
console.log('Window fully loaded, initializing translation app');
|
| 4 |
|
| 5 |
+
// Get form elements ONCE after load
|
| 6 |
const textTranslationForm = document.querySelector('#text-translation-form');
|
| 7 |
const docTranslationForm = document.querySelector('#doc-translation-form');
|
| 8 |
|
| 9 |
+
// Get text form INPUT elements ONCE
|
| 10 |
+
const textInput = document.getElementById('text-input');
|
| 11 |
+
const sourceLangText = document.getElementById('source-lang-text');
|
| 12 |
+
const targetLangText = document.getElementById('target-lang-text');
|
| 13 |
+
const textLoadingElement = document.getElementById('text-loading');
|
| 14 |
+
const debugElement = document.getElementById('debug-info');
|
| 15 |
+
const errorElement = document.getElementById('error-message');
|
| 16 |
+
const textResultBox = document.getElementById('text-result');
|
| 17 |
+
const textOutputElement = document.getElementById('text-output');
|
| 18 |
+
|
| 19 |
+
// Check if essential text elements were found on load
|
| 20 |
+
if (!textTranslationForm || !textInput || !sourceLangText || !targetLangText) {
|
| 21 |
+
console.error('CRITICAL: Essential text form elements not found on window load!', {
|
| 22 |
+
form: !!textTranslationForm,
|
| 23 |
+
input: !!textInput,
|
| 24 |
+
source: !!sourceLangText,
|
| 25 |
+
target: !!targetLangText
|
| 26 |
+
});
|
| 27 |
+
if (errorElement) {
|
| 28 |
+
errorElement.textContent = 'Error: Could not find essential text translation form elements. Check HTML IDs.';
|
| 29 |
+
errorElement.style.display = 'block';
|
| 30 |
+
}
|
| 31 |
+
// Optionally disable the form if elements are missing
|
| 32 |
+
if (textTranslationForm) textTranslationForm.style.opacity = '0.5';
|
| 33 |
+
return; // Stop further initialization if critical elements missing
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
// Set up text translation form
|
| 37 |
+
console.log('Text translation form and elements found on load');
|
| 38 |
+
textTranslationForm.addEventListener('submit', function(event) {
|
| 39 |
+
event.preventDefault();
|
| 40 |
+
console.log('Text translation form submitted');
|
| 41 |
|
| 42 |
+
// Hide previous results/errors
|
| 43 |
+
if (textResultBox) textResultBox.style.display = 'none';
|
| 44 |
+
if (errorElement) errorElement.style.display = 'none';
|
| 45 |
+
if (debugElement) debugElement.style.display = 'none';
|
| 46 |
+
|
| 47 |
+
// **Crucial Check:** Verify elements still exist before use
|
| 48 |
+
if (!textInput || !sourceLangText || !targetLangText) {
|
| 49 |
+
console.error('ERROR: Text form elements became null before accessing value!');
|
| 50 |
+
showError('Internal error: Form elements missing unexpectedly.');
|
| 51 |
+
return;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
// Get values
|
| 55 |
+
const text = textInput.value ? textInput.value.trim() : '';
|
| 56 |
+
if (!text) {
|
| 57 |
+
showError('Please enter text to translate');
|
| 58 |
+
return;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
const sourceLangValue = sourceLangText.value;
|
| 62 |
+
const targetLangValue = targetLangText.value;
|
| 63 |
+
|
| 64 |
+
// Show loading indicator
|
| 65 |
+
if (textLoadingElement) textLoadingElement.style.display = 'block';
|
| 66 |
+
|
| 67 |
+
// Create payload
|
| 68 |
+
const payload = {
|
| 69 |
+
text: text,
|
| 70 |
+
source_lang: sourceLangValue,
|
| 71 |
+
target_lang: targetLangValue
|
| 72 |
+
};
|
| 73 |
+
|
| 74 |
+
console.log('Sending payload:', payload);
|
| 75 |
+
|
| 76 |
+
// Send API request
|
| 77 |
+
fetch('/translate/text', {
|
| 78 |
+
method: 'POST',
|
| 79 |
+
headers: { 'Content-Type': 'application/json' },
|
| 80 |
+
body: JSON.stringify(payload)
|
| 81 |
+
})
|
| 82 |
+
.then(response => {
|
| 83 |
+
if (!response.ok) throw new Error('Server error: ' + response.status);
|
| 84 |
+
return response.text(); // Get raw text first
|
| 85 |
+
})
|
| 86 |
+
.then(responseText => {
|
| 87 |
+
console.log('Response received:', responseText);
|
| 88 |
+
let data;
|
| 89 |
+
try {
|
| 90 |
+
data = JSON.parse(responseText);
|
| 91 |
+
} catch (error) {
|
| 92 |
+
console.error("Failed to parse JSON:", responseText);
|
| 93 |
+
throw new Error('Invalid response format from server');
|
| 94 |
}
|
| 95 |
|
| 96 |
+
if (!data.success || !data.translated_text) {
|
| 97 |
+
throw new Error(data.error || 'No translation returned or success flag false');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
}
|
| 99 |
|
| 100 |
+
// Show result
|
| 101 |
+
if (textResultBox && textOutputElement) {
|
| 102 |
+
textOutputElement.textContent = data.translated_text;
|
| 103 |
+
textResultBox.style.display = 'block';
|
|
|
|
| 104 |
}
|
| 105 |
+
})
|
| 106 |
+
.catch(error => {
|
| 107 |
+
showError(error.message || 'Translation failed');
|
| 108 |
+
console.error('Error:', error);
|
| 109 |
+
})
|
| 110 |
+
.finally(() => {
|
| 111 |
+
if (textLoadingElement) textLoadingElement.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
});
|
| 113 |
+
});
|
|
|
|
|
|
|
| 114 |
|
| 115 |
// Document translation handler
|
| 116 |
if (docTranslationForm) {
|
| 117 |
+
console.log('Document translation form found on load');
|
| 118 |
docTranslationForm.addEventListener('submit', function(event) {
|
| 119 |
event.preventDefault();
|
| 120 |
console.log('Document translation form submitted');
|
|
|
|
| 178 |
});
|
| 179 |
});
|
| 180 |
} else {
|
| 181 |
+
console.error('Document translation form not found on load!');
|
| 182 |
}
|
| 183 |
|
| 184 |
// Helper function to show errors
|
| 185 |
function showError(message) {
|
| 186 |
+
if (errorElement) {
|
| 187 |
+
errorElement.textContent = 'Error: ' + message;
|
| 188 |
+
errorElement.style.display = 'block';
|
|
|
|
| 189 |
}
|
| 190 |
+
console.error('Error displayed:', message);
|
| 191 |
}
|
| 192 |
};
|