From ef7483031d9a71fdc7521c0f4c38a37f948f3acb Mon Sep 17 00:00:00 2001 From: mic Date: Sat, 28 Mar 2026 00:21:45 +0100 Subject: [PATCH] translate_display_mode and translate_max_display_length added. see #247 --- _locales/en/messages.json | 62 +++++++++++------- api_webchat/messagesArea.js | 25 +++++++- claude-spec/01-architecture.md | 16 +++-- claude-spec/02-prompts.md | 8 ++- claude-spec/05-options.md | 13 ++-- js/mzta-compose-script.js | 60 +++++++++++++++++- mzta-background.js | 97 +++++++++++++++++++++++++++-- options/mzta-options-default.js | 2 + pages/summarize/mzta-summarize.html | 10 +-- pages/translate/mzta-translate.html | 27 +++++++- pages/translate/mzta-translate.js | 3 + 11 files changed, 270 insertions(+), 53 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 49c64d6d..6c81b13b 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -239,6 +239,10 @@ "message": "Save as Summary", "description": "Button label in the webchat window to save the AI response as a message summary" }, + "webchat_save_as_translation": { + "message": "Save as Translation", + "description": "Button label in the webchat window to save the AI response as a message translation" + }, "chatgpt_textarea_not_found_error": { "message": "It seems that the ChatGPT page is taking too long to load. If it finishes loading, click the button on the right. If the problem persists, please check the service status.", "description": "" @@ -1957,18 +1961,6 @@ "message": "Auto-summarize messages", "description": "" }, - "prefs_OptionText_summarize_auto_disabled": { - "message": "Disabled", - "description": "" - }, - "prefs_OptionText_summarize_auto_manual": { - "message": "Show summary button", - "description": "" - }, - "prefs_OptionText_summarize_auto_automatic": { - "message": "Generate automatically", - "description": "" - }, "prefs_OptionText_summarize_auto_Info": { "message": "Choose whether to automatically generate summaries when viewing messages. Requires an API-based connection (not ChatGPT Web).", "description": "" @@ -1977,14 +1969,6 @@ "message": "Display summary in", "description": "" }, - "prefs_OptionText_summarize_display_mode_inline": { - "message": "Message pane (inline)", - "description": "" - }, - "prefs_OptionText_summarize_display_mode_webchat": { - "message": "Chat window", - "description": "" - }, "prefs_OptionText_summarize_display_mode_Info": { "message": "Choose where the summary result is displayed. Inline mode shows a summary banner directly in the message pane. Chat window mode opens the AI chat window.", "description": "" @@ -2085,15 +2069,15 @@ "message": "Auto-translate messages", "description": "" }, - "prefs_OptionText_translate_auto_disabled": { + "prefs_OptionText_action_auto_disabled": { "message": "Disabled", "description": "" }, - "prefs_OptionText_translate_auto_manual": { + "prefs_OptionText_action_auto_manual": { "message": "Manual button only", "description": "" }, - "prefs_OptionText_translate_auto_automatic": { + "prefs_OptionText_action_auto_automatic": { "message": "Automatic", "description": "" }, @@ -2101,6 +2085,38 @@ "message": "Choose when to translate messages: disabled, only when clicking the button, or automatically when opening a message.", "description": "" }, + "prefs_OptionText_translate_display_mode": { + "message": "Display mode for translations", + "description": "" + }, + "prefs_OptionText_display_mode_inline": { + "message": "Message pane (inline)", + "description": "" + }, + "prefs_OptionText_display_mode_webchat": { + "message": "Chat window", + "description": "" + }, + "prefs_OptionText_translate_display_mode_Info": { + "message": "Choose where to display translations. Note: automatic mode always uses inline display.", + "description": "" + }, + "prefs_OptionText_translate_max_display_length": { + "message": "Maximum length of displayed translation", + "description": "" + }, + "prefs_OptionText_translate_max_display_length_Info": { + "message": "Maximum number of characters shown in the inline translation. 0 = no limit. When set, longer text is truncated with a \"See more\" toggle.", + "description": "" + }, + "translate_see_more": { + "message": "See more", + "description": "" + }, + "translate_see_less": { + "message": "See less", + "description": "" + }, "prefs_OptionText_translate_lang": { "message": "Translation target language", "description": "" diff --git a/api_webchat/messagesArea.js b/api_webchat/messagesArea.js index 6c01ddca..62fffa42 100644 --- a/api_webchat/messagesArea.js +++ b/api_webchat/messagesArea.js @@ -459,7 +459,7 @@ class MessagesArea extends HTMLElement { } // Save as Summary button (only shown for summary webchat sessions) - if(promptData.prompt_info?.headerMessageId) { + if(promptData.prompt_info?.headerMessageId && promptData.prompt_info?.summaryTabId) { const saveSummaryButton = document.createElement('button'); saveSummaryButton.textContent = browser.i18n.getMessage("webchat_save_as_summary"); saveSummaryButton.classList.add('action_btn'); @@ -481,6 +481,29 @@ class MessagesArea extends HTMLElement { selectionInfo.style.display = "block"; } + // Save as Translation button (only shown for translation webchat sessions) + if(promptData.prompt_info?.headerMessageId && promptData.prompt_info?.translationTabId) { + const saveTranslationButton = document.createElement('button'); + saveTranslationButton.textContent = browser.i18n.getMessage("webchat_save_as_translation"); + saveTranslationButton.classList.add('action_btn'); + saveTranslationButton.addEventListener('click', async () => { + let finalText = removeAloneBRs(fullTextHTMLAtAssignment); + const selectedHTML = this.getCurrentSelectionHTML(); + if(selectedHTML != "") { + finalText = removeAloneBRs(selectedHTML); + } + await browser.runtime.sendMessage({ + command: "chatgpt_saveTranslation", + text: finalText, + headerMessageId: promptData.prompt_info.headerMessageId, + tabId: promptData.prompt_info.translationTabId || promptData.tabId, + }); + browser.runtime.sendMessage({command: "chatgpt_close", window_id: (await browser.windows.getCurrent()).id}); + }); + actionButtons.appendChild(saveTranslationButton); + selectionInfo.style.display = "block"; + } + // diff viewer button if(promptData.prompt_info?.use_diff_viewer == "1") { const diffvButton = document.createElement('button'); diff --git a/claude-spec/01-architecture.md b/claude-spec/01-architecture.md index a679df6f..652c8fcf 100644 --- a/claude-spec/01-architecture.md +++ b/claude-spec/01-architecture.md @@ -80,11 +80,13 @@ mzta-background.js (checks summarize_auto + summarize_display_mode prefs) ### Data Flow: Inline Translation on Message Display -The `translate_auto` preference controls when translation is triggered. Unlike summarize, translation has no `display_mode` option (always inline). +The `translate_display_mode` preference (`'inline'` or `'webchat'`) controls where +the translation is displayed. The `translate_auto` preference controls when it is triggered. -- `translate_auto = 0` (disabled) → do nothing -- `translate_auto = 1` (manual button) → show "Get AI Translation" button in message body -- `translate_auto = 2` (automatic) → generate translation immediately on message open +- `translate_auto = 2` (automatic) always generates inline, regardless of `translate_display_mode`. +- `translate_auto = 1` (manual button) respects `translate_display_mode`: + - `'inline'` → button click triggers inline generation + - `'webchat'` → button click opens the AI chat window via `_openTranslationWebchat()` The target language is determined by `translate_lang` (fallback on `default_chatgpt_lang`). @@ -93,14 +95,16 @@ User opens/selects a message in Thunderbird ↓ mzta-compose-script.js (sends "initTranslation" to background) ↓ -mzta-background.js (checks translate + translate_auto prefs) +mzta-background.js (checks translate + translate_auto + translate_display_mode prefs) ↓ ┌──────────────────────────────────────────────────────────┐ │ translate_auto = 0 → do nothing │ │ translate_auto = 1 → show "click to translate" button │ + │ display_mode = inline → click triggers inline gen │ + │ display_mode = webchat → click opens chat window │ │ translate_auto = 2 → generate immediately (always inline)│ └──────────────────────────────────────────────────────────┘ - ↓ (if generating) + ↓ (if generating inline) taTranslationStore (check cache / set processing) ↓ (cache miss) mzta-special-commands (via Web Worker, NOT chatgpt_web) diff --git a/claude-spec/02-prompts.md b/claude-spec/02-prompts.md index 77b271c2..c2c3eaae 100644 --- a/claude-spec/02-prompts.md +++ b/claude-spec/02-prompts.md @@ -88,14 +88,16 @@ The summarize feature uses two distinct prompt pathways: ### Translate: Inline-Only Prompt System -The translate feature uses a single special prompt (`prompt_translate_this`) for inline translation in the message body. Unlike summarize, it has no context menu entry and no webchat mode. +The translate feature uses a single special prompt (`prompt_translate_this`) for translating emails. It supports both inline display and webchat mode, but has no context menu entry. -**Inline Translation on Message Display** (controlled by `translate_auto` pref): +**Inline Translation on Message Display** (controlled by `translate_auto` and `translate_display_mode` prefs): - Uses a single special prompt: `prompt_translate_this` - The prompt text is appended with the target language and the email body: `prompt_text + " " + lang + ". \"" + body_text + "\""` - Target language is determined by `translate_lang` pref, falling back to `default_chatgpt_lang` - Does **not** support `chatgpt_web` connection type (shows error if configured) -- Result is rendered as a styled banner (green/teal theme) in the message body via `mzta-compose-script.js` +- `translate_display_mode = 'inline'`: result is rendered as a styled banner (green/teal theme) in the message body via `mzta-compose-script.js` +- `translate_display_mode = 'webchat'`: opens AI chat window; webchat shows a "Save as Translation" button to persist the result inline +- `translate_auto = 2` (automatic) always generates inline regardless of `translate_display_mode` - Banner includes refresh (↻) and delete (×) buttons - Cached per-message via `taTranslationStore` / `taStorage` (max 100 entries) - The prompt was originally a regular prompt (`defaultPrompts`) and was moved to `specialPrompts` with `is_special: "1"` and `type: "1"` (reading email only) diff --git a/claude-spec/05-options.md b/claude-spec/05-options.md index 7dbe7217..e1af7369 100644 --- a/claude-spec/05-options.md +++ b/claude-spec/05-options.md @@ -100,6 +100,8 @@ These are generated programmatically at the bottom of `mzta-options-default.js` | `summarize_max_display_length` | `0` | Maximum characters shown in inline summary before truncation. `0` = no limit (show full text). When set, text is truncated at a word boundary and a "See more"/"See less" toggle link is shown. | | `translate` | `true` | Enable email translation | | `translate_auto` | `0` | Auto-translate mode: `0` = disabled, `1` = manual (show button), `2` = automatic (translate on message open) | +| `translate_display_mode` | `'inline'` | Where to display translations: `'inline'` = message pane banner, `'webchat'` = AI chat window. Note: `translate_auto = 2` always uses inline regardless of this setting. | +| `translate_max_display_length` | `0` | Maximum characters shown in inline translation before truncation. `0` = no limit (show full text). When set, text is truncated at a word boundary and a "See more"/"See less" toggle link is shown. | | `translate_lang` | `''` | Target language for translation. Falls back to `default_chatgpt_lang` if empty. | ### Summarize Settings Page (`pages/summarize/`) @@ -132,10 +134,13 @@ The translate settings page provides: - `0` (Disabled) — no inline translations - `1` (Manual) — shows a "Get AI Translation" button in message display - `2` (Automatic) — generates translation immediately when message is opened -3. **Target language** (`translate_lang`) — text input for the destination language. If empty, falls back to `default_chatgpt_lang`. -4. **One editable prompt** — the translation instruction prompt (`prompt_translate_this`) with Save/Reset buttons and placeholder autocomplete. Default text comes from i18n string `prompt_translate_this_full_text`. - -Unlike summarize, translation has no `display_mode` option (always inline) and no max display length setting. +3. **Display mode dropdown** (`translate_display_mode`) — controls where translations are shown: + - `'inline'` — translation banner in the message pane (default) + - `'webchat'` — opens the AI chat window with a "Save as Translation" button + - Note: `translate_auto = 2` always generates inline regardless of this setting. +4. **Max display length** (`translate_max_display_length`) — number input, limits inline translation text to N characters. `0` = no limit. When truncated, a "See more"/"See less" toggle link is appended. +5. **Target language** (`translate_lang`) — text input for the destination language. If empty, falls back to `default_chatgpt_lang`. +6. **One editable prompt** — the translation instruction prompt (`prompt_translate_this`) with Save/Reset buttons and placeholder autocomplete. Default text comes from i18n string `prompt_translate_this_full_text`. ## Adding a New Preference diff --git a/js/mzta-compose-script.js b/js/mzta-compose-script.js index 3fc11537..54a73a87 100644 --- a/js/mzta-compose-script.js +++ b/js/mzta-compose-script.js @@ -1258,6 +1258,9 @@ switch (message.command) { translationHeader.appendChild(translationMenu); translationContainer.appendChild(translationHeader); + const translationTextWrapper = document.createElement('div'); + translationTextWrapper.style.cssText = 'flex-grow: 1;'; + const translationText = document.createElement('div'); translationText.style.cssText = 'white-space: pre-wrap; line-height: 1.5;'; if (translationData.error) { @@ -1265,7 +1268,60 @@ switch (message.command) { } else { translationText.textContent = translationData.translated_text || ''; } - translationContainer.appendChild(translationText); + translationTextWrapper.appendChild(translationText); + + // Expand/collapse for long translations + const maxLenTranslation = translationData.maxDisplayLength || 0; + const fullTranslationText = translationData.translated_text || ''; + if (!translationData.error && 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(() => { + const collapsedHeight = translationText.scrollHeight; + translationText.style.maxHeight = collapsedHeight + 'px'; + }); + + const toggleLinkTranslation = document.createElement('a'); + toggleLinkTranslation.textContent = browser.i18n.getMessage("translate_see_more") || "See more"; + toggleLinkTranslation.href = '#'; + toggleLinkTranslation.style.cssText = 'display: inline-block; margin-top: 4px; font-size: 13px; color: ' + + (isDarkTranslation ? '#6db3f2' : '#1a5fa8') + '; cursor: pointer; text-decoration: underline;'; + + let expandedTranslation = false; + toggleLinkTranslation.addEventListener('click', (e) => { + e.preventDefault(); + if (!expandedTranslation) { + translationText.textContent = fullTranslationText; + const fullHeight = translationText.scrollHeight; + translationText.style.maxHeight = fullHeight + 'px'; + toggleLinkTranslation.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); + translationText.textContent = truncatedTranslation; + }); + toggleLinkTranslation.textContent = browser.i18n.getMessage("translate_see_more") || "See more"; + } + expandedTranslation = !expandedTranslation; + }); + + translationTextWrapper.appendChild(toggleLinkTranslation); + } + + translationContainer.appendChild(translationTextWrapper); const summaryBannerForTranslation = document.getElementById('mzta-summary-banner') || document.getElementById('mzta-summary-generating'); const spamBannerForTranslation = document.getElementById('mzta-spam-report-banner') || document.getElementById('mzta-spam-check-progress'); @@ -1368,7 +1424,7 @@ switch (message.command) { const wrapper = document.getElementById('mzta-translation-trigger-wrapper'); if (wrapper) wrapper.remove(); else translationTriggerBtn.remove(); browser.runtime.sendMessage({ - command: "triggerTranslationGeneration", + command: message.webchat ? "triggerTranslationWebchat" : "triggerTranslationGeneration", headerMessageId: message.headerMessageId }); }; diff --git a/mzta-background.js b/mzta-background.js index 88d0dae4..b0a254d9 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -334,16 +334,17 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { async function _initTranslation() { try { let tabId = sender.tab.id; - let prefs = await browser.storage.sync.get({ translate: prefs_default.translate, translate_auto: prefs_default.translate_auto }); + let prefs = await browser.storage.sync.get({ translate: prefs_default.translate, translate_auto: prefs_default.translate_auto, translate_display_mode: prefs_default.translate_display_mode, translate_max_display_length: prefs_default.translate_max_display_length }); if (!prefs.translate) return; let message = await browser.messageDisplay.getDisplayedMessage(tabId); if (!message) return; + // Always show cached translation if available, regardless of translate_auto let cachedTranslation = await translationStore.loadTranslation(message.headerMessageId); if (cachedTranslation && !cachedTranslation.error) { - browser.tabs.sendMessage(tabId, { command: "showTranslation", data: { ...cachedTranslation } }); + browser.tabs.sendMessage(tabId, { command: "showTranslation", data: { ...cachedTranslation, maxDisplayLength: prefs.translate_max_display_length } }); return; } @@ -352,15 +353,21 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { return; } + // If translate_auto is disabled, don't show button or auto-generate if (prefs.translate_auto === 0) return; + // Auto mode (translate_auto === 2) always generates inline if (prefs.translate_auto === 2) { _generateTranslationForMessage(message.headerMessageId, tabId); return; } // Manual button mode (translate_auto === 1) - browser.tabs.sendMessage(tabId, { command: "showTranslationButton", headerMessageId: message.headerMessageId }); + if (prefs.translate_display_mode === 'inline') { + browser.tabs.sendMessage(tabId, { command: "showTranslationButton", headerMessageId: message.headerMessageId }); + } else { + browser.tabs.sendMessage(tabId, { command: "showTranslationButton", headerMessageId: message.headerMessageId, webchat: true }); + } } catch (e) { taLog.error("Error in initTranslation: " + e); } @@ -374,17 +381,59 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { } _triggerTranslationGeneration(message); break; + case 'triggerTranslationWebchat': + async function _triggerTranslationWebchat(message) { + let tabId = sender.tab.id; + await _openTranslationWebchat(message.headerMessageId, tabId); + } + _triggerTranslationWebchat(message); + break; case 'refreshTranslation': async function _refreshTranslation(message) { let tabId = sender.tab.id; await translationStore.removeTranslation(message.headerMessageId); - await _generateTranslationForMessage(message.headerMessageId, tabId); + let prefs_refresh_tr = await browser.storage.sync.get({ translate_display_mode: prefs_default.translate_display_mode }); + if (prefs_refresh_tr.translate_display_mode === 'webchat') { + await _openTranslationWebchat(message.headerMessageId, tabId); + } else { + await _generateTranslationForMessage(message.headerMessageId, tabId); + } } _refreshTranslation(message); break; case 'removeTranslation': translationStore.removeTranslation(message.headerMessageId); break; + case 'chatgpt_saveTranslation': + async function _saveTranslationFromWebchat(msg) { + try { + let translatedText = msg.text.trim(); + let prefs_tr = await browser.storage.sync.get({ + translate_lang: prefs_default.translate_lang, + default_chatgpt_lang: prefs_default.default_chatgpt_lang, + translate_max_display_length: prefs_default.translate_max_display_length + }); + let lang = prefs_tr.translate_lang || prefs_tr.default_chatgpt_lang || ''; + const translationData = { + translated_text: translatedText, + lang: lang, + headerMessageId: msg.headerMessageId + }; + await translationStore.saveTranslation(translationData, msg.headerMessageId); + try { + browser.tabs.sendMessage(msg.tabId, { + command: "showTranslation", + data: { ...translationData, maxDisplayLength: prefs_tr.translate_max_display_length } + }); + } catch (e) { + taLog.error("Error sending showTranslation to tab: " + e); + } + } catch (error) { + console.error("[ThunderAI] Error saving translation from webchat:", error); + } + } + _saveTranslationFromWebchat(message); + break; case 'chatgpt_close': async function _closeChatGptWindow(window_id) { let prefs_close = await browser.storage.sync.get({chatgpt_win_save_position: prefs_default.chatgpt_win_save_position}); @@ -647,12 +696,13 @@ async function _generateTranslationForMessage(headerMessageId, tabId) { do_debug: prefs_default.do_debug, default_chatgpt_lang: prefs_default.default_chatgpt_lang, translate_lang: prefs_default.translate_lang, + translate_max_display_length: prefs_default.translate_max_display_length, ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type']) }); let cachedTranslation = await translationStore.loadTranslation(headerMessageId); if (cachedTranslation && !cachedTranslation.error) { - browser.tabs.sendMessage(tabId, { command: "showTranslation", data: { ...cachedTranslation } }); + browser.tabs.sendMessage(tabId, { command: "showTranslation", data: { ...cachedTranslation, maxDisplayLength: prefs.translate_max_display_length } }); return; } @@ -704,7 +754,7 @@ async function _generateTranslationForMessage(headerMessageId, tabId) { headerMessageId: headerMessageId }; await translationStore.saveTranslation(translationData, headerMessageId); - browser.tabs.sendMessage(tabId, { command: "showTranslation", data: { ...translationData } }); + browser.tabs.sendMessage(tabId, { command: "showTranslation", data: { ...translationData, maxDisplayLength: prefs.translate_max_display_length } }); taWorkingStatus.stopWorking(); } catch (error) { @@ -862,6 +912,41 @@ async function _openSummaryWebchat(headerMessageId, tabId) { } } +async function _openTranslationWebchat(headerMessageId, tabId) { + try { + const messageResult = await browser.messages.query({ headerMessageId: headerMessageId }); + if (!messageResult || messageResult.messages.length === 0) { + console.error("[ThunderAI] _openTranslationWebchat: Message not found for headerMessageId:", headerMessageId); + return; + } + + const curr_message = messageResult.messages[0]; + const curr_message_full = await browser.messages.getFull(curr_message.id); + + const prefs = await browser.storage.sync.get({ + ...prefs_default, + translate_lang: prefs_default.translate_lang, + default_chatgpt_lang: prefs_default.default_chatgpt_lang + }); + const connectionType = getConnectionType(prefs, {}, 'translate'); + if (connectionType === 'chatgpt_web') { + const errorMsg = browser.i18n.getMessage('translate_chatgpt_web_not_supported'); + await translationStore.saveError(headerMessageId, errorMsg); + browser.tabs.sendMessage(tabId, { command: "showTranslation", data: { error: true, message: errorMsg } }); + return; + } + + const lang = prefs.translate_lang || prefs.default_chatgpt_lang || ''; + const { promptText, promptInfo } = await taPromptUtils.buildTranslationPrompt(curr_message_full, lang); + promptInfo.headerMessageId = headerMessageId; + promptInfo.translationTabId = tabId; + + openChatGPT(promptText, promptInfo.action, tabId, promptInfo.name, promptInfo.need_custom_text, promptInfo); + } catch (error) { + console.error("[ThunderAI] Error opening translation webchat:", error); + } +} + // Listen for messages from ThunderAI-Sparks browser.runtime.onMessageExternal.addListener((message, sender, sendResponse) => { switch (message.action) { diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index a9bbb91e..6a4b13bc 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -143,6 +143,8 @@ export const prefs_default = { summarize_max_display_length: 0, // 0 = no limit, otherwise max chars shown inline translate: true, translate_auto: 0, // 0: disabled, 1: manual button, 2: automatic + translate_display_mode: 'inline', // 'inline' or 'webchat' + translate_max_display_length: 0, // 0 = no limit, otherwise max chars shown inline translate_lang: '', // target language, fallback on default_chatgpt_lang spamfilter_show_msg_panel: true, ...generated_prefs diff --git a/pages/summarize/mzta-summarize.html b/pages/summarize/mzta-summarize.html index ea6f59c7..dd15b738 100644 --- a/pages/summarize/mzta-summarize.html +++ b/pages/summarize/mzta-summarize.html @@ -31,9 +31,9 @@ @@ -44,8 +44,8 @@ diff --git a/pages/translate/mzta-translate.html b/pages/translate/mzta-translate.html index d1a0d187..891dc485 100644 --- a/pages/translate/mzta-translate.html +++ b/pages/translate/mzta-translate.html @@ -31,14 +31,35 @@ + + __MSG_prefs_OptionText_translate_display_mode__ + + + + + + __MSG_prefs_OptionText_translate_max_display_length__ + + + + __MSG_prefs_OptionText_translate_lang__ diff --git a/pages/translate/mzta-translate.js b/pages/translate/mzta-translate.js index 2c1125a9..84e6953e 100644 --- a/pages/translate/mzta-translate.js +++ b/pages/translate/mzta-translate.js @@ -175,6 +175,9 @@ async function restoreOptions() { if (element.id === 'translate_auto') { default_select_value = prefs_default.translate_auto; } + if (element.id === 'translate_display_mode') { + default_select_value = prefs_default.translate_display_mode; + } const restoreValue = result[element.id] ?? default_select_value; let optionExists = Array.from(element.options).some(opt => opt.value === String(restoreValue)); if (element.tomselect) {