cloneloop 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. cloneloop-0.1.0.dist-info/METADATA +392 -0
  2. cloneloop-0.1.0.dist-info/RECORD +118 -0
  3. cloneloop-0.1.0.dist-info/WHEEL +4 -0
  4. cloneloop-0.1.0.dist-info/entry_points.txt +5 -0
  5. mcp_ai_supervisor/__init__.py +52 -0
  6. mcp_ai_supervisor/__main__.py +551 -0
  7. mcp_ai_supervisor/debug.py +15 -0
  8. mcp_ai_supervisor/desktop_app/__init__.py +29 -0
  9. mcp_ai_supervisor/desktop_app/desktop_app.py +390 -0
  10. mcp_ai_supervisor/helpers/__init__.py +4 -0
  11. mcp_ai_supervisor/helpers/feedback_helpers.py +140 -0
  12. mcp_ai_supervisor/helpers/image_helpers.py +73 -0
  13. mcp_ai_supervisor/i18n.py +14 -0
  14. mcp_ai_supervisor/log_writer.py +16 -0
  15. mcp_ai_supervisor/logging/__init__.py +8 -0
  16. mcp_ai_supervisor/logging/log_parser.py +293 -0
  17. mcp_ai_supervisor/logging/log_tailer.py +367 -0
  18. mcp_ai_supervisor/py.typed +0 -0
  19. mcp_ai_supervisor/server.py +654 -0
  20. mcp_ai_supervisor/utils/__init__.py +28 -0
  21. mcp_ai_supervisor/utils/error_handler.py +10 -0
  22. mcp_ai_supervisor/utils/memory_monitor.py +11 -0
  23. mcp_ai_supervisor/utils/paths.py +7 -0
  24. mcp_ai_supervisor/utils/resource_manager.py +15 -0
  25. mcp_ai_supervisor/utils/structure_checker.py +291 -0
  26. mcp_ai_supervisor/web/__init__.py +26 -0
  27. mcp_ai_supervisor/web/constants/__init__.py +8 -0
  28. mcp_ai_supervisor/web/constants/message_codes.py +173 -0
  29. mcp_ai_supervisor/web/core/__init__.py +30 -0
  30. mcp_ai_supervisor/web/core/agent_bridge.py +957 -0
  31. mcp_ai_supervisor/web/core/auto_responder.py +332 -0
  32. mcp_ai_supervisor/web/core/context_collector.py +124 -0
  33. mcp_ai_supervisor/web/core/conversation_recorder.py +751 -0
  34. mcp_ai_supervisor/web/core/delegate_manager.py +1193 -0
  35. mcp_ai_supervisor/web/core/feedback_mediator.py +259 -0
  36. mcp_ai_supervisor/web/core/message_channel.py +551 -0
  37. mcp_ai_supervisor/web/core/response_builder.py +333 -0
  38. mcp_ai_supervisor/web/core/scene_detector.py +184 -0
  39. mcp_ai_supervisor/web/core/task_state.py +194 -0
  40. mcp_ai_supervisor/web/locales/en/translation.json +643 -0
  41. mcp_ai_supervisor/web/locales/zh-CN/translation.json +629 -0
  42. mcp_ai_supervisor/web/locales/zh-TW/translation.json +648 -0
  43. mcp_ai_supervisor/web/main.py +747 -0
  44. mcp_ai_supervisor/web/managers/__init__.py +8 -0
  45. mcp_ai_supervisor/web/managers/desktop_manager.py +228 -0
  46. mcp_ai_supervisor/web/managers/server_manager.py +170 -0
  47. mcp_ai_supervisor/web/managers/session_manager.py +515 -0
  48. mcp_ai_supervisor/web/managers/workbench_connector.py +186 -0
  49. mcp_ai_supervisor/web/models/__init__.py +13 -0
  50. mcp_ai_supervisor/web/models/feedback_result.py +16 -0
  51. mcp_ai_supervisor/web/models/feedback_session.py +938 -0
  52. mcp_ai_supervisor/web/models/session_cleaner.py +245 -0
  53. mcp_ai_supervisor/web/models/session_commands.py +213 -0
  54. mcp_ai_supervisor/web/models/session_resolver.py +158 -0
  55. mcp_ai_supervisor/web/models/session_timer.py +242 -0
  56. mcp_ai_supervisor/web/routes/__init__.py +12 -0
  57. mcp_ai_supervisor/web/routes/main_routes.py +965 -0
  58. mcp_ai_supervisor/web/routes/settings_routes.py +195 -0
  59. mcp_ai_supervisor/web/routes/ws_handlers.py +270 -0
  60. mcp_ai_supervisor/web/static/css/audio-management.css +545 -0
  61. mcp_ai_supervisor/web/static/css/notification-settings.css +152 -0
  62. mcp_ai_supervisor/web/static/css/prompt-management.css +566 -0
  63. mcp_ai_supervisor/web/static/css/session-management.css +1428 -0
  64. mcp_ai_supervisor/web/static/css/styles.css +2267 -0
  65. mcp_ai_supervisor/web/static/favicon.ico +0 -0
  66. mcp_ai_supervisor/web/static/icon-192.png +0 -0
  67. mcp_ai_supervisor/web/static/icon.svg +11 -0
  68. mcp_ai_supervisor/web/static/index.html +37 -0
  69. mcp_ai_supervisor/web/static/js/app.js +1721 -0
  70. mcp_ai_supervisor/web/static/js/i18n.js +376 -0
  71. mcp_ai_supervisor/web/static/js/modules/audio/audio-manager.js +610 -0
  72. mcp_ai_supervisor/web/static/js/modules/audio/audio-settings-ui.js +732 -0
  73. mcp_ai_supervisor/web/static/js/modules/connection-monitor.js +435 -0
  74. mcp_ai_supervisor/web/static/js/modules/constants/message-codes.js +168 -0
  75. mcp_ai_supervisor/web/static/js/modules/countdown-manager.js +273 -0
  76. mcp_ai_supervisor/web/static/js/modules/drag-drop-handler.js +677 -0
  77. mcp_ai_supervisor/web/static/js/modules/file-upload-manager.js +555 -0
  78. mcp_ai_supervisor/web/static/js/modules/image-handler.js +199 -0
  79. mcp_ai_supervisor/web/static/js/modules/logger.js +404 -0
  80. mcp_ai_supervisor/web/static/js/modules/notification/notification-manager.js +360 -0
  81. mcp_ai_supervisor/web/static/js/modules/notification/notification-settings.js +344 -0
  82. mcp_ai_supervisor/web/static/js/modules/prompt/prompt-input-buttons.js +427 -0
  83. mcp_ai_supervisor/web/static/js/modules/prompt/prompt-manager.js +414 -0
  84. mcp_ai_supervisor/web/static/js/modules/prompt/prompt-modal.js +458 -0
  85. mcp_ai_supervisor/web/static/js/modules/prompt/prompt-settings-ui.js +524 -0
  86. mcp_ai_supervisor/web/static/js/modules/session/session-data-manager.js +1042 -0
  87. mcp_ai_supervisor/web/static/js/modules/session/session-details-modal.js +594 -0
  88. mcp_ai_supervisor/web/static/js/modules/session/session-ui-renderer.js +836 -0
  89. mcp_ai_supervisor/web/static/js/modules/session-manager.js +1059 -0
  90. mcp_ai_supervisor/web/static/js/modules/settings-manager.js +1002 -0
  91. mcp_ai_supervisor/web/static/js/modules/tab-manager.js +235 -0
  92. mcp_ai_supervisor/web/static/js/modules/textarea-height-manager.js +267 -0
  93. mcp_ai_supervisor/web/static/js/modules/ui-manager.js +578 -0
  94. mcp_ai_supervisor/web/static/js/modules/utils/dom-utils.js +392 -0
  95. mcp_ai_supervisor/web/static/js/modules/utils/status-utils.js +403 -0
  96. mcp_ai_supervisor/web/static/js/modules/utils/time-utils.js +440 -0
  97. mcp_ai_supervisor/web/static/js/modules/utils.js +557 -0
  98. mcp_ai_supervisor/web/static/js/modules/websocket-manager.js +875 -0
  99. mcp_ai_supervisor/web/static/js/modules/workbench-notification.js +103 -0
  100. mcp_ai_supervisor/web/static/js/vendor/marked.min.js +6 -0
  101. mcp_ai_supervisor/web/static/js/vendor/purify.min.js +3 -0
  102. mcp_ai_supervisor/web/templates/components/image-upload.html +43 -0
  103. mcp_ai_supervisor/web/templates/components/settings-card.html +58 -0
  104. mcp_ai_supervisor/web/templates/components/status-indicator.html +31 -0
  105. mcp_ai_supervisor/web/templates/components/toggle-switch.html +19 -0
  106. mcp_ai_supervisor/web/templates/feedback.html +1198 -0
  107. mcp_ai_supervisor/web/templates/index.html +378 -0
  108. mcp_ai_supervisor/web/utils/__init__.py +12 -0
  109. mcp_ai_supervisor/web/utils/browser.py +35 -0
  110. mcp_ai_supervisor/web/utils/compression_config.py +195 -0
  111. mcp_ai_supervisor/web/utils/compression_monitor.py +314 -0
  112. mcp_ai_supervisor/web/utils/ide_opener.py +286 -0
  113. mcp_ai_supervisor/web/utils/network.py +66 -0
  114. mcp_ai_supervisor/web/utils/port_manager.py +340 -0
  115. mcp_ai_supervisor/web/utils/session_cleanup_manager.py +543 -0
  116. mcp_ai_supervisor/web/utils/workbench_client.py +899 -0
  117. mcp_ai_supervisor/workbench/__init__.py +5 -0
  118. mcp_ai_supervisor/workbench/__main__.py +5 -0
@@ -0,0 +1,376 @@
1
+ /**
2
+ * 國際化(i18n)模組
3
+ * =================
4
+ *
5
+ * 處理多語言支援和界面文字翻譯
6
+ * 從後端 /api/translations 載入翻譯數據
7
+ */
8
+
9
+ class I18nManager {
10
+ constructor() {
11
+ this.currentLanguage = this.getDefaultLanguage();
12
+ this.translations = {};
13
+ this.loadingPromise = null;
14
+ }
15
+
16
+ getDefaultLanguage() {
17
+ // 1. 先檢查本地儲存的設定
18
+ const savedLanguage = localStorage.getItem('language');
19
+ if (savedLanguage && ['zh-TW', 'zh-CN', 'en'].includes(savedLanguage)) {
20
+ console.log('🌐 使用儲存的語言設定:', savedLanguage);
21
+ return savedLanguage;
22
+ }
23
+
24
+ // 2. 檢查瀏覽器語言
25
+ const browserLang = navigator.language || navigator.userLanguage;
26
+ console.log('🌐 瀏覽器語言:', browserLang);
27
+
28
+ if (browserLang.startsWith('zh-TW') || browserLang.includes('Hant')) {
29
+ console.log('🌐 偵測到繁體中文環境');
30
+ return 'zh-TW';
31
+ }
32
+ if (browserLang.startsWith('zh') || browserLang.includes('Hans')) {
33
+ console.log('🌐 偵測到簡體中文環境');
34
+ return 'zh-CN';
35
+ }
36
+ if (browserLang.startsWith('en')) {
37
+ console.log('🌐 偵測到英文環境');
38
+ return 'en';
39
+ }
40
+
41
+ // 3. 預設使用繁體中文
42
+ console.log('🌐 使用預設語言: zh-TW');
43
+ return 'zh-TW';
44
+ }
45
+
46
+ async init() {
47
+ console.log(`i18nManager 使用預設語言: ${this.currentLanguage}`);
48
+
49
+ // 載入翻譯數據
50
+ await this.loadTranslations();
51
+
52
+ // 應用翻譯
53
+ this.applyTranslations();
54
+
55
+ // 設置語言選擇器
56
+ this.setupLanguageSelectors();
57
+
58
+ // 延遲一點再更新動態內容,確保應用程式已初始化
59
+ setTimeout(() => {
60
+ this.updateDynamicContent();
61
+ }, 100);
62
+ }
63
+
64
+ async loadTranslations() {
65
+ if (this.loadingPromise) {
66
+ return this.loadingPromise;
67
+ }
68
+
69
+ this.loadingPromise = fetch('/api/translations')
70
+ .then(response => response.json())
71
+ .then(data => {
72
+ this.translations = data;
73
+ console.log('翻譯數據載入完成:', Object.keys(this.translations));
74
+
75
+ // 檢查當前語言是否有翻譯數據
76
+ if (!this.translations[this.currentLanguage] || Object.keys(this.translations[this.currentLanguage]).length === 0) {
77
+ console.warn(`當前語言 ${this.currentLanguage} 沒有翻譯數據,回退到 zh-TW`);
78
+ this.currentLanguage = 'zh-TW';
79
+ }
80
+ })
81
+ .catch(error => {
82
+ console.error('載入翻譯數據失敗:', error);
83
+ // 使用最小的回退翻譯
84
+ this.translations = this.getMinimalFallbackTranslations();
85
+ });
86
+
87
+ return this.loadingPromise;
88
+ }
89
+
90
+ getMinimalFallbackTranslations() {
91
+ // 最小的回退翻譯,只包含關鍵項目
92
+ return {
93
+ 'zh-TW': {
94
+ 'app': {
95
+ 'title': 'MCP AI Supervisor',
96
+ 'projectDirectory': '專案目錄'
97
+ },
98
+ 'tabs': {
99
+ 'feedback': '回饋',
100
+ 'summary': 'AI 摘要',
101
+ 'command': '命令',
102
+ 'settings': '設定'
103
+ },
104
+ 'buttons': {
105
+ 'cancel': '取消',
106
+ 'submit': '發送'
107
+ },
108
+ 'settings': {
109
+ 'language': '語言'
110
+ }
111
+ }
112
+ };
113
+ }
114
+
115
+ // 支援巢狀鍵值的翻譯函數,支援參數替換
116
+ t(key, params = {}) {
117
+ const langData = this.translations[this.currentLanguage] || {};
118
+ let translation = this.getNestedValue(langData, key);
119
+
120
+ // 如果沒有找到翻譯,返回預設值或鍵名
121
+ if (!translation) {
122
+ return typeof params === 'string' ? params : key;
123
+ }
124
+
125
+ // 如果 params 是字串,當作預設值處理(向後相容)
126
+ if (typeof params === 'string') {
127
+ return translation;
128
+ }
129
+
130
+ // 參數替換:將 {key} 替換為對應的值
131
+ if (typeof params === 'object' && params !== null) {
132
+ Object.keys(params).forEach(paramKey => {
133
+ const placeholder = `{${paramKey}}`;
134
+ translation = translation.replace(new RegExp(placeholder, 'g'), params[paramKey]);
135
+ });
136
+ }
137
+
138
+ return translation;
139
+ }
140
+
141
+ getNestedValue(obj, path) {
142
+ return path.split('.').reduce((current, key) => {
143
+ return current && current[key] !== undefined ? current[key] : null;
144
+ }, obj);
145
+ }
146
+
147
+ setLanguage(language) {
148
+ console.log(`🔄 i18nManager.setLanguage() 被調用: ${this.currentLanguage} -> ${language}`);
149
+ if (this.translations[language]) {
150
+ this.currentLanguage = language;
151
+ this.applyTranslations();
152
+
153
+ // 更新所有語言選擇器(包括現代化版本)
154
+ this.setupLanguageSelectors();
155
+
156
+ // 更新 HTML lang 屬性
157
+ document.documentElement.lang = language;
158
+
159
+ console.log(`✅ i18nManager 語言已切換到: ${language}`);
160
+ } else {
161
+ console.warn(`❌ i18nManager 不支援的語言: ${language}`);
162
+ }
163
+ }
164
+
165
+ applyTranslations() {
166
+ // 翻譯所有有 data-i18n 屬性的元素
167
+ const elements = document.querySelectorAll('[data-i18n]');
168
+ elements.forEach(element => {
169
+ const key = element.getAttribute('data-i18n');
170
+ const translation = this.t(key);
171
+ if (translation && translation !== key) {
172
+ element.textContent = translation;
173
+ }
174
+ });
175
+
176
+ // 翻譯有 data-i18n-placeholder 屬性的元素
177
+ const placeholderElements = document.querySelectorAll('[data-i18n-placeholder]');
178
+ placeholderElements.forEach(element => {
179
+ const key = element.getAttribute('data-i18n-placeholder');
180
+ const translation = this.t(key);
181
+ if (translation && translation !== key) {
182
+ element.placeholder = translation;
183
+ }
184
+ });
185
+
186
+ // 翻譯有 data-i18n-title 屬性的元素
187
+ const titleElements = document.querySelectorAll('[data-i18n-title]');
188
+ titleElements.forEach(element => {
189
+ const key = element.getAttribute('data-i18n-title');
190
+ const translation = this.t(key);
191
+ if (translation && translation !== key) {
192
+ element.title = translation;
193
+ }
194
+ });
195
+
196
+ // 翻譯有 data-i18n-aria-label 屬性的元素
197
+ const ariaLabelElements = document.querySelectorAll('[data-i18n-aria-label]');
198
+ ariaLabelElements.forEach(element => {
199
+ const key = element.getAttribute('data-i18n-aria-label');
200
+ const translation = this.t(key);
201
+ if (translation && translation !== key) {
202
+ element.setAttribute('aria-label', translation);
203
+ }
204
+ });
205
+
206
+ // 更新動態內容
207
+ this.updateDynamicContent();
208
+
209
+ // 更新音效選擇器翻譯
210
+ this.updateAudioSelectTranslations();
211
+
212
+ console.log('翻譯已應用:', this.currentLanguage);
213
+ }
214
+
215
+ updateDynamicContent() {
216
+ // 只更新終端歡迎信息,不要覆蓋 AI 摘要
217
+ this.updateTerminalWelcome();
218
+
219
+ // 更新會話管理相關的動態內容
220
+ this.updateSessionManagementContent();
221
+
222
+ // 更新連線監控相關的動態內容
223
+ this.updateConnectionMonitorContent();
224
+
225
+ // 更新提示詞按鈕文字
226
+ this.updatePromptInputButtons();
227
+
228
+ // 更新應用程式中的動態狀態文字(使用新的模組化架構)
229
+ if (window.feedbackApp && window.feedbackApp.isInitialized) {
230
+ // 更新 UI 狀態
231
+ if (window.feedbackApp.uiManager && typeof window.feedbackApp.uiManager.updateUIState === 'function') {
232
+ window.feedbackApp.uiManager.updateUIState();
233
+ }
234
+
235
+ if (window.feedbackApp.uiManager && typeof window.feedbackApp.uiManager.updateStatusIndicator === 'function') {
236
+ window.feedbackApp.uiManager.updateStatusIndicator();
237
+ }
238
+
239
+
240
+ }
241
+ }
242
+
243
+ updateTerminalWelcome() {
244
+ const commandOutput = document.getElementById('commandOutput');
245
+ if (commandOutput && window.feedbackApp && window.feedbackApp.isInitialized) {
246
+ const welcomeTemplate = this.t('dynamic.terminalWelcome');
247
+ if (welcomeTemplate && welcomeTemplate !== 'dynamic.terminalWelcome') {
248
+ // 使用 currentSessionId 而不是 sessionId
249
+ const sessionId = window.feedbackApp.currentSessionId || window.feedbackApp.sessionId || 'unknown';
250
+ const welcomeMessage = welcomeTemplate.replace('{sessionId}', sessionId);
251
+ commandOutput.textContent = welcomeMessage;
252
+ }
253
+ }
254
+ }
255
+
256
+ updateSessionManagementContent() {
257
+ // 更新會話管理面板中的動態文字
258
+ if (window.feedbackApp && window.feedbackApp.sessionManager) {
259
+ // 觸發會話管理器重新渲染,這會使用最新的翻譯
260
+ if (typeof window.feedbackApp.sessionManager.updateDisplay === 'function') {
261
+ window.feedbackApp.sessionManager.updateDisplay();
262
+ }
263
+
264
+ // 重新渲染統計資訊以更新時間單位
265
+ if (window.feedbackApp.sessionManager.dataManager &&
266
+ window.feedbackApp.sessionManager.uiRenderer) {
267
+ const stats = window.feedbackApp.sessionManager.dataManager.getStats();
268
+ window.feedbackApp.sessionManager.uiRenderer.renderStats(stats);
269
+ console.log('🌐 已更新統計資訊的語言顯示');
270
+
271
+ // 重新渲染會話歷史以更新所有動態創建的元素
272
+ const sessionHistory = window.feedbackApp.sessionManager.dataManager.getSessionHistory();
273
+ window.feedbackApp.sessionManager.uiRenderer.renderSessionHistory(sessionHistory);
274
+ console.log('🌐 已更新會話歷史的語言顯示');
275
+ }
276
+ }
277
+
278
+ // 更新狀態徽章文字
279
+ const statusBadges = document.querySelectorAll('.status-badge');
280
+ statusBadges.forEach(badge => {
281
+ const statusClass = Array.from(badge.classList).find(cls =>
282
+ ['waiting', 'active', 'completed', 'error', 'connecting', 'connected', 'disconnected'].includes(cls)
283
+ );
284
+ if (statusClass && window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.Status) {
285
+ badge.textContent = window.MCPFeedback.Utils.Status.getStatusText(statusClass);
286
+ }
287
+ });
288
+ }
289
+
290
+ updateConnectionMonitorContent() {
291
+ // 更新連線監控器中的動態文字
292
+ if (window.feedbackApp && window.feedbackApp.connectionMonitor) {
293
+ // 觸發連線監控器重新更新顯示
294
+ if (typeof window.feedbackApp.connectionMonitor.updateDisplay === 'function') {
295
+ window.feedbackApp.connectionMonitor.updateDisplay();
296
+ }
297
+ }
298
+
299
+ // 更新連線狀態文字
300
+ const statusText = document.querySelector('.status-text');
301
+ if (statusText && window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.Status) {
302
+ // 從元素的類名或數據屬性中獲取狀態
303
+ const indicator = statusText.closest('.connection-indicator');
304
+ if (indicator) {
305
+ const statusClass = Array.from(indicator.classList).find(cls =>
306
+ ['connecting', 'connected', 'disconnected', 'reconnecting'].includes(cls)
307
+ );
308
+ if (statusClass) {
309
+ statusText.textContent = window.MCPFeedback.Utils.Status.getConnectionStatusText(statusClass);
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ updatePromptInputButtons() {
316
+ // 更新提示詞輸入按鈕的文字和狀態
317
+ if (window.feedbackApp && window.feedbackApp.promptInputButtons) {
318
+ // 觸發提示詞按鈕更新文字
319
+ if (typeof window.feedbackApp.promptInputButtons.updateButtonTexts === 'function') {
320
+ window.feedbackApp.promptInputButtons.updateButtonTexts();
321
+ }
322
+ // 觸發提示詞按鈕更新狀態(包括 tooltip)
323
+ if (typeof window.feedbackApp.promptInputButtons.updateButtonStates === 'function') {
324
+ window.feedbackApp.promptInputButtons.updateButtonStates();
325
+ }
326
+ }
327
+ }
328
+
329
+ setupLanguageSelectors() {
330
+ // 設定頁籤的下拉選擇器
331
+ const selector = document.getElementById('settingsLanguageSelect');
332
+ if (selector) {
333
+ // 只設置當前值,不綁定事件(讓 SettingsManager 統一處理)
334
+ selector.value = this.currentLanguage;
335
+ console.log(`🔧 setupLanguageSelectors: 設置 select.value = ${this.currentLanguage}`);
336
+
337
+ // 不再綁定事件監聽器,避免與 SettingsManager 衝突
338
+ // 事件處理完全交由 SettingsManager 負責
339
+ }
340
+
341
+ // 新版現代化語言選擇器
342
+ const languageOptions = document.querySelectorAll('.language-option');
343
+ if (languageOptions.length > 0) {
344
+ // 只設置當前語言的活躍狀態,不綁定事件
345
+ languageOptions.forEach(option => {
346
+ const lang = option.getAttribute('data-lang');
347
+ if (lang === this.currentLanguage) {
348
+ option.classList.add('active');
349
+ } else {
350
+ option.classList.remove('active');
351
+ }
352
+ });
353
+ // 事件監聽器由 SettingsManager 統一處理,避免重複綁定
354
+ }
355
+ }
356
+
357
+ updateAudioSelectTranslations() {
358
+ // 更新音效設定區域的所有翻譯
359
+ if (window.feedbackApp && window.feedbackApp.audioSettingsUI) {
360
+ if (typeof window.feedbackApp.audioSettingsUI.updateTranslations === 'function') {
361
+ window.feedbackApp.audioSettingsUI.updateTranslations();
362
+ }
363
+ }
364
+ }
365
+
366
+ getCurrentLanguage() {
367
+ return this.currentLanguage;
368
+ }
369
+
370
+ getAvailableLanguages() {
371
+ return Object.keys(this.translations);
372
+ }
373
+ }
374
+
375
+ // 創建全域實例
376
+ window.i18nManager = new I18nManager();