Spaces:
Sleeping
Sleeping
| const plugin = requirePlugin('WechatSI'); | |
| const innerAudioContext = wx.createInnerAudioContext(); | |
| // Get the voice recognition manager from the plugin | |
| 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'], | |
| sourceLang: 'zh', | |
| targetLang: 'en', | |
| transcript: '', | |
| outputText: '', | |
| isRecording: false, | |
| sourceLanguages: [], | |
| targetLanguages: [], | |
| hfSpaceUrl: 'https://dazaozi-wechat-translator-app.hf.space', | |
| currentRecordMode: '' // 'plugin' or 'custom' | |
| }, | |
| onLoad: function () { | |
| this.initializeLanguages(); | |
| // Custom recorder (for HF) | |
| this.customRecorderManager = wx.getRecorderManager(); | |
| this.initCustomRecorderManager(); | |
| // Plugin recorder (for CN/EN) | |
| this.initPluginRecorderManager(); | |
| }, | |
| // --- Language Selection Logic --- | |
| initializeLanguages: function () { | |
| const { langCodes, languages, sourceLang, targetLang } = this.data; | |
| this.setData({ | |
| sourceLanguages: langCodes.map(c => ({ ...languages[c], langCode: c, selected: c === sourceLang })), | |
| targetLanguages: langCodes.map(c => ({ ...languages[c], langCode: c, selected: c === 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); | |
| }, | |
| // --- Recording Logic --- | |
| startRecording: function () { | |
| const { sourceLang, targetLang } = this.data; | |
| const isChineseEnglish = (sourceLang === 'zh' && targetLang === 'en') || (sourceLang === 'en' && targetLang === 'zh'); | |
| this.setData({ isRecording: true, transcript: '正在聆听...', outputText: '' }); | |
| if (isChineseEnglish) { | |
| // Use the ultra-fast WeChat Plugin for both ASR and Translation | |
| this.setData({ currentRecordMode: 'plugin' }); | |
| manager.start({ lang: this.data.languages[sourceLang].code, translate: true, lto: this.data.languages[targetLang].code }); | |
| } else { | |
| // Use our custom HF backend for ASR for other languages | |
| this.setData({ currentRecordMode: 'custom' }); | |
| this.customRecorderManager.start({ duration: 60000, sampleRate: 16000, numberOfChannels: 1, encodeBitRate: 96000, format: 'mp3' }); | |
| } | |
| }, | |
| stopRecording: function () { | |
| if (this.data.currentRecordMode === 'plugin') { | |
| manager.stop(); | |
| } else if (this.data.currentRecordMode === 'custom') { | |
| this.customRecorderManager.stop(); | |
| } | |
| this.setData({ isRecording: false }); | |
| }, | |
| // --- Result Handlers --- | |
| initPluginRecorderManager: function() { | |
| manager.onStart = (res) => { | |
| this.setData({ transcript: '正在识别和翻译...' }); | |
| }; | |
| manager.onRecognize = (res) => { | |
| this.setData({ transcript: res.result }); | |
| }; | |
| manager.onStop = (res) => { | |
| this.setData({ transcript: res.result, outputText: res.translateResult }); | |
| }; | |
| manager.onError = (res) => { | |
| this.setData({ transcript: '微信插件识别失败', outputText: res.msg }); | |
| }; | |
| }, | |
| initCustomRecorderManager: function() { | |
| this.customRecorderManager.onStop = (res) => { | |
| if (res.tempFilePath) { | |
| this.uploadAudioForASR(res.tempFilePath); | |
| } else { | |
| this.setData({ transcript: '录音文件创建失败' }); | |
| } | |
| }; | |
| this.customRecorderManager.onError = () => { | |
| this.setData({ transcript: '语音识别出错' }); | |
| }; | |
| }, | |
| // --- Custom Translation Flow (for non-CN/EN pairs) --- | |
| uploadAudioForASR: function (filePath) { | |
| this.setData({ transcript: '正在识别 (HF)...' }); | |
| 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.hybridTranslate(transcript, this.data.sourceLang, this.data.targetLang); | |
| } else { this.setData({ transcript: 'HF识别失败' }); } | |
| }, | |
| fail: () => { this.setData({ transcript: 'HF识别请求失败' }); } | |
| }); | |
| }}); | |
| }, | |
| hybridTranslate: function(text, sourceLang, targetLang) { | |
| // If source is ZH or EN, use the fast plugin for the first leg of the bridge | |
| if (sourceLang === 'zh' || sourceLang === 'en') { | |
| this.setData({ outputText: '翻译中 (1/2)...' }); | |
| plugin.translate({ lfrom: this.data.languages[sourceLang].code, lto: 'en_US', content: text, success: (res) => { | |
| if (res.retcode === 0) { | |
| this.translateViaHF(res.result, 'en', targetLang); | |
| } else { this.setData({ outputText: '混合翻译失败 (1/2)' }); } | |
| }}); | |
| } else { | |
| // If source is JA or KO, we MUST use our HF backend for the first leg | |
| this.setData({ outputText: '翻译中 (1/2 - HF)..' }); | |
| this.translateViaHF(text, sourceLang, 'en', (englishText) => { | |
| // This is a callback to run after the first leg is complete | |
| this.translateViaHF(englishText, 'en', targetLang); | |
| }); | |
| } | |
| }, | |
| translateViaHF: function(text, sourceLang, targetLang, callback) { | |
| this.setData({ outputText: this.data.outputText + ' (2/2)..' }); | |
| 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) { | |
| if (callback) { | |
| callback(res.data.translated_text); | |
| } else { | |
| this.setData({ outputText: res.data.translated_text }); | |
| } | |
| } else { this.setData({ outputText: 'HF翻译失败' }); } | |
| }, | |
| fail: () => { this.setData({ outputText: 'HF翻译请求失败' }); } | |
| }); | |
| } | |
| }); |