feat: 统一前端翻译调用走用户配置接口

This commit is contained in:
guoqingshan
2026-08-01 21:41:20 +08:00
parent c70649475a
commit 3eabdc9f32
2 changed files with 171 additions and 54 deletions
+1
View File
@@ -80,6 +80,7 @@ server/data/spanish_diary.db
- 用户日记数据隔离 - 用户日记数据隔离
- 共享知识库 - 共享知识库
- 每用户翻译配置(本地 / MyMemory / 自定义接口) - 每用户翻译配置(本地 / MyMemory / 自定义接口)
- 实时翻译、选中翻译、保存和中译西统一调用每用户翻译配置
- 每用户日记统计 - 每用户日记统计
- 每用户单词收藏 - 每用户单词收藏
- 手机、平板、桌面响应式适配 - 手机、平板、桌面响应式适配
+127 -11
View File
@@ -8,6 +8,11 @@ const dbDeleteEntry = deleteEntry;
var BACKEND_MODE = false; var BACKEND_MODE = false;
var currentUser = null; var currentUser = null;
var favoriteWords = {}; var favoriteWords = {};
var currentTranslationSettings = { translator: 'local' };
var liveTranslationTimer = null;
var liveTranslationSeq = 0;
var selectionTimer = null;
var selectionSeq = 0;
function safeGetAllEntries() { function safeGetAllEntries() {
if (BACKEND_MODE) return apiGetEntries(); if (BACKEND_MODE) return apiGetEntries();
@@ -100,20 +105,67 @@ function clearEditor() {
// ----- Live translation & vocabulary ----- // ----- Live translation & vocabulary -----
function shouldUseApiTranslation() {
return BACKEND_MODE && (currentTranslationSettings.translator || 'local') !== 'local';
}
function translateEditorToEnZh(text) {
if (!shouldUseApiTranslation()) {
var local = translateText(text);
return Promise.resolve({ english: local.english, chinese: local.chinese, provider: 'local' });
}
return Promise.all([
apiTranslate({ text: text, source: 'es', target: 'en' }),
apiTranslate({ text: text, source: 'es', target: 'zh' })
]).then(function(results) {
if (!results[0].translatedText || !results[1].translatedText) {
throw new Error('翻译接口未返回结果');
}
return {
english: results[0].translatedText,
chinese: results[1].translatedText,
provider: 'api'
};
}).catch(function() {
var local = translateText(text);
return { english: local.english, chinese: local.chinese, provider: 'local' };
});
}
function updateLiveTranslation() { function updateLiveTranslation() {
var text = document.getElementById('entry-text').value; var text = document.getElementById('entry-text').value;
if (!text.trim()) { if (!text.trim()) {
clearLiveTranslation(); clearLiveTranslation();
return; return;
} }
updateVocabulary(text);
if (!shouldUseApiTranslation()) {
var result = translateText(text); var result = translateText(text);
document.getElementById('editor-translate-en').textContent = result.english; document.getElementById('editor-translate-en').textContent = result.english;
document.getElementById('editor-translate-cn').textContent = result.chinese; document.getElementById('editor-translate-cn').textContent = result.chinese;
return;
}
if (liveTranslationTimer) clearTimeout(liveTranslationTimer);
liveTranslationTimer = setTimeout(function() {
fetchLiveTranslation(text);
}, 350);
}
updateVocabulary(text); function fetchLiveTranslation(text) {
var seq = ++liveTranslationSeq;
translateEditorToEnZh(text).then(function(result) {
if (seq !== liveTranslationSeq) return;
if (document.getElementById('entry-text').value !== text) return;
document.getElementById('editor-translate-en').textContent = result.english;
document.getElementById('editor-translate-cn').textContent = result.chinese;
});
} }
function clearLiveTranslation() { function clearLiveTranslation() {
liveTranslationSeq++;
if (liveTranslationTimer) clearTimeout(liveTranslationTimer);
selectionSeq++;
if (selectionTimer) clearTimeout(selectionTimer);
document.getElementById('editor-translate-en').textContent = '-'; document.getElementById('editor-translate-en').textContent = '-';
document.getElementById('editor-translate-cn').textContent = '-'; document.getElementById('editor-translate-cn').textContent = '-';
document.getElementById('vocab-list').innerHTML = ''; document.getElementById('vocab-list').innerHTML = '';
@@ -157,14 +209,36 @@ function handleSelection() {
var ta = document.getElementById('entry-text'); var ta = document.getElementById('entry-text');
var selText = ta.value.substring(ta.selectionStart, ta.selectionEnd).trim(); var selText = ta.value.substring(ta.selectionStart, ta.selectionEnd).trim();
var el = document.getElementById('selection-translate'); var el = document.getElementById('selection-translate');
if (selText && selText.length > 0) { if (!selText) {
selectionSeq++;
if (selectionTimer) clearTimeout(selectionTimer);
el.style.display = 'none';
return;
}
if (!shouldUseApiTranslation()) {
var result = translateSelection(selText); var result = translateSelection(selText);
document.getElementById('selected-translation').textContent = document.getElementById('selected-translation').textContent =
result.english + ' / ' + result.chinese; result.english + ' / ' + result.chinese;
el.style.display = 'block'; el.style.display = 'block';
} else { return;
el.style.display = 'none';
} }
if (selectionTimer) clearTimeout(selectionTimer);
selectionTimer = setTimeout(function() {
fetchSelectionTranslation(selText);
}, 250);
}
function fetchSelectionTranslation(selText) {
var seq = ++selectionSeq;
translateEditorToEnZh(selText).then(function(result) {
if (seq !== selectionSeq) return;
var ta = document.getElementById('entry-text');
var current = ta.value.substring(ta.selectionStart, ta.selectionEnd).trim();
if (current !== selText) return;
document.getElementById('selected-translation').textContent =
result.english + ' / ' + result.chinese;
document.getElementById('selection-translate').style.display = 'block';
});
} }
// ----- Event handlers ----- // ----- Event handlers -----
@@ -186,7 +260,9 @@ function saveCurrentEntry() {
var spanish = document.getElementById('entry-text').value.trim(); var spanish = document.getElementById('entry-text').value.trim();
if (!spanish) { alert('请输入西语内容'); return; } if (!spanish) { alert('请输入西语内容'); return; }
var translation = translateText(spanish); var saveBtn = document.getElementById('btn-save');
saveBtn.textContent = '\u23F3 翻译中...';
translateEditorToEnZh(spanish).then(function(translation) {
var data = { var data = {
date: date, date: date,
spanish: spanish, spanish: spanish,
@@ -215,7 +291,7 @@ function saveCurrentEntry() {
}); });
} }
savePromise.then(function() { return savePromise.then(function() {
return safeGetAllEntries(); return safeGetAllEntries();
}).then(function(entries) { }).then(function(entries) {
state.entries = entries; state.entries = entries;
@@ -226,6 +302,9 @@ function saveCurrentEntry() {
document.getElementById('btn-save').textContent = '\uD83D\uDCBE 已保存 \u2713'; document.getElementById('btn-save').textContent = '\uD83D\uDCBE 已保存 \u2713';
setTimeout(function() { document.getElementById('btn-save').textContent = '\uD83D\uDCBE 保存'; }, 1500); setTimeout(function() { document.getElementById('btn-save').textContent = '\uD83D\uDCBE 保存'; }, 1500);
}); });
}).catch(function() {
document.getElementById('btn-save').textContent = '\uD83D\uDCBE 保存';
});
} }
function newEntry() { function newEntry() {
@@ -300,26 +379,61 @@ function updateHelperPreview() {
return; return;
} }
var local = translateZhToEs(zh); var local = translateZhToEs(zh);
document.getElementById('helper-result').textContent = local || '本地词典未收录,正在尝试在线翻译...'; if (shouldUseApiTranslation()) {
document.getElementById('helper-result').textContent = local || '正在调用已配置的翻译接口...';
document.getElementById('helper-confirm').disabled = true;
} else {
document.getElementById('helper-result').textContent = local || (BACKEND_MODE ? '本地词典未收录' : '本地词典未收录,正在尝试在线翻译...');
document.getElementById('helper-confirm').disabled = !local; document.getElementById('helper-confirm').disabled = !local;
}
if (shouldUseApiTranslation() || !BACKEND_MODE) {
helperDebounceTimer = setTimeout(function() { helperDebounceTimer = setTimeout(function() {
translateZhOnline(zh); translateZhOnline(zh);
}, 600); }, 600);
} }
}
function translateZhOnline(zh) { function translateZhOnline(zh) {
var seq = ++helperRequestSeq; var seq = ++helperRequestSeq;
var request;
if (shouldUseApiTranslation()) {
request = apiTranslate({ text: zh, source: 'zh', target: 'es' }).then(function(data) {
return { translatedText: data.translatedText };
});
} else if (!BACKEND_MODE) {
var url = 'https://api.mymemory.translated.net/get?q=' + var url = 'https://api.mymemory.translated.net/get?q=' +
encodeURIComponent(zh) + '&langpair=zh-CN|es'; encodeURIComponent(zh) + '&langpair=zh-CN|es';
fetch(url) request = fetch(url)
.then(function(res) { return res.json(); }) .then(function(res) { return res.json(); })
.then(function(data) { .then(function(data) {
return {
translatedText: data && data.responseData && data.responseData.translatedText,
quotaFinished: data && data.quotaFinished
};
});
} else {
var localOnly = translateZhToEs(zh);
if (localOnly) {
document.getElementById('helper-result').textContent = localOnly;
document.getElementById('helper-confirm').disabled = false;
}
return;
}
request.then(function(result) {
if (seq !== helperRequestSeq) return; if (seq !== helperRequestSeq) return;
var text = data && data.responseData && data.responseData.translatedText; var text = result && result.translatedText;
if (text && !data.quotaFinished) { if (text && !result.quotaFinished) {
helperOnlineResult = text.trim(); helperOnlineResult = text.trim();
document.getElementById('helper-result').textContent = helperOnlineResult; document.getElementById('helper-result').textContent = helperOnlineResult;
document.getElementById('helper-confirm').disabled = false; document.getElementById('helper-confirm').disabled = false;
} else {
var local = translateZhToEs(zh);
if (local) {
document.getElementById('helper-result').textContent = local;
document.getElementById('helper-confirm').disabled = false;
} else {
document.getElementById('helper-result').textContent = '翻译接口未返回结果,且本地词典未收录';
}
} }
}) })
.catch(function() { .catch(function() {
@@ -463,6 +577,7 @@ function toggleFavorite(btn) {
function loadSettingsForm() { function loadSettingsForm() {
if (!BACKEND_MODE) return; if (!BACKEND_MODE) return;
apiGetTranslationSettings().then(function(settings) { apiGetTranslationSettings().then(function(settings) {
currentTranslationSettings = settings;
document.getElementById('translator-type').value = settings.translator || 'local'; document.getElementById('translator-type').value = settings.translator || 'local';
document.getElementById('api-url').value = settings.api_url || ''; document.getElementById('api-url').value = settings.api_url || '';
document.getElementById('api-key').value = settings.api_key || ''; document.getElementById('api-key').value = settings.api_key || '';
@@ -535,7 +650,8 @@ function saveSettingsModal() {
api_url: document.getElementById('api-url').value.trim(), api_url: document.getElementById('api-url').value.trim(),
api_key: document.getElementById('api-key').value.trim(), api_key: document.getElementById('api-key').value.trim(),
extra_config: extra extra_config: extra
}).then(function() { }).then(function(saved) {
currentTranslationSettings = saved;
closeSettingsModal(); closeSettingsModal();
alert('翻译设置已保存'); alert('翻译设置已保存');
}).catch(function(err) { alert(err.message); }); }).catch(function(err) { alert(err.message); });