223 lines
7.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 國際化i18n模組
* =================
*
* 處理多語言支援和界面文字翻譯
* 從後端 /api/translations 載入翻譯數據
*/
class I18nManager {
constructor() {
this.currentLanguage = 'zh-TW';
this.translations = {};
this.loadingPromise = null;
}
async init() {
// 從 localStorage 載入語言偏好
const savedLanguage = localStorage.getItem('language');
if (savedLanguage) {
this.currentLanguage = savedLanguage;
}
// 載入翻譯數據
await this.loadTranslations();
// 應用翻譯
this.applyTranslations();
// 設置語言選擇器
this.setupLanguageSelectors();
// 延遲一點再更新動態內容,確保應用程式已初始化
setTimeout(() => {
this.updateDynamicContent();
}, 100);
}
async loadTranslations() {
if (this.loadingPromise) {
return this.loadingPromise;
}
this.loadingPromise = fetch('/api/translations')
.then(response => response.json())
.then(data => {
this.translations = data;
console.log('翻譯數據載入完成:', Object.keys(this.translations));
// 檢查當前語言是否有翻譯數據
if (!this.translations[this.currentLanguage] || Object.keys(this.translations[this.currentLanguage]).length === 0) {
console.warn(`當前語言 ${this.currentLanguage} 沒有翻譯數據,回退到 zh-TW`);
this.currentLanguage = 'zh-TW';
}
})
.catch(error => {
console.error('載入翻譯數據失敗:', error);
// 使用最小的回退翻譯
this.translations = this.getMinimalFallbackTranslations();
});
return this.loadingPromise;
}
getMinimalFallbackTranslations() {
// 最小的回退翻譯,只包含關鍵項目
return {
'zh-TW': {
'app': {
'title': 'MCP Feedback Enhanced',
'projectDirectory': '專案目錄'
},
'tabs': {
'feedback': '💬 回饋',
'summary': '📋 AI 摘要',
'command': '⚡ 命令',
'settings': '⚙️ 設定'
},
'buttons': {
'cancel': '❌ 取消',
'submit': '✅ 提交回饋'
},
'settings': {
'language': '語言'
}
}
};
}
// 支援巢狀鍵值的翻譯函數,支援參數替換
t(key, params = {}) {
const langData = this.translations[this.currentLanguage] || {};
let translation = this.getNestedValue(langData, key);
// 如果沒有找到翻譯,返回預設值或鍵名
if (!translation) {
return typeof params === 'string' ? params : key;
}
// 如果 params 是字串,當作預設值處理(向後相容)
if (typeof params === 'string') {
return translation;
}
// 參數替換:將 {key} 替換為對應的值
if (typeof params === 'object' && params !== null) {
Object.keys(params).forEach(paramKey => {
const placeholder = `{${paramKey}}`;
translation = translation.replace(new RegExp(placeholder, 'g'), params[paramKey]);
});
}
return translation;
}
getNestedValue(obj, path) {
return path.split('.').reduce((current, key) => {
return current && current[key] !== undefined ? current[key] : null;
}, obj);
}
setLanguage(language) {
if (this.translations[language]) {
this.currentLanguage = language;
localStorage.setItem('language', language);
this.applyTranslations();
// 更新語言選擇器(只更新設定頁面的)
const selector = document.getElementById('settingsLanguageSelect');
if (selector) {
selector.value = language;
}
// 更新 HTML lang 屬性
document.documentElement.lang = language;
console.log('語言已切換到:', language);
} else {
console.warn('不支援的語言:', language);
}
}
applyTranslations() {
// 翻譯所有有 data-i18n 屬性的元素
const elements = document.querySelectorAll('[data-i18n]');
elements.forEach(element => {
const key = element.getAttribute('data-i18n');
const translation = this.t(key);
if (translation && translation !== key) {
element.textContent = translation;
}
});
// 翻譯有 data-i18n-placeholder 屬性的元素
const placeholderElements = document.querySelectorAll('[data-i18n-placeholder]');
placeholderElements.forEach(element => {
const key = element.getAttribute('data-i18n-placeholder');
const translation = this.t(key);
if (translation && translation !== key) {
element.placeholder = translation;
}
});
// 更新動態內容
this.updateDynamicContent();
console.log('翻譯已應用:', this.currentLanguage);
}
updateDynamicContent() {
// 只更新終端歡迎信息,不要覆蓋 AI 摘要
this.updateTerminalWelcome();
}
updateTerminalWelcome() {
const commandOutput = document.getElementById('commandOutput');
if (commandOutput && window.feedbackApp) {
const welcomeTemplate = this.t('dynamic.terminalWelcome');
if (welcomeTemplate && welcomeTemplate !== 'dynamic.terminalWelcome') {
const welcomeMessage = welcomeTemplate.replace('{sessionId}', window.feedbackApp.sessionId || 'unknown');
commandOutput.textContent = welcomeMessage;
}
}
}
setupLanguageSelectors() {
// 舊版下拉選擇器(兼容性保留)
const selector = document.getElementById('settingsLanguageSelect');
if (selector) {
// 設置當前值
selector.value = this.currentLanguage;
// 添加事件監聽器
selector.addEventListener('change', (e) => {
this.setLanguage(e.target.value);
});
}
// 新版現代化語言選擇器
const languageOptions = document.querySelectorAll('.language-option');
if (languageOptions.length > 0) {
// 設置當前語言的活躍狀態
languageOptions.forEach(option => {
const lang = option.getAttribute('data-lang');
if (lang === this.currentLanguage) {
option.classList.add('active');
} else {
option.classList.remove('active');
}
});
}
}
getCurrentLanguage() {
return this.currentLanguage;
}
getAvailableLanguages() {
return Object.keys(this.translations);
}
}
// 創建全域實例
window.i18nManager = new I18nManager();