const plugin = requirePlugin("WechatSI"); const manager = plugin.getRecordRecognitionManager(); Page({ data: { languages: { 'zh': { name: '中文', flag: 'cn', code: 'zh_CN' }, 'en': { name: 'English', flag: 'us', code: 'en_US' }, 'ja': { name: '日本語', flag: 'jp', code: 'ja_JP' }, 'ko': { name: '한국어', flag: 'kr', code: 'ko_KR' } }, langCodes: ['zh', 'en', 'ja', 'ko'], // Use short codes for internal logic sourceLang: 'zh', targetLang: 'en', transcript: '', outputText: '', isRecording: false, sourceLanguages: [], targetLanguages: [], hfSpaceUrl: 'https://dazaozi-wechat-translator-app.hf.space' }, onLoad: function () { this.initializeLanguages(); this.initRecordManager(); }, // --- Language Selection Logic --- initializeLanguages: function () { const { langCodes, languages, sourceLang, targetLang } = this.data; this.setData({ sourceLanguages: langCodes.map(c => ({ langCode: c, name: languages[c].name, flag: languages[c].flag, selected: c === sourceLang })), targetLanguages: langCodes.map(c => ({ langCode: c, name: languages[c].name, flag: languages[c].flag, selected: c === targetLang })) }); }, selectSourceLanguage: function (e) { const newSourceLang = e.currentTarget.dataset.langCode; this.setData({ sourceLang: newSourceLang }, this.initializeLanguages); }, selectTargetLanguage: function (e) { const newTargetLang = e.currentTarget.dataset.langCode; this.setData({ targetLang: newTargetLang }, this.initializeLanguages); }, swapLanguages: function () { const { sourceLang, targetLang } = this.data; this.setData({ sourceLang: targetLang, targetLang: sourceLang, transcript: this.data.outputText, outputText: this.data.transcript }, this.initializeLanguages); }, // --- Recorder Manager Initialization (Hybrid Mode) --- initRecordManager: function () { manager.onStart = () => { this.setData({ transcript: '正在聆听...', outputText: '' }); }; manager.onRecognize = (res) => { // For plugin mode, this updates transcript in real-time // For HF mode, we only care about the final result in onStop const { sourceLang, targetLang } = this.data; const isChineseEnglish = (sourceLang === 'zh' && targetLang === 'en') || (sourceLang === 'en' && targetLang === 'zh'); if (isChineseEnglish) { this.setData({ transcript: res.result }); } }; manager.onStop = (res) => { this.setData({ isRecording: false }); const { sourceLang, targetLang } = this.data; const isChineseEnglish = (sourceLang === 'zh' && targetLang === 'en') || (sourceLang === 'en' && targetLang === 'zh'); if (isChineseEnglish) { // Mode 1: Plugin handles ASR, then we explicitly call plugin.translate if (res.result) { this.setData({ transcript: res.result }); this.translate(res.result); // CRITICAL: Explicitly call translate } else { this.setData({ transcript: '识别结果为空', outputText: '' }); } } else { // Mode 2: Plugin handles ASR, then HF handles translation if (res.tempFilePath) { this.uploadAudioForASR(res.tempFilePath); } else { this.setData({ transcript: '录音文件获取失败' }); } } }; manager.onError = (res) => { this.setData({ isRecording: false, transcript: '识别失败', outputText: res.msg }); }; }, startRecording: function () { const { sourceLang, targetLang } = this.data; const isChineseEnglish = (sourceLang === 'zh' && targetLang === 'en') || (sourceLang === 'en' && targetLang === 'zh'); this.setData({ isRecording: true }); if (isChineseEnglish) { // Use plugin for ASR only (no trans_lang here), then explicitly call plugin.translate manager.start({ lang: this.data.languages[sourceLang].code, // No trans_lang here, as we will call plugin.translate explicitly }); } else { // CRITICAL: Check if source language is supported by plugin for ASR if (sourceLang !== 'zh' && sourceLang !== 'en') { wx.showToast({ title: '当前源语言不支持插件ASR', icon: 'none' }); this.setData({ isRecording: false, transcript: '请选择中文或英文作为源语言' }); return; } // Use plugin for ASR only, then HF for translation manager.start({ lang: this.data.languages[sourceLang].code, // No trans_lang here, as HF will handle translation }); } }, stopRecording: function () { manager.stop(); }, // --- CRITICAL: Re-introduced translate function for plugin mode --- translate: function (text) { const { sourceLang, targetLang } = this.data; if (sourceLang === targetLang) { this.setData({ outputText: text }); return; } this.setData({ outputText: '正在翻译...' }); plugin.translate({ lfrom: this.data.languages[sourceLang].code, lto: this.data.languages[targetLang].code, content: text, success: (res) => { if (res.retcode === 0) { this.setData({ outputText: res.result }); } else { console.error('翻译失败', res); this.setData({ outputText: '翻译失败' }); } }, fail: (err) => { console.error('翻译接口调用失败', err); this.setData({ outputText: '翻译出错' }); } }); }, // --- HF Bridge Translation Flow (for non-CN/EN pairs) --- uploadAudioForASR: function (filePath) { this.setData({ transcript: '正在识别 (1/3)...' }); const fileSystemManager = wx.getFileSystemManager(); fileSystemManager.readFile({ filePath, encoding: 'base64', success: (res) => { wx.request({ url: `${this.data.hfSpaceUrl}/api/asr`, method: 'POST', header: { 'Content-Type': 'application/json' }, data: { "audio_data_uri": `data:audio/mp3;base64,${res.data}` }, timeout: 60000, success: (asrRes) => { if (asrRes.statusCode === 200 && asrRes.data.transcript) { const transcript = asrRes.data.transcript; this.setData({ transcript }); this.fullBackendBridge(transcript, this.data.sourceLang, this.data.targetLang); } else { this.setData({ transcript: 'HF识别失败' }); } }, fail: () => { this.setData({ transcript: 'HF识别请求失败' }); } }); }}); }, fullBackendBridge: function(text, sourceLang, targetLang) { this.setData({ outputText: '翻译中 (2/3)..' }); // Step 1: Translate source (e.g., JA) to English via HF this.translateViaHF(text, sourceLang, 'en', (englishResult) => { if (englishResult) { // Step 2: Translate English result to final target (e.g., ZH) via HF this.setData({ outputText: '翻译中 (3/3)..' }); this.translateViaHF(englishResult, 'en', targetLang, (finalResult) => { if (finalResult) { this.setData({ outputText: finalResult }); } }); } }); }, translateViaHF: function(text, sourceLang, targetLang, callback) { wx.request({ url: `${this.data.hfSpaceUrl}/api/translate`, method: 'POST', header: { 'Content-Type': 'application/json' }, data: { "text": text, "source_lang": sourceLang, "target_lang": targetLang }, timeout: 30000, success: (res) => { if (res.statusCode === 200 && res.data.translated_text) { callback(res.data.translated_text); } else { this.setData({ outputText: `HF翻译失败 (${sourceLang}->${targetLang})` }); callback(null); } }, fail: () => { this.setData({ outputText: `HF翻译请求失败 (${sourceLang}->${targetLang})` }); callback(null); } }); } });