Files
InfoGenie/InfoGenie-frontend/public/60sapi/实用功能/在线翻译/script.js
2025-09-15 19:08:47 +08:00

452 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 全局变量
let supportedLanguages = {};
let isTranslating = false;
// DOM元素
const elements = {
fromLang: null,
toLang: null,
inputText: null,
outputText: null,
translateBtn: null,
swapBtn: null,
clearBtn: null,
copyBtn: null,
charCount: null,
detectedLang: null,
targetLang: null,
pronounceSection: null
};
// 初始化
document.addEventListener('DOMContentLoaded', function() {
initializeElements();
loadSupportedLanguages();
bindEvents();
updateCharCount();
});
// 初始化DOM元素
function initializeElements() {
elements.fromLang = document.getElementById('from-lang');
elements.toLang = document.getElementById('to-lang');
elements.inputText = document.getElementById('input-text');
elements.outputText = document.getElementById('output-text');
elements.translateBtn = document.getElementById('translate-btn');
elements.swapBtn = document.getElementById('swap-btn');
elements.clearBtn = document.getElementById('clear-btn');
elements.copyBtn = document.getElementById('copy-btn');
elements.charCount = document.getElementById('char-count');
elements.detectedLang = document.getElementById('detected-lang');
elements.targetLang = document.getElementById('target-lang');
elements.pronounceSection = document.getElementById('pronounce-section');
}
// 加载支持的语言列表
async function loadSupportedLanguages() {
try {
const response = await fetch('https://60s.viki.moe/v2/fanyi/langs');
const data = await response.json();
if (data.code === 200 && data.data && Array.isArray(data.data)) {
// 转换数组格式为对象格式
supportedLanguages = {};
supportedLanguages['auto'] = '自动检测';
data.data.forEach(lang => {
supportedLanguages[lang.code] = lang.label;
});
populateLanguageSelectors();
} else {
throw new Error('获取语言列表失败');
}
} catch (error) {
console.error('加载语言列表失败:', error);
showToast('加载语言列表失败,请刷新页面重试', 'error');
// 使用默认语言列表
useDefaultLanguages();
}
}
// 使用默认语言列表(备用方案)
function useDefaultLanguages() {
supportedLanguages = {
'auto': '自动检测',
'zh-CHS': '中文',
'en': '英语',
'ja': '日语',
'ko': '韩语',
'fr': '法语',
'de': '德语',
'es': '西班牙语',
'ru': '俄语',
'th': '泰语',
'ar': '阿拉伯语',
'pt': '葡萄牙语',
'it': '意大利语'
};
populateLanguageSelectors();
}
// 填充语言选择器
function populateLanguageSelectors() {
const fromSelect = elements.fromLang;
const toSelect = elements.toLang;
// 清空现有选项
fromSelect.innerHTML = '';
toSelect.innerHTML = '';
// 添加语言选项
Object.entries(supportedLanguages).forEach(([code, name]) => {
const fromOption = new Option(name, code);
const toOption = new Option(name, code);
fromSelect.appendChild(fromOption);
toSelect.appendChild(toOption);
});
// 设置默认值
fromSelect.value = 'auto';
toSelect.value = 'en';
// 如果没有auto选项则设置为中文
if (!supportedLanguages['auto']) {
fromSelect.value = 'zh-CHS';
}
}
// 绑定事件
function bindEvents() {
// 输入框事件
elements.inputText.addEventListener('input', function() {
updateCharCount();
clearOutput();
});
elements.inputText.addEventListener('keydown', function(e) {
if (e.ctrlKey && e.key === 'Enter') {
translateText();
}
});
// 按钮事件
elements.translateBtn.addEventListener('click', translateText);
elements.swapBtn.addEventListener('click', swapLanguages);
elements.clearBtn.addEventListener('click', clearInput);
elements.copyBtn.addEventListener('click', copyOutput);
// 语言选择器事件
elements.fromLang.addEventListener('change', function() {
clearOutput();
updateLanguageLabels();
});
elements.toLang.addEventListener('change', function() {
clearOutput();
updateLanguageLabels();
});
}
// 更新字符计数
function updateCharCount() {
const text = elements.inputText.value;
const count = text.length;
elements.charCount.textContent = `${count}/5000`;
if (count > 5000) {
elements.charCount.style.color = '#e74c3c';
} else {
elements.charCount.style.color = '#74c69d';
}
}
// 更新语言标签
function updateLanguageLabels() {
const fromLang = elements.fromLang.value;
const toLang = elements.toLang.value;
elements.detectedLang.textContent = supportedLanguages[fromLang] || '未知语言';
elements.targetLang.textContent = supportedLanguages[toLang] || '未知语言';
}
// 翻译文本
async function translateText() {
const text = elements.inputText.value.trim();
if (!text) {
showToast('请输入要翻译的文本', 'error');
return;
}
if (text.length > 5000) {
showToast('文本长度不能超过5000字符', 'error');
return;
}
if (isTranslating) {
return;
}
setTranslating(true);
try {
const fromLang = elements.fromLang.value;
const toLang = elements.toLang.value;
// 构建请求URL
const params = new URLSearchParams({
text: text,
from: fromLang,
to: toLang
});
const response = await fetch(`https://60s.viki.moe/v2/fanyi?${params}`);
const data = await response.json();
if (data.code === 200 && data.data) {
displayTranslationResult(data.data);
} else {
throw new Error(data.msg || '翻译失败');
}
} catch (error) {
console.error('翻译失败:', error);
showToast('翻译失败: ' + error.message, 'error');
elements.outputText.textContent = '翻译失败,请重试';
} finally {
setTranslating(false);
}
}
// 显示翻译结果
function displayTranslationResult(data) {
// 显示翻译结果
const translation = data.target ? data.target.text : '';
elements.outputText.textContent = translation;
// 更新检测到的语言
if (data.source && data.source.type_desc) {
elements.detectedLang.textContent = `检测: ${data.source.type_desc}`;
}
// 显示发音信息
displayPronunciation(data);
// 如果翻译结果为空
if (!translation) {
elements.outputText.textContent = '未获取到翻译结果';
}
}
// 显示发音信息
function displayPronunciation(data) {
const pronounceSection = elements.pronounceSection;
if (!pronounceSection) {
return;
}
pronounceSection.innerHTML = '';
// 原文发音
if (data.source && data.source.pronounce) {
const sourcePhoneticDiv = document.createElement('div');
sourcePhoneticDiv.className = 'pronounce-item show';
sourcePhoneticDiv.textContent = `原文发音: [${data.source.pronounce}]`;
pronounceSection.appendChild(sourcePhoneticDiv);
}
// 译文发音
if (data.target && data.target.pronounce) {
const targetPhoneticDiv = document.createElement('div');
targetPhoneticDiv.className = 'pronounce-item show';
targetPhoneticDiv.textContent = `译文发音: [${data.target.pronounce}]`;
pronounceSection.appendChild(targetPhoneticDiv);
}
}
// 设置翻译状态
function setTranslating(translating) {
isTranslating = translating;
elements.translateBtn.disabled = translating;
if (translating) {
elements.translateBtn.classList.add('loading');
} else {
elements.translateBtn.classList.remove('loading');
}
}
// 交换语言
function swapLanguages() {
const fromValue = elements.fromLang.value;
const toValue = elements.toLang.value;
// 不能交换自动检测
if (fromValue === 'auto') {
showToast('自动检测语言无法交换', 'error');
return;
}
elements.fromLang.value = toValue;
elements.toLang.value = fromValue;
// 交换文本内容
const inputText = elements.inputText.value;
const outputText = elements.outputText.textContent;
if (outputText && outputText !== '翻译结果将在这里显示...' && outputText !== '翻译失败,请重试' && outputText !== '未获取到翻译结果') {
elements.inputText.value = outputText;
elements.outputText.textContent = inputText;
}
updateCharCount();
updateLanguageLabels();
clearPronunciation();
}
// 清空输入
function clearInput() {
elements.inputText.value = '';
updateCharCount();
clearOutput();
}
// 清空输出
function clearOutput() {
elements.outputText.textContent = '翻译结果将在这里显示...';
clearPronunciation();
}
// 清空发音信息
function clearPronunciation() {
if (elements.pronounceSection) {
elements.pronounceSection.innerHTML = '';
}
}
// 复制输出
function copyOutput() {
const text = elements.outputText.textContent;
if (!text || text === '翻译结果将在这里显示...' || text === '翻译失败,请重试' || text === '未获取到翻译结果') {
showToast('没有可复制的内容', 'error');
return;
}
// 使用现代API复制
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(() => {
showToast('已复制到剪贴板');
}).catch(() => {
fallbackCopy(text);
});
} else {
fallbackCopy(text);
}
}
// 备用复制方法
function fallbackCopy(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
showToast('已复制到剪贴板');
} catch (err) {
showToast('复制失败,请手动复制', 'error');
}
document.body.removeChild(textArea);
}
// 显示提示消息
function showToast(message, type = 'success') {
// 移除现有的toast
const existingToast = document.querySelector('.toast');
if (existingToast) {
existingToast.remove();
}
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.textContent = message;
document.body.appendChild(toast);
// 显示toast
setTimeout(() => {
toast.classList.add('show');
}, 100);
// 自动隐藏
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, 300);
}, 3000);
}
// 工具函数:防抖
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 添加键盘快捷键支持
document.addEventListener('keydown', function(e) {
// Ctrl+Enter 翻译
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
translateText();
}
// Ctrl+Shift+C 复制结果
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
e.preventDefault();
copyOutput();
}
// Ctrl+Shift+X 清空输入
if (e.ctrlKey && e.shiftKey && e.key === 'X') {
e.preventDefault();
clearInput();
}
// Ctrl+Shift+S 交换语言
if (e.ctrlKey && e.shiftKey && e.key === 'S') {
e.preventDefault();
swapLanguages();
}
});
// 页面可见性变化时的处理
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
// 页面隐藏时暂停翻译请求
if (isTranslating) {
setTranslating(false);
}
}
});
// 错误处理
window.addEventListener('error', function(e) {
console.error('页面错误:', e.error);
});
window.addEventListener('unhandledrejection', function(e) {
console.error('未处理的Promise拒绝:', e.reason);
});