some fixes on JSON translation. see #247

This commit is contained in:
mic 2026-04-01 00:27:47 +02:00
parent 38f6b1987d
commit c0ebc646b8
3 changed files with 106 additions and 38 deletions

View file

@ -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,16 +1279,55 @@ 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';
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;`;
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';
@ -1283,11 +1337,6 @@ switch (message.command) {
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();
@ -1313,6 +1362,7 @@ switch (message.command) {
});
translationTextWrapper.appendChild(toggleLink);
}
}
translationContainer.appendChild(translationTextWrapper);

View file

@ -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) {

View file

@ -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;