amine_dubs commited on
Commit
443ca56
·
1 Parent(s): bf7a078
Files changed (2) hide show
  1. backend/main.py +57 -36
  2. static/script.js +199 -217
backend/main.py CHANGED
@@ -87,8 +87,8 @@ def initialize_model():
87
  try:
88
  print(f"Initializing model and tokenizer (attempt {model_initialization_attempts})...")
89
 
90
- # Use a better translation model that handles multilingual tasks well
91
- model_name = "facebook/nllb-200-distilled-600M" # Better multilingual translation model
92
 
93
  # Check for available device - properly detect CPU/GPU
94
  device = "cpu" # Default to CPU which is more reliable
@@ -99,15 +99,20 @@ def initialize_model():
99
 
100
  # Load the tokenizer with explicit cache directory
101
  print(f"Loading tokenizer from {model_name}...")
102
- tokenizer = AutoTokenizer.from_pretrained(
103
- model_name,
104
- cache_dir="/tmp/transformers_cache",
105
- use_fast=True # Use faster tokenizer when possible
106
- )
107
- if tokenizer is None:
108
- print("Failed to load tokenizer")
 
 
 
 
 
 
109
  return False
110
- print("Tokenizer loaded successfully")
111
 
112
  # Load the model with explicit device placement
113
  print(f"Loading model from {model_name}...")
@@ -143,8 +148,7 @@ def initialize_model():
143
  return False
144
 
145
  # Test the model with a simple translation to verify it works
146
- # NLLB needs language codes in format like "eng_Latn" and "ara_Arab"
147
- test_result = translator("hello", src_lang="eng_Latn", tgt_lang="ara_Arab", max_length=128)
148
  print(f"Model test result: {test_result}")
149
  if not test_result or not isinstance(test_result, list) or len(test_result) == 0:
150
  print("Model test failed: Invalid output format")
@@ -194,25 +198,19 @@ Text to translate:
194
  else:
195
  # For non-Arabic target languages, use standard approach
196
  prompt = text
197
-
198
- # Prepare input with explicit instruction format for better results with NLLB
199
- src_lang_code = f"{source_lang}_Latn" if source_lang != "ar" else f"{source_lang}_Arab"
200
- tgt_lang_code = f"{target_lang}_Latn" if target_lang != "ar" else f"{target_lang}_Arab"
201
 
202
  # Use a more reliable timeout approach with concurrent.futures
203
  with concurrent.futures.ThreadPoolExecutor() as executor:
204
  future = executor.submit(
205
  lambda: translator(
206
  prompt, # Using our enhanced prompt instead of raw text
207
- src_lang=src_lang_code,
208
- tgt_lang=tgt_lang_code,
209
  max_length=768 # Increased max_length to accommodate longer prompt
210
  )[0]["translation_text"]
211
  )
212
 
213
  try:
214
- # Set a reasonable timeout (15 seconds instead of 10)
215
- result = future.result(timeout=15)
216
 
217
  # Post-process the result for Arabic cultural adaptation
218
  if target_lang == "ar":
@@ -220,7 +218,7 @@ Text to translate:
220
 
221
  return result
222
  except concurrent.futures.TimeoutError:
223
- print(f"Model inference timed out after 15 seconds, falling back to online translation")
224
  return use_fallback_translation(text, source_lang, target_lang)
225
  except Exception as e:
226
  print(f"Error during model inference: {e}")
@@ -265,7 +263,7 @@ def check_and_reinitialize_model():
265
 
266
  # Test the existing model with a simple translation
267
  test_text = "hello"
268
- result = translator(test_text, src_lang="eng_Latn", tgt_lang="fra_Latn", max_length=128)
269
 
270
  # If we got a valid result, model is working fine
271
  if result and isinstance(result, list) and len(result) > 0:
@@ -281,15 +279,30 @@ def check_and_reinitialize_model():
281
 
282
  def use_fallback_translation(text, source_lang, target_lang):
283
  """Use various fallback online translation services."""
284
- # List of LibreTranslate servers to try in order
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  libre_servers = [
286
  "https://translate.terraprint.co/translate",
287
  "https://libretranslate.de/translate",
288
  "https://translate.argosopentech.com/translate",
289
- "https://translate.fedilab.app/translate" # Added additional server
 
290
  ]
291
 
292
- # Try each LibreTranslate server
293
  for server in libre_servers:
294
  try:
295
  print(f"Attempting fallback translation using LibreTranslate: {server}")
@@ -302,30 +315,38 @@ def use_fallback_translation(text, source_lang, target_lang):
302
  "target": target_lang
303
  }
304
 
305
- # Use a shorter timeout for the request (5 seconds instead of 10)
306
- response = requests.post(server, json=payload, headers=headers, timeout=5)
307
 
308
  if response.status_code == 200:
309
  result = response.json()
310
  if "translatedText" in result:
 
311
  return result["translatedText"]
312
  except Exception as e:
313
  print(f"Error with LibreTranslate {server}: {str(e)}")
314
  continue
315
 
316
- # If all LibreTranslate servers fail, try Google Translate API with a wrapper
317
- # that doesn't need an API key for limited usage
318
  try:
319
- print("Attempting fallback with Google Translate (no API key)")
320
- from googletrans import Translator
321
- google_translator = Translator()
322
- result = google_translator.translate(text, src=source_lang, dest=target_lang)
323
- return result.text
 
 
 
 
 
 
 
324
  except Exception as e:
325
- print(f"Error with Google Translate fallback: {str(e)}")
326
 
327
  # Final fallback - return original text with error message
328
- return f"[Translation failed] {text}"
 
329
 
330
  # --- Helper Functions ---
331
  async def extract_text_from_file(file: UploadFile) -> str:
 
87
  try:
88
  print(f"Initializing model and tokenizer (attempt {model_initialization_attempts})...")
89
 
90
+ # Use a smaller, faster model
91
+ model_name = "Helsinki-NLP/opus-mt-en-ar" # Much smaller English-to-Arabic model
92
 
93
  # Check for available device - properly detect CPU/GPU
94
  device = "cpu" # Default to CPU which is more reliable
 
99
 
100
  # Load the tokenizer with explicit cache directory
101
  print(f"Loading tokenizer from {model_name}...")
102
+ try:
103
+ tokenizer = AutoTokenizer.from_pretrained(
104
+ model_name,
105
+ cache_dir="/tmp/transformers_cache",
106
+ use_fast=True,
107
+ local_files_only=False
108
+ )
109
+ if tokenizer is None:
110
+ print("Failed to load tokenizer")
111
+ return False
112
+ print("Tokenizer loaded successfully")
113
+ except Exception as e:
114
+ print(f"Error loading tokenizer: {e}")
115
  return False
 
116
 
117
  # Load the model with explicit device placement
118
  print(f"Loading model from {model_name}...")
 
148
  return False
149
 
150
  # Test the model with a simple translation to verify it works
151
+ test_result = translator("hello world", max_length=128)
 
152
  print(f"Model test result: {test_result}")
153
  if not test_result or not isinstance(test_result, list) or len(test_result) == 0:
154
  print("Model test failed: Invalid output format")
 
198
  else:
199
  # For non-Arabic target languages, use standard approach
200
  prompt = text
 
 
 
 
201
 
202
  # Use a more reliable timeout approach with concurrent.futures
203
  with concurrent.futures.ThreadPoolExecutor() as executor:
204
  future = executor.submit(
205
  lambda: translator(
206
  prompt, # Using our enhanced prompt instead of raw text
 
 
207
  max_length=768 # Increased max_length to accommodate longer prompt
208
  )[0]["translation_text"]
209
  )
210
 
211
  try:
212
+ # Set a reasonable timeout (10 seconds instead of 15)
213
+ result = future.result(timeout=10)
214
 
215
  # Post-process the result for Arabic cultural adaptation
216
  if target_lang == "ar":
 
218
 
219
  return result
220
  except concurrent.futures.TimeoutError:
221
+ print(f"Model inference timed out after 10 seconds, falling back to online translation")
222
  return use_fallback_translation(text, source_lang, target_lang)
223
  except Exception as e:
224
  print(f"Error during model inference: {e}")
 
263
 
264
  # Test the existing model with a simple translation
265
  test_text = "hello"
266
+ result = translator(test_text, max_length=128)
267
 
268
  # If we got a valid result, model is working fine
269
  if result and isinstance(result, list) and len(result) > 0:
 
279
 
280
  def use_fallback_translation(text, source_lang, target_lang):
281
  """Use various fallback online translation services."""
282
+ print("Using fallback translation...")
283
+
284
+ # Try Google Translate API with a wrapper first (most reliable)
285
+ try:
286
+ print("Attempting fallback with Google Translate (no API key)")
287
+ from googletrans import Translator
288
+ google_translator = Translator(service_urls=['translate.google.com', 'translate.google.co.kr'])
289
+ result = google_translator.translate(text, src=source_lang, dest=target_lang)
290
+ if result and result.text:
291
+ print("Google Translate successful!")
292
+ return result.text
293
+ except Exception as e:
294
+ print(f"Error with Google Translate fallback: {str(e)}")
295
+
296
+ # List of LibreTranslate servers to try with increased timeout
297
  libre_servers = [
298
  "https://translate.terraprint.co/translate",
299
  "https://libretranslate.de/translate",
300
  "https://translate.argosopentech.com/translate",
301
+ "https://translate.fedilab.app/translate",
302
+ "https://trans.zillyhuhn.com/translate" # Additional server
303
  ]
304
 
305
+ # Try each LibreTranslate server with increased timeout
306
  for server in libre_servers:
307
  try:
308
  print(f"Attempting fallback translation using LibreTranslate: {server}")
 
315
  "target": target_lang
316
  }
317
 
318
+ # Use a longer timeout for the request (8 seconds instead of 5)
319
+ response = requests.post(server, json=payload, headers=headers, timeout=8)
320
 
321
  if response.status_code == 200:
322
  result = response.json()
323
  if "translatedText" in result:
324
+ print(f"LibreTranslate successful using {server}")
325
  return result["translatedText"]
326
  except Exception as e:
327
  print(f"Error with LibreTranslate {server}: {str(e)}")
328
  continue
329
 
330
+ # Try MyMemory as another fallback
 
331
  try:
332
+ print("Attempting fallback with MyMemory Translation API")
333
+ url = "https://api.mymemory.translated.net/get"
334
+ params = {
335
+ "q": text,
336
+ "langpair": f"{source_lang}|{target_lang}",
337
+ }
338
+ response = requests.get(url, params=params, timeout=10)
339
+ if response.status_code == 200:
340
+ data = response.json()
341
+ if data and data.get("responseData") and data["responseData"].get("translatedText"):
342
+ print("MyMemory translation successful!")
343
+ return data["responseData"]["translatedText"]
344
  except Exception as e:
345
+ print(f"Error with MyMemory fallback: {str(e)}")
346
 
347
  # Final fallback - return original text with error message
348
+ print("All translation services failed. Returning error message.")
349
+ return f"[Translation services unavailable] {text}"
350
 
351
  # --- Helper Functions ---
352
  async def extract_text_from_file(file: UploadFile) -> str:
static/script.js CHANGED
@@ -1,235 +1,217 @@
1
- document.addEventListener('DOMContentLoaded', () => {
2
- console.log("Translation app initialized");
 
3
 
4
- // Get form elements - only get the base forms on page load
5
- const textForm = document.getElementById('text-translation-form');
6
- const docForm = document.getElementById('doc-translation-form');
7
 
8
- // Simple function to show errors
9
- function showError(message) {
10
- const errorDiv = document.getElementById('error-message');
11
- if (errorDiv) {
12
- errorDiv.textContent = "Error: " + message;
13
- errorDiv.style.display = 'block';
14
- console.error("Error:", message);
15
- } else {
16
- alert("Error: " + message);
17
- console.error("Error div not found. Error:", message);
18
- }
19
  }
20
 
21
- // Simple function to show debug information
22
- function showDebug(message) {
23
- console.log("DEBUG:", message);
24
- const debugDiv = document.getElementById('debug-info');
25
- if (debugDiv) {
26
- debugDiv.textContent = typeof message === 'string' ? message : JSON.stringify(message, null, 2);
27
- debugDiv.style.display = 'block';
28
- }
29
  }
30
 
31
- // Clear all feedback elements
32
- function clearFeedback() {
33
- const elements = ['error-message', 'debug-info', 'text-result'];
34
- elements.forEach(id => {
35
- const el = document.getElementById(id);
36
- if (el) {
37
- el.style.display = 'none';
38
- if (id !== 'text-result') el.textContent = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
40
- });
41
- }
42
-
43
- // Add text form submission handler
44
- if (textForm) {
45
- textForm.addEventListener('submit', function(e) {
46
- e.preventDefault();
47
- clearFeedback();
48
 
 
49
  try {
50
- // Find form elements when needed, not earlier
51
- const textInput = document.getElementById('text-input');
52
- const sourceLang = document.getElementById('source-lang-text');
53
- const targetLang = document.getElementById('target-lang-text');
54
- const textLoading = document.getElementById('text-loading');
55
-
56
- // Debug which elements were found/not found
57
- const foundElements = {
58
- textInput: !!textInput,
59
- sourceLang: !!sourceLang,
60
- targetLang: !!targetLang,
61
- textLoading: !!textLoading
62
- };
63
- showDebug("Found elements: " + JSON.stringify(foundElements));
64
-
65
- // Check if elements exist
66
- if (!textInput || !sourceLang || !targetLang) {
67
- showError("Required form elements not found");
68
- return;
69
- }
70
-
71
- // Get values safely
72
- const text = textInput.value ? textInput.value.trim() : '';
73
- if (!text) {
74
- showError("Please enter text to translate");
75
- return;
76
- }
77
-
78
- // Show loading state if element exists
79
- if (textLoading) textLoading.style.display = 'block';
80
-
81
- // Prepare request data
82
- const requestData = {
83
- text: text,
84
- source_lang: sourceLang.value,
85
- target_lang: targetLang.value
86
- };
87
-
88
- // Debug the request data
89
- showDebug("Sending request: " + JSON.stringify(requestData));
90
-
91
- // Use simple promise then/catch for better browser compatibility
92
- fetch('/translate/text', {
93
- method: 'POST',
94
- headers: {
95
- 'Content-Type': 'application/json'
96
- },
97
- body: JSON.stringify(requestData)
98
- })
99
- .then(response => {
100
- // First check if response is ok
101
- if (!response.ok) {
102
- throw new Error('Server returned ' + response.status);
103
- }
104
- return response.text();
105
- })
106
- .then(responseText => {
107
- // Parse the response text
108
- showDebug("Got response: " + responseText);
109
-
110
- let data;
111
- try {
112
- data = JSON.parse(responseText);
113
- } catch (err) {
114
- throw new Error('Invalid JSON response');
115
- }
116
-
117
- // Check for translation result
118
- if (data && data.translated_text) {
119
- // Display the result
120
- const resultBox = document.getElementById('text-result');
121
- const outputEl = document.getElementById('text-output');
122
-
123
- if (resultBox && outputEl) {
124
- outputEl.textContent = data.translated_text;
125
- resultBox.style.display = 'block';
126
- } else {
127
- throw new Error('Result display elements not found');
128
- }
129
- } else {
130
- throw new Error(data.error || 'No translation result returned');
131
- }
132
- })
133
- .catch(error => {
134
- showError(error.message || "Translation failed");
135
- console.error("Translation error:", error);
136
- })
137
- .finally(() => {
138
- // Hide loading indicator
139
- if (textLoading) textLoading.style.display = 'none';
140
- });
141
-
142
  } catch (error) {
143
- showError(error.message || "An unexpected error occurred");
144
- console.error("General error:", error);
145
-
146
- // Ensure loading indicator is hidden
147
- const textLoading = document.getElementById('text-loading');
148
- if (textLoading) textLoading.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  }
150
  });
151
- } else {
152
- console.error("Text translation form not found!");
153
  }
154
-
155
- // Document translation handler with similar approach
156
- if (docForm) {
157
- docForm.addEventListener('submit', function(e) {
158
- e.preventDefault();
159
- clearFeedback();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
- try {
162
- const fileInput = document.getElementById('doc-input');
163
- const loadingIndicator = document.getElementById('doc-loading');
164
-
165
- if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
166
- showError("Please select a document to upload");
167
- return;
168
- }
169
-
170
- if (loadingIndicator) loadingIndicator.style.display = 'block';
171
-
172
- // Create form data
173
- const formData = new FormData(docForm);
174
-
175
- fetch('/translate/document', {
176
- method: 'POST',
177
- body: formData
178
- })
179
- .then(response => {
180
- if (!response.ok) throw new Error('Server returned ' + response.status);
181
- return response.text();
182
- })
183
- .then(responseText => {
184
- showDebug("Document response: " + responseText);
185
-
186
- let data;
187
- try {
188
- data = JSON.parse(responseText);
189
- } catch (err) {
190
- throw new Error('Invalid JSON response');
191
- }
192
-
193
- if (!data || !data.translated_text) {
194
- throw new Error('No translation returned');
195
- }
196
-
197
- // Display result
198
- const resultBox = document.getElementById('doc-result');
199
- const outputEl = document.getElementById('doc-output');
200
- const filenameEl = document.getElementById('doc-filename');
201
- const sourceLangEl = document.getElementById('doc-source-lang');
202
-
203
- if (resultBox && outputEl) {
204
- if (filenameEl) filenameEl.textContent = data.original_filename || 'N/A';
205
- if (sourceLangEl) sourceLangEl.textContent = data.detected_source_lang || 'N/A';
206
- outputEl.textContent = data.translated_text;
207
- resultBox.style.display = 'block';
208
- } else {
209
- throw new Error('Result display elements not found');
210
- }
211
- })
212
- .catch(error => {
213
- showError(error.message || "Document translation failed");
214
- })
215
- .finally(() => {
216
- if (loadingIndicator) loadingIndicator.style.display = 'none';
217
- const button = docForm.querySelector('button');
218
- if (button) {
219
- button.disabled = false;
220
- button.textContent = 'Translate Document';
221
- }
222
- });
223
-
224
- } catch (error) {
225
- showError(error.message || "An unexpected error occurred");
226
- console.error("Document error:", error);
227
-
228
- const loadingIndicator = document.getElementById('doc-loading');
229
- if (loadingIndicator) loadingIndicator.style.display = 'none';
230
  }
231
  });
232
- } else {
233
- console.error("Document translation form not found!");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  }
235
  });
 
1
+ // Wait for the DOM to be fully loaded before attaching event handlers
2
+ document.addEventListener('DOMContentLoaded', function() {
3
+ console.log('DOM fully loaded, initializing translation app');
4
 
5
+ // Get form elements
6
+ const textTranslationForm = document.getElementById('text-translation-form');
7
+ const docTranslationForm = document.getElementById('doc-translation-form');
8
 
9
+ // Set up text translation form
10
+ if (textTranslationForm) {
11
+ console.log('Text translation form found');
12
+ textTranslationForm.onsubmit = handleTextTranslation;
13
+ } else {
14
+ console.error('Text translation form not found!');
 
 
 
 
 
15
  }
16
 
17
+ // Set up document translation form
18
+ if (docTranslationForm) {
19
+ console.log('Document translation form found');
20
+ docTranslationForm.onsubmit = handleDocTranslation;
21
+ } else {
22
+ console.error('Document translation form not found!');
 
 
23
  }
24
 
25
+ // Function to handle text translation
26
+ function handleTextTranslation(event) {
27
+ event.preventDefault();
28
+ console.log('Text translation form submitted');
29
+
30
+ // Clear previous results and errors
31
+ hideElement('text-result');
32
+ hideElement('error-message');
33
+ hideElement('debug-info');
34
+
35
+ // Get form elements
36
+ const textInputElement = document.getElementById('text-input');
37
+ const sourceLangElement = document.getElementById('source-lang-text');
38
+ const targetLangElement = document.getElementById('target-lang-text');
39
+ const loadingElement = document.getElementById('text-loading');
40
+ const debugElement = document.getElementById('debug-info');
41
+
42
+ // Debug which elements were found
43
+ let debug = 'Found elements: ';
44
+ debug += textInputElement ? 'text-input ✓ ' : 'text-input ✗ ';
45
+ debug += sourceLangElement ? 'source-lang-text ✓ ' : 'source-lang-text ✗ ';
46
+ debug += targetLangElement ? 'target-lang-text ✓ ' : 'target-lang-text ✗ ';
47
+ console.log(debug);
48
+
49
+ // Show debug info
50
+ if (debugElement) {
51
+ debugElement.textContent = debug;
52
+ debugElement.style.display = 'block';
53
+ }
54
+
55
+ // Check for missing elements
56
+ if (!textInputElement || !sourceLangElement || !targetLangElement) {
57
+ showError('One or more form elements are missing');
58
+ return;
59
+ }
60
+
61
+ // Get text input
62
+ const textInput = textInputElement.value ? textInputElement.value.trim() : '';
63
+ if (!textInput) {
64
+ showError('Please enter text to translate');
65
+ return;
66
+ }
67
+
68
+ // Get language selections
69
+ const sourceLang = sourceLangElement.value;
70
+ const targetLang = targetLangElement.value;
71
+
72
+ // Show loading indicator
73
+ if (loadingElement) {
74
+ loadingElement.style.display = 'block';
75
+ }
76
+
77
+ // Create request payload
78
+ const payload = {
79
+ text: textInput,
80
+ source_lang: sourceLang,
81
+ target_lang: targetLang
82
+ };
83
+
84
+ // Make the API request
85
+ fetch('/translate/text', {
86
+ method: 'POST',
87
+ headers: {
88
+ 'Content-Type': 'application/json',
89
+ },
90
+ body: JSON.stringify(payload)
91
+ })
92
+ .then(function(response) {
93
+ if (!response.ok) {
94
+ throw new Error(`Server returned ${response.status}: ${response.statusText}`);
95
+ }
96
+ return response.text();
97
+ })
98
+ .then(function(responseText) {
99
+ // Show response in debug
100
+ if (debugElement) {
101
+ debugElement.textContent += '\n\nResponse: ' + responseText;
102
  }
 
 
 
 
 
 
 
 
103
 
104
+ let data;
105
  try {
106
+ data = JSON.parse(responseText);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  } catch (error) {
108
+ throw new Error('Invalid JSON response from server');
109
+ }
110
+
111
+ if (!data.translated_text && !data.success) {
112
+ throw new Error(data.error || 'No translation returned');
113
+ }
114
+
115
+ // Show the result
116
+ const resultBox = document.getElementById('text-result');
117
+ const outputElement = document.getElementById('text-output');
118
+ if (resultBox && outputElement) {
119
+ outputElement.textContent = data.translated_text;
120
+ resultBox.style.display = 'block';
121
+ }
122
+ })
123
+ .catch(function(error) {
124
+ showError(error.message);
125
+ console.error('Translation error:', error);
126
+ })
127
+ .finally(function() {
128
+ // Hide loading indicator
129
+ if (loadingElement) {
130
+ loadingElement.style.display = 'none';
131
  }
132
  });
 
 
133
  }
134
+
135
+ // Function to handle document translation
136
+ function handleDocTranslation(event) {
137
+ event.preventDefault();
138
+ console.log('Document translation form submitted');
139
+
140
+ // Clear previous results and errors
141
+ hideElement('doc-result');
142
+ hideElement('error-message');
143
+
144
+ // Get form elements
145
+ const fileInput = document.getElementById('doc-input');
146
+ const loadingIndicator = document.getElementById('doc-loading');
147
+
148
+ if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
149
+ showError('Please select a document to upload');
150
+ return;
151
+ }
152
+
153
+ // Show loading indicator
154
+ if (loadingIndicator) {
155
+ loadingIndicator.style.display = 'block';
156
+ }
157
+
158
+ // Create form data
159
+ const formData = new FormData(docTranslationForm);
160
+
161
+ // Make API request
162
+ fetch('/translate/document', {
163
+ method: 'POST',
164
+ body: formData
165
+ })
166
+ .then(function(response) {
167
+ if (!response.ok) {
168
+ throw new Error(`Server returned ${response.status}`);
169
+ }
170
+ return response.json();
171
+ })
172
+ .then(function(data) {
173
+ if (!data.translated_text) {
174
+ throw new Error('No translation returned');
175
+ }
176
 
177
+ // Display result
178
+ const resultBox = document.getElementById('doc-result');
179
+ const outputEl = document.getElementById('doc-output');
180
+ const filenameEl = document.getElementById('doc-filename');
181
+ const sourceLangEl = document.getElementById('doc-source-lang');
182
+
183
+ if (resultBox && outputEl) {
184
+ if (filenameEl) filenameEl.textContent = data.original_filename || 'N/A';
185
+ if (sourceLangEl) sourceLangEl.textContent = data.detected_source_lang || 'N/A';
186
+ outputEl.textContent = data.translated_text;
187
+ resultBox.style.display = 'block';
188
+ }
189
+ })
190
+ .catch(function(error) {
191
+ showError(error.message);
192
+ })
193
+ .finally(function() {
194
+ if (loadingIndicator) {
195
+ loadingIndicator.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  }
197
  });
198
+ }
199
+
200
+ // Helper function to show errors
201
+ function showError(message) {
202
+ const errorDiv = document.getElementById('error-message');
203
+ if (errorDiv) {
204
+ errorDiv.textContent = 'Error: ' + message;
205
+ errorDiv.style.display = 'block';
206
+ }
207
+ console.error('Error:', message);
208
+ }
209
+
210
+ // Helper function to hide elements
211
+ function hideElement(id) {
212
+ const element = document.getElementById(id);
213
+ if (element) {
214
+ element.style.display = 'none';
215
+ }
216
  }
217
  });