const plugin = requirePlugin("WechatSI"); const pluginManager = plugin.getRecordRecognitionManager(); const nativeRecorderManager = wx.getRecorderManager(); Page({ data: { languages: { 'zh': { name: '中文', flag: 'cn', code: 'zh_CN', usePlugin: true }, 'en': { name: 'English', flag: 'us', code: 'en_US', usePlugin: true }, 'ja': { name: '日本語', flag: 'jp', code: 'ja_JP', usePlugin: false }, 'ko': { name: '한국어', flag: 'kr', code: 'ko_KR', usePlugin: false } }, langCodes: ['zh', 'en', 'ja', 'ko'], sourceLang: 'zh', targetLang: 'en', transcript: '', outputText: '', isRecording: false, hfSpaceUrl: 'https://dazaozi-wechat-translator-app.hf.space', currentManagerType: null // 'plugin' or 'native' }, onLoad: function () { this.initializeLanguages(); this.initPluginManager(); this.initNativeRecorderManager(); }, // --- Language Selection Logic (Simplified) --- initializeLanguages: function () { const { languages, sourceLang, targetLang } = this.data; this.setData({ sourceLanguages: Object.keys(languages).map(key => ({ ...languages[key], langCode: key, selected: key === sourceLang })), targetLanguages: Object.keys(languages).map(key => ({ ...languages[key], langCode: key, selected: key === targetLang })) }); }, selectSourceLanguage: function (e) { this.setData({ sourceLang: e.currentTarget.dataset.langCode }, this.initializeLanguages); }, selectTargetLanguage: function (e) { this.setData({ targetLang: e.currentTarget.dataset.langCode }, this.initializeLanguages); }, swapLanguages: function () { this.setData({ sourceLang: this.data.targetLang, targetLang: this.data.sourceLang, transcript: this.data.outputText, outputText: this.data.transcript }, this.initializeLanguages); }, // --- Manager Initializations with Detailed Error Logging --- initPluginManager: function () { pluginManager.onStart = () => this.setData({ transcript: '正在聆听 (插件)...', outputText: '' }); pluginManager.onRecognize = (res) => this.setData({ transcript: res.result }); pluginManager.onStop = (res) => { this.setData({ isRecording: false }); if (!res.result) { return this.setData({ transcript: '识别结果为空' }); } this.setData({ transcript: res.result }); const { sourceLang, targetLang } = this.data; const isPluginTarget = this.data.languages[targetLang].usePlugin; if (isPluginTarget) { this.translateViaPlugin(res.result); } else { this.translateViaHfBridge(res.result, sourceLang, targetLang); } }; pluginManager.onError = (res) => this.setData({ isRecording: false, transcript: `插件错误: ${res.msg}` }); }, initNativeRecorderManager: function () { nativeRecorderManager.onStart = () => this.setData({ transcript: '正在聆听 (原生)...', outputText: '' }); nativeRecorderManager.onStop = (res) => { this.setData({ isRecording: false }); if (res.tempFilePath) { this.uploadAudioForASR(res.tempFilePath); } else { this.setData({ transcript: '原生录音文件获取失败' }); } }; nativeRecorderManager.onError = (res) => this.setData({ isRecording: false, transcript: `原生录音失败: ${res.errMsg}` }); }, // --- Main Record Button Handler --- handleRecordToggle: function() { if (this.data.isRecording) { this.stopRecording(); return; } // Check permissions before starting a new recording wx.getSetting({ success: (res) => { if (!res.authSetting['scope.record']) { wx.authorize({ scope: 'scope.record', success: this.startRecording, fail: () => wx.showToast({ title: '您拒绝了录音权限', icon: 'none' }) }); } else { this.startRecording(); } }, fail: () => wx.showToast({ title: '无法获取权限设置', icon: 'none' }) }); }, startRecording: function () { const { sourceLang, languages } = this.data; const shouldUsePlugin = languages[sourceLang].usePlugin; const managerType = shouldUsePlugin ? 'plugin' : 'native'; this.setData({ isRecording: true, currentManagerType: managerType }); if (shouldUsePlugin) { pluginManager.start({ lang: languages[sourceLang].code, translate: false }); } else { nativeRecorderManager.start({ format: 'mp3', sampleRate: 16000, numberOfChannels: 1 }); } }, stopRecording: function () { if (this.data.currentManagerType === 'native') { nativeRecorderManager.stop(); } else { pluginManager.stop(); } }, // --- Translation Logic --- translateViaPlugin: function(text) { const { sourceLang, targetLang, languages } = this.data; if (sourceLang === targetLang) { return this.setData({ outputText: text }); } this.setData({ outputText: '翻译中 (插件)...' }); plugin.translate({ lfrom: languages[sourceLang].code, lto: languages[targetLang].code, content: text, success: (res) => { if (res.retcode === 0) { this.setData({ outputText: res.result }); } else { this.setData({ outputText: `插件翻译失败: ${res.retcode}` }); } }, fail: (err) => this.setData({ outputText: '插件翻译接口调用失败' }) }); }, uploadAudioForASR: function (filePath) { this.setData({ transcript: '正在识别 (HF)...' }); wx.getFileSystemManager().readFile({ filePath, encoding: 'base64', success: (res) => { wx.request({ url: `${this.data.hfSpaceUrl}/api/asr`, method: 'POST', 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.translateViaHfBridge(transcript, this.data.sourceLang, this.data.targetLang); } else { this.setData({ transcript: 'HF识别失败', outputText: asrRes.data.error || '' }); } }, fail: () => this.setData({ transcript: 'HF识别请求失败' }) }); }}); }, translateViaHfBridge: function(text, source, target) { if (source === target) { return this.setData({ outputText: text }); } this.setData({ outputText: '翻译中 (HF)...' }); this.translateViaHF(text, source, target, (result) => { if (result) { this.setData({ outputText: result }); } }); }, translateViaHF: function(text, sourceLang, targetLang, callback) { wx.request({ url: `${this.data.hfSpaceUrl}/api/translate`, method: 'POST', data: { "text": text, "source_lang": sourceLang, "target_lang": targetLang }, timeout: 45000, success: (res) => { if (res.statusCode === 200 && res.data.translated_text) { callback(res.data.translated_text); } else { this.setData({ outputText: `HF翻译失败: ${res.data.error || '未知错误'}` }); callback(null); } }, fail: () => { this.setData({ outputText: 'HF翻译请求失败' }); callback(null); } }); } });