feat(localization): add trigger words functionality with localization support for UI elements and messages

This commit is contained in:
Will Miao
2025-08-31 15:13:12 +08:00
parent 8303196b57
commit b2428f607c
4 changed files with 71 additions and 28 deletions

View File

@@ -543,6 +543,27 @@
"valuePlaceholder": "Value",
"add": "Add"
},
"triggerWords": {
"label": "Trigger Words",
"noTriggerWordsNeeded": "No trigger word needed",
"edit": "Edit trigger words",
"cancel": "Cancel editing",
"save": "Save changes",
"addPlaceholder": "Type to add or click suggestions below",
"copyWord": "Copy trigger word",
"deleteWord": "Delete trigger word",
"suggestions": {
"noSuggestions": "No suggestions available",
"noTrainedWords": "No trained words or class tokens found in this model. You can manually enter trigger words.",
"classToken": "Class Token",
"classTokenDescription": "Add to your prompt for best results",
"wordSuggestions": "Word Suggestions",
"wordsFound": "{count} words found",
"loading": "Loading suggestions...",
"frequency": "Frequency",
"alreadyAdded": "Already added"
}
},
"description": {
"noDescription": "No model description available",
"failedToLoad": "Failed to load model description",

View File

@@ -543,6 +543,27 @@
"valuePlaceholder": "值",
"add": "添加"
},
"triggerWords": {
"label": "触发词",
"noTriggerWordsNeeded": "无需触发词",
"edit": "编辑触发词",
"cancel": "取消编辑",
"save": "保存更改",
"addPlaceholder": "输入以添加或点击下方建议",
"copyWord": "复制触发词",
"deleteWord": "删除触发词",
"suggestions": {
"noSuggestions": "暂无可用建议",
"noTrainedWords": "此模型未找到训练词或类别标记。您可以手动输入触发词。",
"classToken": "类别标记",
"classTokenDescription": "添加到提示词以获得最佳效果",
"wordSuggestions": "词语建议",
"wordsFound": "已找到 {count} 个词",
"loading": "正在加载建议...",
"frequency": "出现频率",
"alreadyAdded": "已添加"
}
},
"description": {
"noDescription": "无模型描述信息",
"failedToLoad": "加载模型描述失败",

View File

@@ -1,6 +1,6 @@
import { state, getCurrentPageState } from '../state/index.js';
import { showToast } from '../utils/uiHelpers.js';
import { translate } from '../utils/i18n.js';
import { translate } from '../utils/i18nHelpers.js';
import { getStorageItem, getSessionItem, saveMapToStorage } from '../utils/storageHelpers.js';
import {
getCompleteApiConfig,

View File

@@ -4,6 +4,7 @@
* Moved to shared directory for consistency
*/
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
import { translate } from '../../utils/i18nHelpers.js';
import { getModelApiClient } from '../../api/modelApiFactory.js';
/**
@@ -48,9 +49,9 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
// No suggestions case
if ((!trainedWords || trainedWords.length === 0) && !classTokens) {
header.innerHTML = '<span>No suggestions available</span>';
header.innerHTML = `<span>${translate('modals.model.triggerWords.suggestions.noSuggestions')}</span>`;
dropdown.appendChild(header);
dropdown.innerHTML += '<div class="no-suggestions">No trained words or class tokens found in this model. You can manually enter trigger words.</div>';
dropdown.innerHTML += `<div class="no-suggestions">${translate('modals.model.triggerWords.suggestions.noTrainedWords')}</div>`;
return dropdown;
}
@@ -65,8 +66,8 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
const classTokensHeader = document.createElement('div');
classTokensHeader.className = 'metadata-suggestions-header';
classTokensHeader.innerHTML = `
<span>Class Token</span>
<small>Add to your prompt for best results</small>
<span>${translate('modals.model.triggerWords.suggestions.classToken')}</span>
<small>${translate('modals.model.triggerWords.suggestions.classTokenDescription')}</small>
`;
dropdown.appendChild(classTokensHeader);
@@ -77,13 +78,13 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
// Create a special item for the class token
const tokenItem = document.createElement('div');
tokenItem.className = `metadata-suggestion-item class-token-item ${existingWords.includes(classTokens) ? 'already-added' : ''}`;
tokenItem.title = `Class token: ${classTokens}`;
tokenItem.title = `${translate('modals.model.triggerWords.suggestions.classToken')}: ${classTokens}`;
tokenItem.innerHTML = `
<span class="metadata-suggestion-text">${classTokens}</span>
<div class="metadata-suggestion-meta">
<span class="token-badge">Class Token</span>
<span class="token-badge">${translate('modals.model.triggerWords.suggestions.classToken')}</span>
${existingWords.includes(classTokens) ?
'<span class="added-indicator"><i class="fas fa-check"></i></span>' : ''}
`<span class="added-indicator"><i class="fas fa-check"></i></span>` : ''}
</div>
`;
@@ -119,8 +120,8 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
// Add trained words header if we have any
if (trainedWords && trainedWords.length > 0) {
header.innerHTML = `
<span>Word Suggestions</span>
<small>${trainedWords.length} words found</small>
<span>${translate('modals.model.triggerWords.suggestions.wordSuggestions')}</span>
<small>${translate('modals.model.triggerWords.suggestions.wordsFound', { count: trainedWords.length })}</small>
`;
dropdown.appendChild(header);
@@ -139,7 +140,7 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
<span class="metadata-suggestion-text">${word}</span>
<div class="metadata-suggestion-meta">
<span class="trained-word-freq">${frequency}</span>
${isAdded ? '<span class="added-indicator"><i class="fas fa-check"></i></span>' : ''}
${isAdded ? `<span class="added-indicator"><i class="fas fa-check"></i></span>` : ''}
</div>
`;
@@ -166,7 +167,7 @@ function createSuggestionDropdown(trainedWords, classTokens, existingWords = [])
dropdown.appendChild(container);
} else if (!classTokens) {
// If we have neither class tokens nor trained words
dropdown.innerHTML += '<div class="no-suggestions">No word suggestions found in this model. You can manually enter trigger words.</div>';
dropdown.innerHTML += `<div class="no-suggestions">${translate('modals.model.triggerWords.suggestions.noTrainedWords')}</div>`;
}
return dropdown;
@@ -182,22 +183,22 @@ export function renderTriggerWords(words, filePath) {
if (!words.length) return `
<div class="info-item full-width trigger-words">
<div class="trigger-words-header">
<label>Trigger Words</label>
<button class="edit-trigger-words-btn metadata-edit-btn" data-file-path="${filePath}" title="Edit trigger words">
<label>${translate('modals.model.triggerWords.label')}</label>
<button class="edit-trigger-words-btn metadata-edit-btn" data-file-path="${filePath}" title="${translate('modals.model.triggerWords.edit')}">
<i class="fas fa-pencil-alt"></i>
</button>
</div>
<div class="trigger-words-content">
<span class="no-trigger-words">No trigger word needed</span>
<span class="no-trigger-words">${translate('modals.model.triggerWords.noTriggerWordsNeeded')}</span>
<div class="trigger-words-tags" style="display:none;"></div>
</div>
<div class="metadata-edit-controls" style="display:none;">
<button class="metadata-save-btn" title="Save changes">
<i class="fas fa-save"></i> Save
<button class="metadata-save-btn" title="${translate('modals.model.triggerWords.save')}">
<i class="fas fa-save"></i> ${translate('common.actions.save')}
</button>
</div>
<div class="metadata-add-form" style="display:none;">
<input type="text" class="metadata-input" placeholder="Type to add or click suggestions below">
<input type="text" class="metadata-input" placeholder="${translate('modals.model.triggerWords.addPlaceholder')}">
</div>
</div>
`;
@@ -205,20 +206,20 @@ export function renderTriggerWords(words, filePath) {
return `
<div class="info-item full-width trigger-words">
<div class="trigger-words-header">
<label>Trigger Words</label>
<button class="edit-trigger-words-btn metadata-edit-btn" data-file-path="${filePath}" title="Edit trigger words">
<label>${translate('modals.model.triggerWords.label')}</label>
<button class="edit-trigger-words-btn metadata-edit-btn" data-file-path="${filePath}" title="${translate('modals.model.triggerWords.edit')}">
<i class="fas fa-pencil-alt"></i>
</button>
</div>
<div class="trigger-words-content">
<div class="trigger-words-tags">
${words.map(word => `
<div class="trigger-word-tag" data-word="${word}" onclick="copyTriggerWord('${word}')">
<div class="trigger-word-tag" data-word="${word}" onclick="copyTriggerWord('${word}')" title="${translate('modals.model.triggerWords.copyWord')}">
<span class="trigger-word-content">${word}</span>
<span class="trigger-word-copy">
<i class="fas fa-copy"></i>
</span>
<button class="metadata-delete-btn" style="display:none;" onclick="event.stopPropagation();">
<button class="metadata-delete-btn" style="display:none;" onclick="event.stopPropagation();" title="${translate('modals.model.triggerWords.deleteWord')}">
<i class="fas fa-times"></i>
</button>
</div>
@@ -226,12 +227,12 @@ export function renderTriggerWords(words, filePath) {
</div>
</div>
<div class="metadata-edit-controls" style="display:none;">
<button class="metadata-save-btn" title="Save changes">
<i class="fas fa-save"></i> Save
<button class="metadata-save-btn" title="${translate('modals.model.triggerWords.save')}">
<i class="fas fa-save"></i> ${translate('common.actions.save')}
</button>
</div>
<div class="metadata-add-form" style="display:none;">
<input type="text" class="metadata-input" placeholder="Type to add or click suggestions below">
<input type="text" class="metadata-input" placeholder="${translate('modals.model.triggerWords.addPlaceholder')}">
</div>
</div>
`;
@@ -265,7 +266,7 @@ export function setupTriggerWordsEditMode() {
if (isEditMode) {
this.innerHTML = '<i class="fas fa-times"></i>'; // Change to cancel icon
this.title = "Cancel editing";
this.title = translate('modals.model.triggerWords.cancel');
// Store original trigger words for potential restoration
originalTriggerWords = Array.from(triggerWordTags).map(tag => tag.dataset.word);
@@ -302,7 +303,7 @@ export function setupTriggerWordsEditMode() {
// Add loading indicator
const loadingIndicator = document.createElement('div');
loadingIndicator.className = 'metadata-loading';
loadingIndicator.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Loading suggestions...';
loadingIndicator.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${translate('modals.model.triggerWords.suggestions.loading')}`;
addForm.appendChild(loadingIndicator);
// Get currently added trigger words
@@ -329,7 +330,7 @@ export function setupTriggerWordsEditMode() {
} else {
this.innerHTML = '<i class="fas fa-pencil-alt"></i>'; // Change back to edit icon
this.title = "Edit trigger words";
this.title = translate('modals.model.triggerWords.edit');
// Hide edit controls and input form
editControls.style.display = 'none';