the translation prompt now returns a JSON. see #247

This commit is contained in:
mic 2026-04-01 00:19:35 +02:00
parent 894d3475dd
commit 38f6b1987d
6 changed files with 63 additions and 12 deletions

View file

@ -821,7 +821,7 @@
"description": "" "description": ""
}, },
"prompt_translate_this_full_text": { "prompt_translate_this_full_text": {
"message": "Translate the following email in", "message": "Translate the email below into {%thunderai_translate_lang%}.\n\nRules:\n- Translate both the subject and the body.\n- Return the result as a JSON object with three fields: \"subject\", \"body\" and \"status\".\n- If the translation has been done the status is equal to 1.\n- If the email is written in one of these languages \"{%thunderai_translate_exclude_lang%}\" or in the {%thunderai_translate_lang%} language, return an empty string for the body and the subject and set the status to -1.\n- Do not add explanations, notes, or any text outside the JSON.\n\nMail subject: {%mail_subject%}\n\nMail body: {%mail_html_body%}\n\nGenerate a response in JSON format only. The output should be only a JSON object. Here is an example of the JSON format to be used:\n{\n\"subject\": \"subject translation\",\n\"body\": \"body translation\",\n\"status\": \"status result\"\n}",
"description": "" "description": ""
}, },
"prompt_this_full_text": { "prompt_this_full_text": {
@ -2189,6 +2189,10 @@
"message": "Translation language is not configured. Please set a language in the Translation settings or set a default language in the General settings.", "message": "Translation language is not configured. Please set a language in the Translation settings or set a default language in the General settings.",
"description": "" "description": ""
}, },
"translate_skipped": {
"message": "Translation skipped: Language excluded or identical to target.",
"description": "Shown in the translation banner when the email language matches the excluded or target language"
},
"antispam_by": { "antispam_by": {
"message": "Antispam by", "message": "Antispam by",
"description": "" "description": ""

View file

@ -1255,14 +1255,22 @@ switch (message.command) {
translationText.style.cssText = 'white-space: pre-wrap; line-height: 1.5;'; translationText.style.cssText = 'white-space: pre-wrap; line-height: 1.5;';
if (translationData.error) { if (translationData.error) {
translationText.textContent = translationData.message || browser.i18n.getMessage("translate_error"); translationText.textContent = translationData.message || browser.i18n.getMessage("translate_error");
} else if (translationData.translation_status === '-1') {
translationText.textContent = browser.i18n.getMessage("translate_skipped");
} else { } else {
if (translationData.translated_subject) {
const subjectEl = document.createElement('div');
subjectEl.style.cssText = 'font-weight: bold; margin-bottom: 4px;';
subjectEl.textContent = translationData.translated_subject;
translationTextWrapper.appendChild(subjectEl);
}
translationText.textContent = translationData.translated_text || ''; translationText.textContent = translationData.translated_text || '';
} }
translationTextWrapper.appendChild(translationText); translationTextWrapper.appendChild(translationText);
const maxLenTranslation = translationData.maxDisplayLength || 0; const maxLenTranslation = translationData.maxDisplayLength || 0;
const fullTranslationText = translationData.translated_text || ''; const fullTranslationText = translationData.translated_text || '';
if (!translationData.error && maxLenTranslation > 0 && fullTranslationText.length > maxLenTranslation) { if (!translationData.error && translationData.translation_status !== '-1' && maxLenTranslation > 0 && fullTranslationText.length > maxLenTranslation) {
translationText.style.overflow = 'hidden'; translationText.style.overflow = 'hidden';
translationText.style.transition = 'max-height 0.2s ease'; translationText.style.transition = 'max-height 0.2s ease';

View file

@ -278,7 +278,15 @@ export class taStorage {
* @param {string} lang - Target language code. * @param {string} lang - Target language code.
* @param {boolean} [force=true] - If true, overwrite existing translation data. * @param {boolean} [force=true] - If true, overwrite existing translation data.
*/ */
async writeTranslation(messageId, translated_text, lang, force = true, error = false, error_message = '') { async writeTranslation(messageId, data, force = true) {
const {
translated_text = '',
translated_subject = '',
translation_status = '',
lang = '',
error = false,
message = '',
} = data || {};
this.taLog.log('[writeTranslation] messageId: ' + messageId + ', lang: ' + lang + ', force: ' + force); this.taLog.log('[writeTranslation] messageId: ' + messageId + ', lang: ' + lang + ', force: ' + force);
try { try {
let key = this._buildKey(messageId); let key = this._buildKey(messageId);
@ -288,7 +296,15 @@ export class taStorage {
return; return;
} }
let now = Date.now(); let now = Date.now();
record[taStorage.FIELD_TRANSLATION] = { translated_text: translated_text, lang: lang, error: error, message: error_message, ts: now }; record[taStorage.FIELD_TRANSLATION] = {
translated_text,
translated_subject,
translation_status,
lang,
error,
message,
ts: now
};
record.ts = now; record.ts = now;
await messenger.storage.local.set({ [key]: record }); await messenger.storage.local.set({ [key]: record });
} catch (e) { } catch (e) {

View file

@ -49,7 +49,7 @@ export class taTranslationStore {
async saveTranslation(data, data_id) { async saveTranslation(data, data_id) {
this.taLog.log("[saveTranslation] data_id: " + data_id); this.taLog.log("[saveTranslation] data_id: " + data_id);
try { try {
await this._storage.writeTranslation(data_id, data.translated_text || '', data.lang || '', true, data.error || false, data.message || ''); await this._storage.writeTranslation(data_id, data, true);
await browser.storage.session.remove(this._processing_prefix + data_id); await browser.storage.session.remove(this._processing_prefix + data_id);
} catch (e) { } catch (e) {
this.taLog.error("[saveTranslation] error: " + e); this.taLog.error("[saveTranslation] error: " + e);
@ -81,6 +81,8 @@ export class taTranslationStore {
return { return {
headerMessageId: data_id, headerMessageId: data_id,
translated_text: translation.translated_text || '', translated_text: translation.translated_text || '',
translated_subject: translation.translated_subject || '',
translation_status: translation.translation_status || '',
lang: translation.lang || '', lang: translation.lang || '',
error: translation.error || false, error: translation.error || false,
message: translation.message || '', message: translation.message || '',

View file

@ -173,7 +173,7 @@ export const taPromptUtils = {
return { promptText, promptInfo: prompt }; return { promptText, promptInfo: prompt };
}, },
async buildTranslationPrompt(fullMessage, lang) { async buildTranslationPrompt(fullMessage) {
const specialPrompts = await getSpecialPrompts(); const specialPrompts = await getSpecialPrompts();
const prompt = specialPrompts.find(p => p.id === 'prompt_translate_this'); const prompt = specialPrompts.find(p => p.id === 'prompt_translate_this');
@ -183,12 +183,19 @@ export const taPromptUtils = {
} }
const bodyHtml = getMailBody(fullMessage); const bodyHtml = getMailBody(fullMessage);
let bodyText = htmlBodyToPlainText(bodyHtml.html); const mailSubject = fullMessage.headers?.subject?.[0] || '';
if (bodyText.length === 0) {
bodyText = bodyHtml.text || '';
}
const fullPrompt = promptText + " " + lang + ". \"" + bodyText + "\""; const finalSubs = await placeholdersUtils.getPlaceholdersValues({
prompt_text: promptText,
msg_text: { html: bodyHtml.html, text: bodyHtml.text },
mail_subject: mailSubject,
});
const fullPrompt = placeholdersUtils.replacePlaceholders({
text: promptText,
replacements: finalSubs,
use_default_value: false,
});
return { promptText: fullPrompt, promptInfo: prompt }; return { promptText: fullPrompt, promptInfo: prompt };
}, },

View file

@ -789,8 +789,22 @@ async function _generateTranslationForMessage(headerMessageId, tabId = null, opt
await cmd.initWorker(); await cmd.initWorker();
const aiResponse = await cmd.sendPrompt(); const aiResponse = await cmd.sendPrompt();
let translatedBody = '';
let translatedSubject = '';
let translationStatus = '';
try {
const parsed = JSON.parse(aiResponse);
translatedBody = parsed.body || '';
translatedSubject = parsed.subject || '';
translationStatus = String(parsed.status || '');
} catch (e) {
translatedBody = aiResponse;
}
const translationData = { const translationData = {
translated_text: aiResponse, translated_text: translatedBody,
translated_subject: translatedSubject,
translation_status: translationStatus,
lang: lang, lang: lang,
headerMessageId: headerMessageId headerMessageId: headerMessageId
}; };