feat(多语言): 添加提示文本的多语言支持
- 在状态管理中新增当前语言和可用语言列表字段 - 为提示文本添加多语言输入框生成功能 - 实现根据当前语言自动更新提示文本显示 - 重构重启对话逻辑为独立函数并添加重试机制 - 将提示文本多语言配置保存到本地存储
This commit is contained in:
@@ -52,7 +52,10 @@ const state = {
|
||||
isMuted: false,
|
||||
jobInfo: null,
|
||||
currentInteractionMode: InteractionMode.AUDIO,
|
||||
keepAliveActive: false
|
||||
keepAliveActive: false,
|
||||
currentLanguage: 'zh_CN',
|
||||
availableLanguages: [],
|
||||
tipTextValues: {}
|
||||
};
|
||||
|
||||
const elements = {
|
||||
@@ -86,6 +89,7 @@ const elements = {
|
||||
tipBgOpacity: document.getElementById('tipBgOpacity'),
|
||||
tipBorderRadius: document.getElementById('tipBorderRadius'),
|
||||
tipPadding: document.getElementById('tipPadding'),
|
||||
tipTextFieldsContainer: document.getElementById('tipTextFieldsContainer'),
|
||||
|
||||
messageFontSize: document.getElementById('messageFontSize'),
|
||||
messageFontWeight: document.getElementById('messageFontWeight'),
|
||||
@@ -220,6 +224,15 @@ const eventListeners = {
|
||||
[ICSEventKey.ERROR]: (error) => {
|
||||
logger.error('SDK 错误', error);
|
||||
updateConnectionStatus(false);
|
||||
if (error?.error_code === '999400003' || error?.errorMsg?.includes('MUTE_TIMEOUT')) {
|
||||
logger.warn('检测到静默超时,正在重新开始对话...');
|
||||
state.isChatting = false;
|
||||
state.jobInfo = null;
|
||||
updateUIState();
|
||||
if (state.keepAliveActive) {
|
||||
setTimeout(() => restartChat(), 2000);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
[ICSEventKey.JOB_INFO_CHANGE]: (jobInfo) => {
|
||||
@@ -270,38 +283,7 @@ const eventListeners = {
|
||||
state.jobInfo = null;
|
||||
updateUIState();
|
||||
if (state.keepAliveActive) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
logger.info('正在调用 startChat 重新开始对话...');
|
||||
const chatResult = await HwICSUiSdk.startChat({
|
||||
interactionMode: InteractionMode.AUDIO,
|
||||
wakeupType: WakeupType.VOICE,
|
||||
preCheckPermissions: false
|
||||
});
|
||||
if (chatResult.result) {
|
||||
logger.info('重新开始对话成功', chatResult);
|
||||
state.isChatting = true;
|
||||
updateUIState();
|
||||
} else {
|
||||
logger.warn('重新开始对话返回失败', chatResult);
|
||||
if (chatResult.errorCode === '999400004') {
|
||||
logger.info('对话已存在,尝试 activeInteractionMode');
|
||||
await HwICSUiSdk.activeInteractionMode();
|
||||
state.isChatting = true;
|
||||
updateUIState();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('重新激活失败', error);
|
||||
try {
|
||||
await HwICSUiSdk.activeInteractionMode();
|
||||
state.isChatting = true;
|
||||
updateUIState();
|
||||
} catch (e) {
|
||||
logger.error('activeInteractionMode 也失败', e);
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
setTimeout(() => restartChat(), 2000);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -309,6 +291,48 @@ const eventListeners = {
|
||||
let keepAliveTimer = null;
|
||||
const KEEP_ALIVE_INTERVAL = 10000;
|
||||
|
||||
async function restartChat(retryCount = 0) {
|
||||
const MAX_RETRIES = 3;
|
||||
try {
|
||||
logger.info('正在调用 startChat 重新开始对话...');
|
||||
const chatResult = await HwICSUiSdk.startChat({
|
||||
interactionMode: InteractionMode.AUDIO,
|
||||
wakeupType: WakeupType.VOICE,
|
||||
preCheckPermissions: false
|
||||
});
|
||||
if (chatResult.result) {
|
||||
logger.info('重新开始对话成功', chatResult);
|
||||
state.isChatting = true;
|
||||
updateUIState();
|
||||
} else {
|
||||
logger.warn('重新开始对话返回失败', chatResult);
|
||||
if (chatResult.errorCode === '999400004') {
|
||||
logger.info('对话已存在,尝试 activeInteractionMode');
|
||||
await HwICSUiSdk.activeInteractionMode();
|
||||
state.isChatting = true;
|
||||
updateUIState();
|
||||
} else if (retryCount < MAX_RETRIES) {
|
||||
logger.info(`将在 3 秒后重试 (${retryCount + 1}/${MAX_RETRIES})`);
|
||||
setTimeout(() => restartChat(retryCount + 1), 3000);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('重新激活失败', error);
|
||||
if (retryCount < MAX_RETRIES) {
|
||||
logger.info(`将在 3 秒后重试 (${retryCount + 1}/${MAX_RETRIES})`);
|
||||
setTimeout(() => restartChat(retryCount + 1), 3000);
|
||||
} else {
|
||||
try {
|
||||
await HwICSUiSdk.activeInteractionMode();
|
||||
state.isChatting = true;
|
||||
updateUIState();
|
||||
} catch (e) {
|
||||
logger.error('activeInteractionMode 也失败', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startKeepAlive() {
|
||||
if (keepAliveTimer) return;
|
||||
state.keepAliveActive = true;
|
||||
@@ -409,6 +433,8 @@ async function loadLanguageOptions() {
|
||||
if (langInfo.currentLanguage) {
|
||||
elements.languageSelect.value = langInfo.currentLanguage;
|
||||
}
|
||||
|
||||
generateTipTextFields(langInfo.languageList);
|
||||
} else {
|
||||
elements.languageSelect.innerHTML = '<option value="">无可用语言</option>';
|
||||
}
|
||||
@@ -429,10 +455,14 @@ function updateLanguageSelect(langInfo) {
|
||||
option.textContent = item.language_desc;
|
||||
elements.languageSelect.appendChild(option);
|
||||
});
|
||||
|
||||
generateTipTextFields(langInfo.languageList);
|
||||
}
|
||||
|
||||
if (langInfo.currentLanguage) {
|
||||
elements.languageSelect.value = langInfo.currentLanguage;
|
||||
state.currentLanguage = langInfo.currentLanguage;
|
||||
updateTipTextFromState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,6 +641,94 @@ function clearLogs() {
|
||||
logger.info('日志已清空');
|
||||
}
|
||||
|
||||
async function loadTipTextFields() {
|
||||
if (state.availableLanguages.length > 0) {
|
||||
generateTipTextFields(state.availableLanguages);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.isInitialized) return;
|
||||
|
||||
try {
|
||||
const langInfo = await HwICSUiSdk.getLanguageInfo();
|
||||
if (langInfo.languageList && langInfo.languageList.length > 0) {
|
||||
state.availableLanguages = langInfo.languageList;
|
||||
generateTipTextFields(langInfo.languageList);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('获取语言信息用于提示文本失败', error);
|
||||
}
|
||||
}
|
||||
|
||||
function generateTipTextFields(languageList) {
|
||||
if (!elements.tipTextFieldsContainer) return;
|
||||
|
||||
elements.tipTextFieldsContainer.innerHTML = '';
|
||||
|
||||
if (!languageList || languageList.length === 0) {
|
||||
elements.tipTextFieldsContainer.innerHTML = '<div class="form-group"><label>提示文本</label><p style="color: var(--text-muted); font-size: 0.85rem;">暂无可用的语言信息</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
state.availableLanguages = languageList;
|
||||
|
||||
languageList.forEach(item => {
|
||||
const langCode = item.language;
|
||||
const langDesc = item.language_desc;
|
||||
|
||||
if (!state.tipTextValues[langCode]) {
|
||||
const defaultTexts = {
|
||||
'zh_CN': '点击开始对话',
|
||||
'en_US': 'Click to start conversation'
|
||||
};
|
||||
state.tipTextValues[langCode] = defaultTexts[langCode] || '';
|
||||
}
|
||||
|
||||
const group = document.createElement('div');
|
||||
group.className = 'form-group';
|
||||
group.dataset.langCode = langCode;
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.setAttribute('for', `tipText_${langCode}`);
|
||||
label.textContent = `提示文本 (${langDesc})`;
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.id = `tipText_${langCode}`;
|
||||
input.value = state.tipTextValues[langCode];
|
||||
input.placeholder = `请输入${langDesc}提示文本`;
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
state.tipTextValues[langCode] = input.value;
|
||||
const tipEl = document.querySelector('.caption-container-hub .layout-hub-tip');
|
||||
if (tipEl && state.currentLanguage === langCode) {
|
||||
tipEl.textContent = input.value;
|
||||
}
|
||||
saveCaptionStyleToStorage();
|
||||
});
|
||||
|
||||
group.appendChild(label);
|
||||
group.appendChild(input);
|
||||
elements.tipTextFieldsContainer.appendChild(group);
|
||||
});
|
||||
}
|
||||
|
||||
function updateTipText(tipEl) {
|
||||
const currentLang = state.currentLanguage || 'zh_CN';
|
||||
const text = state.tipTextValues[currentLang] || state.tipTextValues['zh_CN'] || '';
|
||||
if (tipEl) {
|
||||
tipEl.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
function updateTipTextFromState() {
|
||||
const tipEl = document.querySelector('.caption-container-hub .layout-hub-tip');
|
||||
if (!tipEl) return;
|
||||
const currentLang = state.currentLanguage || 'zh_CN';
|
||||
const text = state.tipTextValues[currentLang] || state.tipTextValues['zh_CN'] || '';
|
||||
tipEl.textContent = text;
|
||||
}
|
||||
|
||||
function applyCaptionStyle() {
|
||||
const captionContainer = document.querySelector('.caption-container-hub');
|
||||
if (!captionContainer) {
|
||||
@@ -636,6 +754,7 @@ function applyCaptionStyle() {
|
||||
tipEl.style.backgroundColor = hexToRgba(elements.tipBgColor.value, elements.tipBgOpacity.value / 100);
|
||||
tipEl.style.borderRadius = `${elements.tipBorderRadius.value}px`;
|
||||
tipEl.style.padding = `${elements.tipPadding.value}px`;
|
||||
updateTipText(tipEl);
|
||||
}
|
||||
|
||||
const messageItems = captionContainer.querySelectorAll('.message-item .item-content');
|
||||
@@ -896,6 +1015,7 @@ function saveCaptionStyleToStorage() {
|
||||
tipBgOpacity: elements.tipBgOpacity.value,
|
||||
tipBorderRadius: elements.tipBorderRadius.value,
|
||||
tipPadding: elements.tipPadding.value,
|
||||
tipTextValues: state.tipTextValues,
|
||||
messageFontSize: elements.messageFontSize.value,
|
||||
messageFontWeight: elements.messageFontWeight.value,
|
||||
messageColor: elements.messageColor.value,
|
||||
@@ -978,6 +1098,9 @@ function applySavedCaptionStyle(savedData) {
|
||||
if (savedData.hotIssuesBgOpacity) elements.hotIssuesBgOpacity.value = savedData.hotIssuesBgOpacity;
|
||||
if (savedData.hotIssuesBorderRadius) elements.hotIssuesBorderRadius.value = savedData.hotIssuesBorderRadius;
|
||||
if (savedData.hotIssuesPadding) elements.hotIssuesPadding.value = savedData.hotIssuesPadding;
|
||||
if (savedData.tipTextValues) {
|
||||
state.tipTextValues = savedData.tipTextValues;
|
||||
}
|
||||
|
||||
document.querySelectorAll('#captionPanel input[type="range"]').forEach(slider => {
|
||||
const valueSpan = slider.nextElementSibling;
|
||||
@@ -1397,6 +1520,7 @@ function bindEvents() {
|
||||
elements.toggleConfigBtn.addEventListener('click', () => togglePanel('configPanel'));
|
||||
elements.toggleCaptionBtn.addEventListener('click', () => {
|
||||
togglePanel('captionPanel');
|
||||
loadTipTextFields();
|
||||
if (!captionStylePromptShown && hasSavedCaptionStyle()) {
|
||||
captionStylePromptShown = true;
|
||||
showCaptionStylePrompt((applySaved) => {
|
||||
@@ -1428,6 +1552,8 @@ function bindEvents() {
|
||||
loadLanguageOptions();
|
||||
});
|
||||
elements.languageSelect.addEventListener('change', (e) => {
|
||||
state.currentLanguage = e.target.value;
|
||||
updateTipTextFromState();
|
||||
changeLanguage(e.target.value);
|
||||
});
|
||||
elements.fullscreenBtn.addEventListener('click', toggleFullscreen);
|
||||
|
||||
Reference in New Issue
Block a user