talk2me/static/js/main.js
2025-04-04 13:23:15 -06:00

256 lines
8.5 KiB
JavaScript

document.addEventListener('DOMContentLoaded', function() {
// DOM elements
const sourceLanguage = document.getElementById('sourceLanguage');
const targetLanguage = document.getElementById('targetLanguage');
const swapButton = document.getElementById('swapLanguages');
const sourceText = document.getElementById('sourceText');
const translatedText = document.getElementById('translatedText');
const recordSourceButton = document.getElementById('recordSource');
const speakButton = document.getElementById('speak');
const clearSourceButton = document.getElementById('clearSource');
const copyTranslationButton = document.getElementById('copyTranslation');
const translateButton = document.getElementById('translateButton');
const statusMessage = document.getElementById('status');
// Audio recording variables
let mediaRecorder;
let audioChunks = [];
let isRecording = false;
// Speech recognition setup
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = SpeechRecognition ? new SpeechRecognition() : null;
if (recognition) {
recognition.continuous = false;
recognition.interimResults = false;
}
// Event listeners
swapButton.addEventListener('click', swapLanguages);
translateButton.addEventListener('click', translateText);
clearSourceButton.addEventListener('click', clearSource);
copyTranslationButton.addEventListener('click', copyTranslation);
if (recognition) {
recordSourceButton.addEventListener('click', toggleRecording);
} else {
recordSourceButton.textContent = "Speech API not supported";
recordSourceButton.disabled = true;
}
speakButton.addEventListener('click', speakTranslation);
// Functions (continued)
function swapLanguages() {
const tempLang = sourceLanguage.value;
sourceLanguage.value = targetLanguage.value;
targetLanguage.value = tempLang;
// Also swap the text if both fields have content
if (sourceText.value && translatedText.value) {
const tempText = sourceText.value;
sourceText.value = translatedText.value;
translatedText.value = tempText;
}
}
function clearSource() {
sourceText.value = '';
updateStatus('');
}
function copyTranslation() {
if (!translatedText.value) {
updateStatus('Nothing to copy', 'error');
return;
}
navigator.clipboard.writeText(translatedText.value)
.then(() => {
updateStatus('Copied to clipboard!', 'success');
setTimeout(() => updateStatus(''), 2000);
})
.catch(err => {
updateStatus('Failed to copy: ' + err, 'error');
});
}
async function translateText() {
const source = sourceText.value.trim();
if (!source) {
updateStatus('Please enter or speak some text to translate', 'error');
return;
}
updateStatus('Translating...');
translatedText.value = '';
try {
const response = await fetch('/translate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sourceLanguage: sourceLanguage.value,
targetLanguage: targetLanguage.value,
text: source
})
});
const data = await response.json();
if (response.ok) {
translatedText.value = data.translation;
updateStatus('Translation complete', 'success');
setTimeout(() => updateStatus(''), 2000);
} else {
updateStatus(data.error || 'Translation failed', 'error');
}
} catch (error) {
updateStatus('Network error: ' + error.message, 'error');
}
}
function toggleRecording() {
if (!recognition) {
updateStatus('Speech recognition not supported in this browser', 'error');
return;
}
if (isRecording) {
stopRecording();
} else {
startRecording();
}
}
function startRecording() {
sourceText.value = '';
updateStatus('Listening...');
recognition.lang = getLanguageCode(sourceLanguage.value);
recognition.onresult = function(event) {
const transcript = event.results[0][0].transcript;
sourceText.value = transcript;
updateStatus('Recording completed', 'success');
setTimeout(() => updateStatus(''), 2000);
};
recognition.onerror = function(event) {
updateStatus('Error in speech recognition: ' + event.error, 'error');
stopRecording();
};
recognition.onend = function() {
stopRecording();
};
try {
recognition.start();
isRecording = true;
recordSourceButton.classList.add('recording');
recordSourceButton.querySelector('.button-text').textContent = 'Stop';
} catch (error) {
updateStatus('Failed to start recording: ' + error.message, 'error');
}
}
function stopRecording() {
if (isRecording) {
try {
recognition.stop();
} catch (error) {
console.error('Error stopping recognition:', error);
}
isRecording = false;
recordSourceButton.classList.remove('recording');
recordSourceButton.querySelector('.button-text').textContent = 'Record';
}
}
function speakTranslation() {
const text = translatedText.value.trim();
if (!text) {
updateStatus('No translation to speak', 'error');
return;
}
// Use the browser's speech synthesis API
const speech = new SpeechSynthesisUtterance(text);
speech.lang = getLanguageCode(targetLanguage.value);
speech.volume = 1;
speech.rate = 1;
speech.pitch = 1;
speech.onstart = function() {
updateStatus('Speaking...');
speakButton.disabled = true;
};
speech.onend = function() {
updateStatus('');
speakButton.disabled = false;
};
speech.onerror = function(event) {
updateStatus('Speech synthesis error: ' + event.error, 'error');
speakButton.disabled = false;
};
window.speechSynthesis.speak(speech);
}
function getLanguageCode(language) {
// Map language names to BCP 47 language tags for speech recognition/synthesis
const languageMap = {
"arabic": "ar-SA",
"armenian": "hy-AM",
"azerbaijani": "az-AZ",
"english": "en-US",
"french": "fr-FR",
"georgian": "ka-GE",
"kazakh": "kk-KZ",
"mandarin": "zh-CN",
"persian": "fa-IR",
"portuguese": "pt-PT",
"russian": "ru-RU",
"turkish": "tr-TR",
"uzbek": "uz-UZ"
};
return languageMap[language] || 'en-US';
}
function updateStatus(message, type = '') {
statusMessage.textContent = message;
statusMessage.className = 'status-message';
if (type) {
statusMessage.classList.add(type);
}
}
// Check for microphone and speech support when page loads
function checkSupportedFeatures() {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
updateStatus('Microphone access is not supported in this browser', 'error');
recordSourceButton.disabled = true;
}
if (!window.SpeechRecognition && !window.webkitSpeechRecognition) {
updateStatus('Speech recognition is not supported in this browser', 'error');
recordSourceButton.disabled = true;
}
if (!window.speechSynthesis) {
updateStatus('Speech synthesis is not supported in this browser', 'error');
speakButton.disabled = true;
}
}
checkSupportedFeatures();
});