Spaces:
Sleeping
Sleeping
| const plugin = requirePlugin('WechatSI'); | |
| const innerAudioContext = wx.createInnerAudioContext(); | |
| 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' | |
| }, | |
| onLoad: function () { | |
| this.initializeLanguages(); | |
| this.recorderManager = wx.getRecorderManager(); | |
| this.initRecorderManager(); | |
| plugin.getRecordRecognitionManager = wx.getRecordRecognitionManager; | |
| }, | |
| initializeLanguages: function () { | |
| const { langCodes, languages, sourceLang, targetLang } = this.data; | |
| const sourceLanguages = langCodes.map(code => ({ | |
| langCode: code, | |
| name: languages[code].name, | |
| flag: languages[code].flag, | |
| selected: code === sourceLang | |
| })); | |
| const targetLanguages = langCodes.map(code => ({ | |
| langCode: code, | |
| name: languages[code].name, | |
| flag: languages[code].flag, | |
| selected: code === targetLang | |
| })); | |
| this.setData({ sourceLanguages, targetLanguages }); | |
| }, | |
| selectSourceLanguage: function (e) { | |
| const newSourceLang = e.currentTarget.dataset.langCode; | |
| this.setData({ sourceLang: newSourceLang }, () => { | |
| this.initializeLanguages(); | |
| if (this.data.transcript.trim() && this.data.transcript !== '正在聆听...' && this.data.transcript !== '未能识别到语音') { | |
| this.translate(this.data.transcript); | |
| } | |
| }); | |
| }, | |
| selectTargetLanguage: function (e) { | |
| const newTargetLang = e.currentTarget.dataset.langCode; | |
| this.setData({ targetLang: newTargetLang }, () => { | |
| this.initializeLanguages(); | |
| if (this.data.transcript.trim() && this.data.transcript !== '正在聆听...' && this.data.transcript !== '未能识别到语音') { | |
| this.translate(this.data.transcript); | |
| } | |
| }); | |
| }, | |
| swapLanguages: function () { | |
| let { sourceLang, targetLang, transcript, outputText } = this.data; | |
| const tempLang = sourceLang; | |
| sourceLang = targetLang; | |
| targetLang = tempLang; | |
| const tempText = transcript; | |
| transcript = outputText; | |
| outputText = tempText; | |
| this.setData({ sourceLang, targetLang, transcript, outputText }, () => { | |
| this.initializeLanguages(); | |
| if (this.data.transcript.trim() && this.data.transcript !== '正在聆听...' && this.data.transcript !== '未能识别到语音') { | |
| this.translate(this.data.transcript); | |
| } | |
| }); | |
| }, | |
| initRecorderManager: function () { | |
| this.recorderManager.onStart(() => { | |
| this.setData({ isRecording: true, transcript: '正在聆听...', outputText: '' }); | |
| }); | |
| this.recorderManager.onStop((res) => { | |
| this.setData({ isRecording: false }); | |
| if (res.tempFilePath) { | |
| this.uploadAudioForASR(res.tempFilePath); | |
| } else { | |
| this.setData({ transcript: '录音文件创建失败' }); | |
| } | |
| }); | |
| this.recorderManager.onError(() => { | |
| this.setData({ isRecording: false, transcript: '语音识别出错' }); | |
| }); | |
| }, | |
| startRecording: function () { | |
| this.recorderManager.start({ duration: 60000, sampleRate: 16000, numberOfChannels: 1, encodeBitRate: 96000, format: 'mp3' }); | |
| }, | |
| stopRecording: function () { | |
| this.recorderManager.stop(); | |
| }, | |
| uploadAudioForASR: function (filePath) { | |
| this.setData({ transcript: '正在识别...' }); | |
| const fileSystemManager = wx.getFileSystemManager(); | |
| fileSystemManager.readFile({ | |
| filePath: filePath, | |
| encoding: 'base64', | |
| success: (res) => { | |
| const base64Data = res.data; | |
| const dataUri = 'data:audio/mp3;base64,' + base64Data; | |
| wx.request({ | |
| url: `${this.data.hfSpaceUrl}/api/asr`, | |
| method: 'POST', | |
| header: { 'Content-Type': 'application/json' }, | |
| data: { "audio_data_uri": dataUri }, | |
| timeout: 60000, | |
| success: (res) => { | |
| if (res.statusCode === 200 && res.data && res.data.transcript) { | |
| const transcript = res.data.transcript; | |
| this.setData({ transcript: transcript }); | |
| this.translate(transcript); | |
| } else { | |
| this.setData({ transcript: '语音识别失败' }); | |
| } | |
| }, | |
| fail: () => { | |
| this.setData({ transcript: '语音识别请求失败' }); | |
| } | |
| }); | |
| }, | |
| fail: () => { | |
| this.setData({ transcript: '读取音频文件失败' }); | |
| } | |
| }); | |
| }, | |
| translate: function (text) { | |
| if (!text) return; | |
| const { sourceLang, targetLang, languages } = this.data; | |
| const isChineseEnglish = (sourceLang === 'zh' && targetLang === 'en') || (sourceLang === 'en' && targetLang === 'zh'); | |
| if (isChineseEnglish) { | |
| // --- Scenario 1: Direct CN-EN translation using WeChat Plugin --- | |
| 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: '微信翻译失败' }); | |
| } | |
| }, | |
| fail: () => { | |
| this.setData({ outputText: '微信翻译接口调用失败' }); | |
| } | |
| }); | |
| } else { | |
| // --- Scenario 2: Hybrid translation for other languages --- | |
| this.hybridTranslate(text, sourceLang, targetLang); | |
| } | |
| }, | |
| hybridTranslate: function(text, sourceLang, targetLang) { | |
| // If source is not English, first translate to English using WeChat Plugin | |
| if (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) { | |
| const englishText = res.result; | |
| // If the final target is English, we are done. | |
| if (targetLang === 'en') { | |
| this.setData({ outputText: englishText }); | |
| } else { | |
| // Otherwise, send the high-quality English text to HF for the second leg | |
| this.translateViaHF(englishText, 'en', targetLang); | |
| } | |
| } else { | |
| this.setData({ outputText: '混合翻译失败 (1/2)' }); | |
| } | |
| }, | |
| fail: () => { | |
| this.setData({ outputText: '混合翻译接口调用失败 (1/2)' }); | |
| } | |
| }); | |
| } else { | |
| // If source is already English, directly call HF for translation | |
| this.translateViaHF(text, sourceLang, targetLang); | |
| } | |
| }, | |
| translateViaHF: function(text, sourceLang, targetLang) { | |
| this.setData({ 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 && res.data.translated_text) { | |
| this.setData({ outputText: res.data.translated_text }); | |
| } else { | |
| this.setData({ outputText: '混合翻译失败 (2/2)' }); | |
| } | |
| }, | |
| fail: () => { | |
| this.setData({ outputText: '混合翻译出错 (2/2)' }); | |
| } | |
| }); | |
| } | |
| }); |