some fixes on JSON translation. see #247
This commit is contained in:
parent
38f6b1987d
commit
c0ebc646b8
3 changed files with 106 additions and 38 deletions
|
|
@ -177,6 +177,21 @@ function _updatePanelMargins() {
|
|||
if (lastPanel) lastPanel.style.marginBottom = '1rem';
|
||||
}
|
||||
|
||||
function _isHtml(text) {
|
||||
return /<[a-z][^>]*>/i.test(text);
|
||||
}
|
||||
|
||||
function _renderSafeHtml(container, html) {
|
||||
container.textContent = '';
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
doc.querySelectorAll('script, img').forEach(el => el.remove());
|
||||
while (doc.body.firstChild) {
|
||||
container.appendChild(doc.body.firstChild);
|
||||
}
|
||||
container.querySelectorAll('p').forEach(p => { p.style.marginBlockStart = '0'; });
|
||||
}
|
||||
|
||||
function createThreeDotsMenu(isDark, menuItems, panelColors) {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.style.cssText = 'position: relative; display: inline-flex; align-items: center;';
|
||||
|
|
@ -1264,54 +1279,89 @@ switch (message.command) {
|
|||
subjectEl.textContent = translationData.translated_subject;
|
||||
translationTextWrapper.appendChild(subjectEl);
|
||||
}
|
||||
translationText.textContent = translationData.translated_text || '';
|
||||
const bodyText = translationData.translated_text || '';
|
||||
const bodyIsHtml = _isHtml(bodyText);
|
||||
if (bodyIsHtml) {
|
||||
translationText.style.whiteSpace = '';
|
||||
_renderSafeHtml(translationText, bodyText);
|
||||
} else {
|
||||
translationText.textContent = bodyText;
|
||||
}
|
||||
}
|
||||
translationTextWrapper.appendChild(translationText);
|
||||
|
||||
const maxLenTranslation = translationData.maxDisplayLength || 0;
|
||||
const fullTranslationText = translationData.translated_text || '';
|
||||
const fullTranslationIsHtml = _isHtml(fullTranslationText);
|
||||
if (!translationData.error && translationData.translation_status !== '-1' && maxLenTranslation > 0 && fullTranslationText.length > maxLenTranslation) {
|
||||
translationText.style.overflow = 'hidden';
|
||||
translationText.style.transition = 'max-height 0.2s ease';
|
||||
|
||||
let cutPos = fullTranslationText.lastIndexOf(' ', maxLenTranslation);
|
||||
if (cutPos <= 0) cutPos = maxLenTranslation;
|
||||
const truncatedTranslation = fullTranslationText.substring(0, cutPos) + '\u2026';
|
||||
translationText.textContent = truncatedTranslation;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
translationText.style.maxHeight = translationText.scrollHeight + 'px';
|
||||
});
|
||||
|
||||
const toggleLink = document.createElement('a');
|
||||
toggleLink.textContent = browser.i18n.getMessage("translate_see_more") || "See more";
|
||||
toggleLink.href = '#';
|
||||
toggleLink.style.cssText = `display: inline-block; margin-top: 4px; font-size: 13px; color: ${colors.linkColor}; cursor: pointer; text-decoration: underline;`;
|
||||
|
||||
let expanded = false;
|
||||
toggleLink.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if (!expanded) {
|
||||
translationText.textContent = fullTranslationText;
|
||||
if (fullTranslationIsHtml) {
|
||||
const collapsedMaxHeight = '4.2em';
|
||||
translationText.style.maxHeight = collapsedMaxHeight;
|
||||
|
||||
let expanded = false;
|
||||
toggleLink.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if (!expanded) {
|
||||
translationText.style.maxHeight = translationText.scrollHeight + 'px';
|
||||
toggleLink.textContent = browser.i18n.getMessage("translate_see_less") || "See less";
|
||||
} else {
|
||||
translationText.style.maxHeight = collapsedMaxHeight;
|
||||
toggleLink.textContent = browser.i18n.getMessage("translate_see_more") || "See more";
|
||||
}
|
||||
expanded = !expanded;
|
||||
});
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (translationText.scrollHeight > translationText.clientHeight) {
|
||||
translationTextWrapper.appendChild(toggleLink);
|
||||
} else {
|
||||
translationText.style.maxHeight = '';
|
||||
translationText.style.overflow = '';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let cutPos = fullTranslationText.lastIndexOf(' ', maxLenTranslation);
|
||||
if (cutPos <= 0) cutPos = maxLenTranslation;
|
||||
const truncatedTranslation = fullTranslationText.substring(0, cutPos) + '\u2026';
|
||||
translationText.textContent = truncatedTranslation;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
translationText.style.maxHeight = translationText.scrollHeight + 'px';
|
||||
toggleLink.textContent = browser.i18n.getMessage("translate_see_less") || "See less";
|
||||
} else {
|
||||
translationText.textContent = truncatedTranslation;
|
||||
const collapsedHeight = translationText.scrollHeight;
|
||||
translationText.textContent = fullTranslationText;
|
||||
translationText.style.maxHeight = translationText.scrollHeight + 'px';
|
||||
requestAnimationFrame(() => {
|
||||
translationText.style.maxHeight = collapsedHeight + 'px';
|
||||
});
|
||||
translationText.addEventListener('transitionend', function handler() {
|
||||
translationText.removeEventListener('transitionend', handler);
|
||||
});
|
||||
|
||||
let expanded = false;
|
||||
toggleLink.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if (!expanded) {
|
||||
translationText.textContent = fullTranslationText;
|
||||
translationText.style.maxHeight = translationText.scrollHeight + 'px';
|
||||
toggleLink.textContent = browser.i18n.getMessage("translate_see_less") || "See less";
|
||||
} else {
|
||||
translationText.textContent = truncatedTranslation;
|
||||
});
|
||||
toggleLink.textContent = browser.i18n.getMessage("translate_see_more") || "See more";
|
||||
}
|
||||
expanded = !expanded;
|
||||
});
|
||||
translationTextWrapper.appendChild(toggleLink);
|
||||
const collapsedHeight = translationText.scrollHeight;
|
||||
translationText.textContent = fullTranslationText;
|
||||
translationText.style.maxHeight = translationText.scrollHeight + 'px';
|
||||
requestAnimationFrame(() => {
|
||||
translationText.style.maxHeight = collapsedHeight + 'px';
|
||||
});
|
||||
translationText.addEventListener('transitionend', function handler() {
|
||||
translationText.removeEventListener('transitionend', handler);
|
||||
translationText.textContent = truncatedTranslation;
|
||||
});
|
||||
toggleLink.textContent = browser.i18n.getMessage("translate_see_more") || "See more";
|
||||
}
|
||||
expanded = !expanded;
|
||||
});
|
||||
translationTextWrapper.appendChild(toggleLink);
|
||||
}
|
||||
}
|
||||
|
||||
translationContainer.appendChild(translationTextWrapper);
|
||||
|
|
|
|||
|
|
@ -274,8 +274,13 @@ export class taStorage {
|
|||
/**
|
||||
* Write the translation field for a given Message-ID.
|
||||
* @param {string} messageId - The Message-ID header string.
|
||||
* @param {string} translated_text - The translated text.
|
||||
* @param {string} lang - Target language code.
|
||||
* @param {Object} data - Translation data object.
|
||||
* @param {string} [data.translated_text=''] - The translated body text.
|
||||
* @param {string} [data.translated_subject=''] - The translated subject.
|
||||
* @param {string} [data.translation_status=''] - Status: "1" = ok, "-1" = skipped.
|
||||
* @param {string} [data.lang=''] - Target language code.
|
||||
* @param {boolean} [data.error=false] - Whether an error occurred.
|
||||
* @param {string} [data.message=''] - Error message.
|
||||
* @param {boolean} [force=true] - If true, overwrite existing translation data.
|
||||
*/
|
||||
async writeTranslation(messageId, data, force = true) {
|
||||
|
|
|
|||
|
|
@ -430,7 +430,18 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
case 'chatgpt_saveTranslation':
|
||||
async function _saveTranslationFromWebchat(msg) {
|
||||
try {
|
||||
let translatedText = msg.text.trim();
|
||||
let rawText = msg.text.trim();
|
||||
let translatedBody = '';
|
||||
let translatedSubject = '';
|
||||
let translationStatus = '';
|
||||
try {
|
||||
const parsed = JSON.parse(rawText);
|
||||
translatedBody = parsed.body || '';
|
||||
translatedSubject = parsed.subject || '';
|
||||
translationStatus = String(parsed.status || '');
|
||||
} catch (e) {
|
||||
translatedBody = rawText;
|
||||
}
|
||||
let prefs_tr = await browser.storage.sync.get({
|
||||
translate_lang: prefs_default.translate_lang,
|
||||
default_chatgpt_lang: prefs_default.default_chatgpt_lang,
|
||||
|
|
@ -438,7 +449,9 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
});
|
||||
let lang = prefs_tr.translate_lang || prefs_tr.default_chatgpt_lang || '';
|
||||
const translationData = {
|
||||
translated_text: translatedText,
|
||||
translated_text: translatedBody,
|
||||
translated_subject: translatedSubject,
|
||||
translation_status: translationStatus,
|
||||
lang: lang,
|
||||
headerMessageId: msg.headerMessageId
|
||||
};
|
||||
|
|
@ -777,7 +790,7 @@ async function _generateTranslationForMessage(headerMessageId, tabId = null, opt
|
|||
taWorkingStatus.stopWorking();
|
||||
return;
|
||||
}
|
||||
const { promptText } = await taPromptUtils.buildTranslationPrompt(fullMessage, lang);
|
||||
const { promptText } = await taPromptUtils.buildTranslationPrompt(fullMessage);
|
||||
|
||||
const cmd = new mzta_specialCommand({
|
||||
prompt: promptText,
|
||||
|
|
@ -996,7 +1009,7 @@ async function _openTranslationWebchat(headerMessageId, tabId) {
|
|||
taLog.warn("Translation skipped: no language configured (translate_lang and default_chatgpt_lang are both empty).");
|
||||
return;
|
||||
}
|
||||
const { promptText, promptInfo } = await taPromptUtils.buildTranslationPrompt(curr_message_full, lang);
|
||||
const { promptText, promptInfo } = await taPromptUtils.buildTranslationPrompt(curr_message_full);
|
||||
promptInfo.headerMessageId = headerMessageId;
|
||||
promptInfo.translationTabId = tabId;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue