feat: 统一前端翻译调用走用户配置接口
This commit is contained in:
@@ -80,6 +80,7 @@ server/data/spanish_diary.db
|
||||
- 用户日记数据隔离
|
||||
- 共享知识库
|
||||
- 每用户翻译配置(本地 / MyMemory / 自定义接口)
|
||||
- 实时翻译、选中翻译、保存和中译西统一调用每用户翻译配置
|
||||
- 每用户日记统计
|
||||
- 每用户单词收藏
|
||||
- 手机、平板、桌面响应式适配
|
||||
|
||||
@@ -8,6 +8,11 @@ const dbDeleteEntry = deleteEntry;
|
||||
var BACKEND_MODE = false;
|
||||
var currentUser = null;
|
||||
var favoriteWords = {};
|
||||
var currentTranslationSettings = { translator: 'local' };
|
||||
var liveTranslationTimer = null;
|
||||
var liveTranslationSeq = 0;
|
||||
var selectionTimer = null;
|
||||
var selectionSeq = 0;
|
||||
|
||||
function safeGetAllEntries() {
|
||||
if (BACKEND_MODE) return apiGetEntries();
|
||||
@@ -100,20 +105,67 @@ function clearEditor() {
|
||||
|
||||
// ----- 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() {
|
||||
var text = document.getElementById('entry-text').value;
|
||||
if (!text.trim()) {
|
||||
clearLiveTranslation();
|
||||
return;
|
||||
}
|
||||
updateVocabulary(text);
|
||||
if (!shouldUseApiTranslation()) {
|
||||
var result = translateText(text);
|
||||
document.getElementById('editor-translate-en').textContent = result.english;
|
||||
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() {
|
||||
liveTranslationSeq++;
|
||||
if (liveTranslationTimer) clearTimeout(liveTranslationTimer);
|
||||
selectionSeq++;
|
||||
if (selectionTimer) clearTimeout(selectionTimer);
|
||||
document.getElementById('editor-translate-en').textContent = '-';
|
||||
document.getElementById('editor-translate-cn').textContent = '-';
|
||||
document.getElementById('vocab-list').innerHTML = '';
|
||||
@@ -157,14 +209,36 @@ function handleSelection() {
|
||||
var ta = document.getElementById('entry-text');
|
||||
var selText = ta.value.substring(ta.selectionStart, ta.selectionEnd).trim();
|
||||
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);
|
||||
document.getElementById('selected-translation').textContent =
|
||||
result.english + ' / ' + result.chinese;
|
||||
el.style.display = 'block';
|
||||
} else {
|
||||
el.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
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 -----
|
||||
@@ -186,7 +260,9 @@ function saveCurrentEntry() {
|
||||
var spanish = document.getElementById('entry-text').value.trim();
|
||||
if (!spanish) { alert('请输入西语内容'); return; }
|
||||
|
||||
var translation = translateText(spanish);
|
||||
var saveBtn = document.getElementById('btn-save');
|
||||
saveBtn.textContent = '\u23F3 翻译中...';
|
||||
translateEditorToEnZh(spanish).then(function(translation) {
|
||||
var data = {
|
||||
date: date,
|
||||
spanish: spanish,
|
||||
@@ -215,7 +291,7 @@ function saveCurrentEntry() {
|
||||
});
|
||||
}
|
||||
|
||||
savePromise.then(function() {
|
||||
return savePromise.then(function() {
|
||||
return safeGetAllEntries();
|
||||
}).then(function(entries) {
|
||||
state.entries = entries;
|
||||
@@ -226,6 +302,9 @@ function saveCurrentEntry() {
|
||||
document.getElementById('btn-save').textContent = '\uD83D\uDCBE 已保存 \u2713';
|
||||
setTimeout(function() { document.getElementById('btn-save').textContent = '\uD83D\uDCBE 保存'; }, 1500);
|
||||
});
|
||||
}).catch(function() {
|
||||
document.getElementById('btn-save').textContent = '\uD83D\uDCBE 保存';
|
||||
});
|
||||
}
|
||||
|
||||
function newEntry() {
|
||||
@@ -300,26 +379,61 @@ function updateHelperPreview() {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (shouldUseApiTranslation() || !BACKEND_MODE) {
|
||||
helperDebounceTimer = setTimeout(function() {
|
||||
translateZhOnline(zh);
|
||||
}, 600);
|
||||
}
|
||||
}
|
||||
|
||||
function translateZhOnline(zh) {
|
||||
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=' +
|
||||
encodeURIComponent(zh) + '&langpair=zh-CN|es';
|
||||
fetch(url)
|
||||
request = fetch(url)
|
||||
.then(function(res) { return res.json(); })
|
||||
.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;
|
||||
var text = data && data.responseData && data.responseData.translatedText;
|
||||
if (text && !data.quotaFinished) {
|
||||
var text = result && result.translatedText;
|
||||
if (text && !result.quotaFinished) {
|
||||
helperOnlineResult = text.trim();
|
||||
document.getElementById('helper-result').textContent = helperOnlineResult;
|
||||
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() {
|
||||
@@ -463,6 +577,7 @@ function toggleFavorite(btn) {
|
||||
function loadSettingsForm() {
|
||||
if (!BACKEND_MODE) return;
|
||||
apiGetTranslationSettings().then(function(settings) {
|
||||
currentTranslationSettings = settings;
|
||||
document.getElementById('translator-type').value = settings.translator || 'local';
|
||||
document.getElementById('api-url').value = settings.api_url || '';
|
||||
document.getElementById('api-key').value = settings.api_key || '';
|
||||
@@ -535,7 +650,8 @@ function saveSettingsModal() {
|
||||
api_url: document.getElementById('api-url').value.trim(),
|
||||
api_key: document.getElementById('api-key').value.trim(),
|
||||
extra_config: extra
|
||||
}).then(function() {
|
||||
}).then(function(saved) {
|
||||
currentTranslationSettings = saved;
|
||||
closeSettingsModal();
|
||||
alert('翻译设置已保存');
|
||||
}).catch(function(err) { alert(err.message); });
|
||||
|
||||
Reference in New Issue
Block a user