translate_display_mode and translate_max_display_length added. see #247

This commit is contained in:
mic 2026-03-28 00:21:45 +01:00
parent 32abc00693
commit ef7483031d
11 changed files with 270 additions and 53 deletions

View file

@ -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": ""

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -31,9 +31,9 @@
<td>
<label>
<select id="summarize_auto" name="summarize_auto" class="option-input">
<option value="0">__MSG_prefs_OptionText_summarize_auto_disabled__</option>
<option value="1">__MSG_prefs_OptionText_summarize_auto_manual__</option>
<option value="2">__MSG_prefs_OptionText_summarize_auto_automatic__</option>
<option value="0">__MSG_prefs_OptionText_action_auto_disabled__</option>
<option value="1">__MSG_prefs_OptionText_action_auto_manual__</option>
<option value="2">__MSG_prefs_OptionText_action_auto_automatic__</option>
</select>
<br>__MSG_prefs_OptionText_summarize_auto_Info__
</label>
@ -44,8 +44,8 @@
<td>
<label>
<select id="summarize_display_mode" name="summarize_display_mode" class="option-input">
<option value="inline">__MSG_prefs_OptionText_summarize_display_mode_inline__</option>
<option value="webchat">__MSG_prefs_OptionText_summarize_display_mode_webchat__</option>
<option value="inline">__MSG_prefs_OptionText_display_mode_inline__</option>
<option value="webchat">__MSG_prefs_OptionText_display_mode_webchat__</option>
</select>
<br>__MSG_prefs_OptionText_summarize_display_mode_Info__
</label>

View file

@ -31,14 +31,35 @@
<td>
<label>
<select id="translate_auto" name="translate_auto" class="option-input">
<option value="0">__MSG_prefs_OptionText_translate_auto_disabled__</option>
<option value="1">__MSG_prefs_OptionText_translate_auto_manual__</option>
<option value="2">__MSG_prefs_OptionText_translate_auto_automatic__</option>
<option value="0">__MSG_prefs_OptionText_action_auto_disabled__</option>
<option value="1">__MSG_prefs_OptionText_action_auto_manual__</option>
<option value="2">__MSG_prefs_OptionText_action_auto_automatic__</option>
</select>
<br>__MSG_prefs_OptionText_translate_auto_Info__
</label>
</td>
</tr>
<tr class="translate_tr">
<td><span class="opt_title">__MSG_prefs_OptionText_translate_display_mode__</span></td>
<td>
<label>
<select id="translate_display_mode" name="translate_display_mode" class="option-input">
<option value="inline">__MSG_prefs_OptionText_display_mode_inline__</option>
<option value="webchat">__MSG_prefs_OptionText_display_mode_webchat__</option>
</select>
<br>__MSG_prefs_OptionText_translate_display_mode_Info__
</label>
</td>
</tr>
<tr class="translate_tr">
<td><span class="opt_title">__MSG_prefs_OptionText_translate_max_display_length__</span></td>
<td>
<label>
<input type="number" id="translate_max_display_length" name="translate_max_display_length" min="0" class="option-input" />
<br>__MSG_prefs_OptionText_translate_max_display_length_Info__
</label>
</td>
</tr>
<tr class="translate_tr">
<td><span class="opt_title">__MSG_prefs_OptionText_translate_lang__</span></td>
<td>

View file

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