|
|
|
window.onload = function() { |
|
console.log('Window fully loaded, initializing Tarjama app'); |
|
|
|
|
|
const textTabLink = document.querySelector('nav ul li a[href="#text-translation"]'); |
|
const docTabLink = document.querySelector('nav ul li a[href="#document-translation"]'); |
|
const textSection = document.getElementById('text-translation'); |
|
const docSection = document.getElementById('document-translation'); |
|
|
|
|
|
const textTranslationForm = document.getElementById('text-translation-form'); |
|
const docTranslationForm = document.getElementById('doc-translation-form'); |
|
|
|
|
|
const textInput = document.getElementById('text-input'); |
|
const textResult = document.getElementById('text-result'); |
|
const docResult = document.getElementById('doc-result'); |
|
const textOutput = document.getElementById('text-output'); |
|
const docOutput = document.getElementById('doc-output'); |
|
const docInputText = document.getElementById('doc-input-text'); |
|
const textLoadingIndicator = document.getElementById('text-loading'); |
|
const docLoadingIndicator = document.getElementById('doc-loading'); |
|
const errorMessageElement = document.getElementById('error-message'); |
|
const notificationElement = document.getElementById('notification'); |
|
const charCountElement = document.getElementById('char-count'); |
|
const fileNameDisplay = document.getElementById('file-name-display'); |
|
const docFilename = document.getElementById('doc-filename'); |
|
const docSourceLang = document.getElementById('doc-source-lang'); |
|
|
|
|
|
const sourceLangText = document.getElementById('source-lang-text'); |
|
const targetLangText = document.getElementById('target-lang-text'); |
|
const sourceLangDoc = document.getElementById('source-lang-doc'); |
|
const targetLangDoc = document.getElementById('target-lang-doc'); |
|
|
|
|
|
const swapLanguages = document.getElementById('swap-languages'); |
|
const swapLanguagesDoc = document.getElementById('swap-languages-doc'); |
|
const copyTranslation = document.getElementById('copy-translation'); |
|
const copyDocTranslation = document.getElementById('copy-doc-translation'); |
|
const clearSource = document.getElementById('clear-source'); |
|
|
|
|
|
const phraseButtons = document.querySelectorAll('.phrase-btn'); |
|
|
|
|
|
const rtlLanguages = ['ar', 'he']; |
|
|
|
|
|
if (textTabLink && docTabLink && textSection && docSection) { |
|
textTabLink.addEventListener('click', function(e) { |
|
e.preventDefault(); |
|
docSection.classList.add('hidden'); |
|
textSection.classList.remove('hidden'); |
|
textTabLink.parentElement.classList.add('active'); |
|
docTabLink.parentElement.classList.remove('active'); |
|
}); |
|
|
|
docTabLink.addEventListener('click', function(e) { |
|
e.preventDefault(); |
|
textSection.classList.add('hidden'); |
|
docSection.classList.remove('hidden'); |
|
docTabLink.parentElement.classList.add('active'); |
|
textTabLink.parentElement.classList.remove('active'); |
|
}); |
|
} |
|
|
|
|
|
if (textInput && charCountElement) { |
|
textInput.addEventListener('input', function() { |
|
const charCount = textInput.value.length; |
|
charCountElement.textContent = charCount; |
|
|
|
|
|
if (charCount > 3000) { |
|
charCountElement.className = 'char-count-warning'; |
|
} else if (charCount > 2000) { |
|
charCountElement.className = 'char-count-approaching'; |
|
} else { |
|
charCountElement.className = ''; |
|
} |
|
}); |
|
} |
|
|
|
|
|
if (phraseButtons && phraseButtons.length > 0) { |
|
console.log('Found phrase buttons:', phraseButtons.length); |
|
phraseButtons.forEach(button => { |
|
button.addEventListener('click', function(e) { |
|
e.preventDefault(); |
|
console.log('Phrase button clicked'); |
|
const phrase = this.getAttribute('data-text') || this.getAttribute('data-phrase'); |
|
|
|
if (phrase && textInput) { |
|
console.log('Using phrase:', phrase); |
|
|
|
if (textSection.classList.contains('hidden')) { |
|
console.log('Switching to text tab'); |
|
docSection.classList.add('hidden'); |
|
textSection.classList.remove('hidden'); |
|
textTabLink.parentElement.classList.add('active'); |
|
docTabLink.parentElement.classList.remove('active'); |
|
} |
|
|
|
|
|
if (typeof textInput.selectionStart === 'number') { |
|
const startPos = textInput.selectionStart; |
|
const endPos = textInput.selectionEnd; |
|
const currentValue = textInput.value; |
|
const spaceChar = currentValue && currentValue[startPos - 1] !== ' ' && startPos > 0 ? ' ' : ''; |
|
|
|
|
|
textInput.value = currentValue.substring(0, startPos) + |
|
spaceChar + phrase + |
|
currentValue.substring(endPos); |
|
|
|
|
|
textInput.selectionStart = startPos + phrase.length + spaceChar.length; |
|
textInput.selectionEnd = textInput.selectionStart; |
|
} else { |
|
|
|
const currentValue = textInput.value; |
|
const spaceChar = currentValue && currentValue.length > 0 && currentValue[currentValue.length - 1] !== ' ' ? ' ' : ''; |
|
textInput.value += spaceChar + phrase; |
|
} |
|
|
|
|
|
const inputEvent = new Event('input', { bubbles: true }); |
|
textInput.dispatchEvent(inputEvent); |
|
|
|
|
|
textInput.focus(); |
|
|
|
|
|
const autoTranslate = this.getAttribute('data-auto-translate') === 'true'; |
|
if (autoTranslate && textTranslationForm) { |
|
textTranslationForm.dispatchEvent(new Event('submit')); |
|
} |
|
} |
|
}); |
|
}); |
|
} |
|
|
|
|
|
if (swapLanguages && sourceLangText && targetLangText) { |
|
swapLanguages.addEventListener('click', function(e) { |
|
e.preventDefault(); |
|
|
|
|
|
if (sourceLangText.value === 'auto') { |
|
showNotification('Cannot swap when source language is set to auto-detect.'); |
|
return; |
|
} |
|
|
|
|
|
const sourceValue = sourceLangText.value; |
|
const targetValue = targetLangText.value; |
|
|
|
|
|
sourceLangText.value = targetValue; |
|
targetLangText.value = sourceValue; |
|
|
|
|
|
if (textOutput.textContent.trim() !== '') { |
|
|
|
textInput.value = textOutput.textContent; |
|
|
|
|
|
const inputEvent = new Event('input', { bubbles: true }); |
|
textInput.dispatchEvent(inputEvent); |
|
|
|
|
|
if (textTranslationForm) { |
|
textTranslationForm.dispatchEvent(new Event('submit')); |
|
} |
|
} |
|
|
|
|
|
applyRtlStyling(sourceLangText.value, textInput); |
|
applyRtlStyling(targetLangText.value, textOutput); |
|
}); |
|
} |
|
|
|
|
|
if (swapLanguagesDoc && sourceLangDoc && targetLangDoc) { |
|
swapLanguagesDoc.addEventListener('click', function(e) { |
|
e.preventDefault(); |
|
|
|
|
|
if (sourceLangDoc.value === 'auto') { |
|
showNotification('Cannot swap when source language is set to auto-detect.'); |
|
return; |
|
} |
|
|
|
|
|
const sourceValue = sourceLangDoc.value; |
|
const targetValue = targetLangDoc.value; |
|
|
|
|
|
sourceLangDoc.value = targetValue; |
|
targetLangDoc.value = sourceValue; |
|
}); |
|
} |
|
|
|
|
|
function applyRtlStyling(langCode, element) { |
|
if (element) { |
|
if (rtlLanguages.includes(langCode)) { |
|
element.style.direction = 'rtl'; |
|
element.style.textAlign = 'right'; |
|
} else { |
|
element.style.direction = 'ltr'; |
|
element.style.textAlign = 'left'; |
|
} |
|
} |
|
} |
|
|
|
|
|
function handleLanguageChange() { |
|
|
|
if (sourceLangText && textInput) { |
|
applyRtlStyling(sourceLangText.value, textInput); |
|
} |
|
|
|
|
|
if (targetLangText && textOutput) { |
|
applyRtlStyling(targetLangText.value, textOutput); |
|
} |
|
|
|
if (sourceLangDoc && docInputText) { |
|
applyRtlStyling(sourceLangDoc.value, docInputText); |
|
} |
|
|
|
if (targetLangDoc && docOutput) { |
|
applyRtlStyling(targetLangDoc.value, docOutput); |
|
} |
|
} |
|
|
|
|
|
if (sourceLangText) sourceLangText.addEventListener('change', handleLanguageChange); |
|
if (targetLangText) targetLangText.addEventListener('change', handleLanguageChange); |
|
if (sourceLangDoc) sourceLangDoc.addEventListener('change', handleLanguageChange); |
|
if (targetLangDoc) targetLangDoc.addEventListener('change', handleLanguageChange); |
|
|
|
|
|
if (copyTranslation) { |
|
copyTranslation.addEventListener('click', function() { |
|
if (textOutput && textOutput.textContent.trim() !== '') { |
|
navigator.clipboard.writeText(textOutput.textContent) |
|
.then(() => { |
|
showNotification('Translation copied to clipboard!'); |
|
}) |
|
.catch(err => { |
|
console.error('Error copying text: ', err); |
|
showNotification('Failed to copy text. Please try again.'); |
|
}); |
|
} |
|
}); |
|
} |
|
|
|
|
|
if (copyDocTranslation) { |
|
copyDocTranslation.addEventListener('click', function() { |
|
if (docOutput && docOutput.textContent.trim() !== '') { |
|
navigator.clipboard.writeText(docOutput.textContent) |
|
.then(() => { |
|
showNotification('Document translation copied to clipboard!'); |
|
}) |
|
.catch(err => { |
|
console.error('Error copying document: ', err); |
|
showNotification('Failed to copy document. Please try again.'); |
|
}); |
|
} |
|
}); |
|
} |
|
|
|
|
|
if (clearSource) { |
|
clearSource.addEventListener('click', function() { |
|
if (textInput) { |
|
textInput.value = ''; |
|
textOutput.textContent = ''; |
|
|
|
|
|
const inputEvent = new Event('input', { bubbles: true }); |
|
textInput.dispatchEvent(inputEvent); |
|
|
|
|
|
textInput.focus(); |
|
} |
|
}); |
|
} |
|
|
|
|
|
const fileInput = document.getElementById('doc-input'); |
|
const translateDocumentBtn = document.querySelector('#doc-translation-form .translate-button'); |
|
|
|
if (fileInput && fileNameDisplay && translateDocumentBtn) { |
|
fileInput.addEventListener('change', function() { |
|
if (this.files && this.files.length > 0) { |
|
const fileName = this.files[0].name; |
|
fileNameDisplay.textContent = `File selected: ${fileName}`; |
|
fileNameDisplay.style.display = 'block'; |
|
|
|
|
|
translateDocumentBtn.classList.add('active-button'); |
|
|
|
showNotification('Document uploaded successfully!'); |
|
} else { |
|
fileNameDisplay.textContent = ''; |
|
fileNameDisplay.style.display = 'none'; |
|
|
|
|
|
translateDocumentBtn.classList.remove('active-button'); |
|
} |
|
}); |
|
} |
|
|
|
|
|
if (textTranslationForm) { |
|
textTranslationForm.addEventListener('submit', function(e) { |
|
e.preventDefault(); |
|
|
|
const text = textInput.value.trim(); |
|
if (!text) { |
|
showNotification('Please enter text to translate.'); |
|
return; |
|
} |
|
|
|
const sourceLang = sourceLangText.value; |
|
const targetLang = targetLangText.value; |
|
|
|
translateText(text, sourceLang, targetLang); |
|
}); |
|
} |
|
|
|
|
|
if (docTranslationForm) { |
|
docTranslationForm.addEventListener('submit', function(e) { |
|
e.preventDefault(); |
|
|
|
const fileInput = document.getElementById('doc-input'); |
|
if (!fileInput.files || fileInput.files.length === 0) { |
|
showError('Please select a document to translate.'); |
|
return; |
|
} |
|
|
|
const file = fileInput.files[0]; |
|
const sourceLang = sourceLangDoc.value; |
|
const targetLang = targetLangDoc.value; |
|
|
|
translateDocument(file, sourceLang, targetLang); |
|
}); |
|
} |
|
|
|
|
|
const fileUploadArea = document.querySelector('.file-upload-area'); |
|
|
|
if (fileUploadArea && fileInput) { |
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
|
fileUploadArea.addEventListener(eventName, preventDefaults, false); |
|
}); |
|
|
|
function preventDefaults(e) { |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
} |
|
|
|
['dragenter', 'dragover'].forEach(eventName => { |
|
fileUploadArea.addEventListener(eventName, highlight, false); |
|
}); |
|
|
|
['dragleave', 'drop'].forEach(eventName => { |
|
fileUploadArea.addEventListener(eventName, unhighlight, false); |
|
}); |
|
|
|
function highlight() { |
|
fileUploadArea.classList.add('highlight'); |
|
} |
|
|
|
function unhighlight() { |
|
fileUploadArea.classList.remove('highlight'); |
|
} |
|
|
|
fileUploadArea.addEventListener('drop', handleDrop, false); |
|
|
|
function handleDrop(e) { |
|
const dt = e.dataTransfer; |
|
const files = dt.files; |
|
|
|
if (files && files.length > 0) { |
|
fileInput.files = files; |
|
const fileName = files[0].name; |
|
fileNameDisplay.textContent = `File selected: ${fileName}`; |
|
fileNameDisplay.style.display = 'block'; |
|
|
|
|
|
if (translateDocumentBtn) { |
|
translateDocumentBtn.classList.add('active-button'); |
|
} |
|
|
|
showNotification('Document uploaded successfully!'); |
|
} |
|
} |
|
} |
|
|
|
|
|
function translateText(text, sourceLang, targetLang) { |
|
if (textLoadingIndicator) textLoadingIndicator.style.display = 'block'; |
|
if (errorMessageElement) errorMessageElement.style.display = 'none'; |
|
|
|
|
|
fetch('/translate/text', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ |
|
text: text, |
|
source_lang: sourceLang, |
|
target_lang: targetLang |
|
}), |
|
}) |
|
.then(response => { |
|
if (!response.ok) { |
|
throw new Error(`HTTP error! Status: ${response.status}`); |
|
} |
|
return response.json(); |
|
}) |
|
.then(data => { |
|
if (textLoadingIndicator) textLoadingIndicator.style.display = 'none'; |
|
|
|
if (data.success === false) { |
|
throw new Error(data.error || 'Translation failed with an unknown error'); |
|
} |
|
|
|
|
|
if (data.detected_source_lang && sourceLang === 'auto') { |
|
showNotification(`Detected language: ${getLanguageName(data.detected_source_lang)}`); |
|
|
|
|
|
if (sourceLangText && data.detected_source_lang) { |
|
|
|
|
|
const detectedOption = Array.from(sourceLangText.options).find( |
|
option => option.value === data.detected_source_lang |
|
); |
|
|
|
if (detectedOption) { |
|
|
|
sourceLangText.parentElement.setAttribute('data-detected', |
|
`Detected: ${detectedOption.text}`); |
|
} |
|
} |
|
} |
|
|
|
|
|
if (textOutput) { |
|
textOutput.textContent = data.translated_text; |
|
|
|
|
|
applyRtlStyling(targetLang, textOutput); |
|
} |
|
|
|
|
|
if (textResult) textResult.style.display = 'block'; |
|
}) |
|
.catch(error => { |
|
console.error('Error during translation:', error); |
|
|
|
if (textLoadingIndicator) textLoadingIndicator.style.display = 'none'; |
|
|
|
|
|
showError(`Translation error: ${error.message}`); |
|
}); |
|
} |
|
|
|
|
|
function translateDocument(file, sourceLang, targetLang) { |
|
if (docLoadingIndicator) docLoadingIndicator.style.display = 'block'; |
|
if (errorMessageElement) errorMessageElement.style.display = 'none'; |
|
|
|
const formData = new FormData(); |
|
formData.append('file', file); |
|
formData.append('source_lang', sourceLang); |
|
formData.append('target_lang', targetLang); |
|
|
|
fetch('/translate/document', { |
|
method: 'POST', |
|
body: formData, |
|
}) |
|
.then(response => { |
|
if (!response.ok) { |
|
throw new Error(`HTTP error! Status: ${response.status}`); |
|
} |
|
return response.json(); |
|
}) |
|
.then(data => { |
|
if (docLoadingIndicator) docLoadingIndicator.style.display = 'none'; |
|
|
|
if (data.success === false) { |
|
throw new Error(data.error || 'Document translation failed'); |
|
} |
|
|
|
|
|
if (data.detected_source_lang && sourceLang === 'auto') { |
|
showNotification(`Detected document language: ${getLanguageName(data.detected_source_lang)}`); |
|
} |
|
|
|
|
|
if (docInputText) { |
|
docInputText.textContent = data.original_text; |
|
|
|
if (data.detected_source_lang && sourceLang === 'auto') { |
|
applyRtlStyling(data.detected_source_lang, docInputText); |
|
} else { |
|
applyRtlStyling(sourceLang, docInputText); |
|
} |
|
} |
|
|
|
|
|
if (docOutput) { |
|
docOutput.textContent = data.translated_text; |
|
|
|
applyRtlStyling(targetLang, docOutput); |
|
} |
|
|
|
|
|
if (docResult) { |
|
docResult.classList.remove('hidden'); |
|
docResult.style.display = 'flex'; |
|
} |
|
|
|
|
|
if (docFilename) docFilename.textContent = data.original_filename || file.name; |
|
if (docSourceLang) docSourceLang.textContent = (data.detected_source_lang ? getLanguageName(data.detected_source_lang) : getLanguageName(sourceLang)); |
|
|
|
|
|
const resultArea = document.querySelector('.document-result-area'); |
|
if (resultArea) { |
|
|
|
const existingDownloadBtn = document.getElementById('download-translated-doc'); |
|
if (existingDownloadBtn) { |
|
existingDownloadBtn.remove(); |
|
} |
|
|
|
|
|
const downloadBtn = document.createElement('button'); |
|
downloadBtn.id = 'download-translated-doc'; |
|
downloadBtn.className = 'download-button'; |
|
downloadBtn.innerHTML = '<i class="fas fa-download"></i> Download Translation'; |
|
|
|
|
|
downloadBtn.addEventListener('click', function() { |
|
downloadTranslatedDocument(data.translated_text, file.name, file.type); |
|
}); |
|
|
|
|
|
resultArea.appendChild(downloadBtn); |
|
} |
|
}) |
|
.catch(error => { |
|
console.error('Error during document translation:', error); |
|
|
|
if (docLoadingIndicator) docLoadingIndicator.style.display = 'none'; |
|
|
|
|
|
showError(`Document translation error: ${error.message}`); |
|
}); |
|
} |
|
|
|
|
|
function downloadTranslatedDocument(content, fileName, fileType) { |
|
console.log('Downloading translated document:', fileName, fileType); |
|
|
|
let extension = ''; |
|
if (fileName.toLowerCase().endsWith('.pdf')) { |
|
extension = '.pdf'; |
|
} else if (fileName.toLowerCase().endsWith('.docx')) { |
|
extension = '.docx'; |
|
} else if (fileName.toLowerCase().endsWith('.txt')) { |
|
extension = '.txt'; |
|
} else { |
|
extension = '.txt'; |
|
} |
|
|
|
|
|
const baseName = fileName.substring(0, fileName.lastIndexOf('.')); |
|
const translatedFileName = `${baseName}_translated${extension}`; |
|
|
|
|
|
showNotification('Preparing document for download...'); |
|
|
|
|
|
if (extension === '.txt') { |
|
const blob = new Blob([content], { type: 'text/plain' }); |
|
const url = URL.createObjectURL(blob); |
|
|
|
const a = document.createElement('a'); |
|
a.href = url; |
|
a.download = translatedFileName; |
|
document.body.appendChild(a); |
|
a.click(); |
|
document.body.removeChild(a); |
|
URL.revokeObjectURL(url); |
|
|
|
showNotification('Document downloaded successfully!'); |
|
} else { |
|
|
|
fetch('/download/translated-document', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ |
|
content: content, |
|
filename: translatedFileName, |
|
original_type: fileType || 'text/plain' |
|
}), |
|
}) |
|
.then(response => { |
|
if (!response.ok) { |
|
throw new Error(`Server error: ${response.status} ${response.statusText}`); |
|
} |
|
return response.blob(); |
|
}) |
|
.then(blob => { |
|
|
|
let mimeType; |
|
if (extension === '.pdf') { |
|
mimeType = 'application/pdf'; |
|
} else if (extension === '.docx') { |
|
mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; |
|
} else { |
|
mimeType = 'text/plain'; |
|
} |
|
|
|
|
|
const fileBlob = new Blob([blob], { type: mimeType }); |
|
const url = URL.createObjectURL(fileBlob); |
|
|
|
|
|
const a = document.createElement('a'); |
|
a.href = url; |
|
a.download = translatedFileName; |
|
document.body.appendChild(a); |
|
a.click(); |
|
document.body.removeChild(a); |
|
URL.revokeObjectURL(url); |
|
|
|
showNotification('Document downloaded successfully!'); |
|
}) |
|
.catch(error => { |
|
console.error('Error downloading document:', error); |
|
showError(`Download error: ${error.message}`); |
|
}); |
|
} |
|
} |
|
|
|
|
|
function getLanguageName(code) { |
|
|
|
const langMap = { |
|
'ar': 'Arabic', |
|
'en': 'English', |
|
'fr': 'French', |
|
'es': 'Spanish', |
|
'de': 'German', |
|
'zh': 'Chinese', |
|
'ru': 'Russian', |
|
'ja': 'Japanese', |
|
'hi': 'Hindi', |
|
'auto': 'Auto-detect' |
|
}; |
|
|
|
|
|
if (langMap[code]) { |
|
return langMap[code]; |
|
} |
|
|
|
|
|
if (sourceLangText) { |
|
const option = Array.from(sourceLangText.options).find(opt => opt.value === code); |
|
if (option) { |
|
return option.text; |
|
} |
|
} |
|
|
|
|
|
return code; |
|
} |
|
|
|
|
|
function showNotification(message) { |
|
if (notificationElement) { |
|
notificationElement.textContent = message; |
|
notificationElement.style.display = 'block'; |
|
notificationElement.classList.add('show'); |
|
|
|
|
|
setTimeout(() => { |
|
notificationElement.classList.remove('show'); |
|
setTimeout(() => { |
|
notificationElement.style.display = 'none'; |
|
}, 300); |
|
}, 3000); |
|
} |
|
} |
|
|
|
|
|
function showError(message) { |
|
if (errorMessageElement) { |
|
errorMessageElement.textContent = message; |
|
errorMessageElement.style.display = 'block'; |
|
|
|
|
|
setTimeout(() => { |
|
errorMessageElement.style.display = 'none'; |
|
}, 5000); |
|
} |
|
} |
|
|
|
|
|
handleLanguageChange(); |
|
}; |
|
|