File size: 6,707 Bytes
b3b0b53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
const plugin = requirePlugin('WechatSI');
const innerAudioContext = wx.createInnerAudioContext();
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',
        currentMode: '' // 'plugin-translate' or 'hf-bridge'
    },

    onLoad: function () {
        this.initializeLanguages();
        this.initRecorderManager();
    },

    // --- 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);
    },

    // --- Unified Recording Logic ---
    initRecorderManager: function() {
        manager.onStart = (res) => {
            this.setData({ transcript: '正在聆听...', outputText: '' });
        };

        manager.onRecognize = (res) => {
            this.setData({ transcript: res.result });
        };

        manager.onTranslate = (res) => {
            this.setData({ outputText: res.result });
        };

        manager.onStop = (res) => {
            this.setData({ isRecording: false });
            if (this.data.currentMode === 'plugin-translate') {
                this.setData({ transcript: res.result, outputText: res.translateResult });
            } else if (this.data.currentMode === 'hf-bridge') {
                // For HF mode, we need the audio file to upload.
                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) {
            // Mode 1: Fast, all-in-one ASR and Translation by the plugin
            this.setData({ currentMode: 'plugin-translate' });
            manager.start({ lang: this.data.languages[sourceLang].code, translate: true, lto: this.data.languages[targetLang].code });
        } else {
            // Mode 2: HF Bridge. Use plugin for ASR only, then our backend for translation.
            this.setData({ currentMode: 'hf-bridge' });
            // We ask the plugin to record, but NOT to translate.
            manager.start({ lang: this.data.languages[sourceLang].code, translate: false });
        }
    },

    stopRecording: function () {
        manager.stop();
    },

    // --- HF Bridge Translation Flow ---
    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)..' });
        this.translateViaHF(text, sourceLang, 'en', (englishResult) => {
            if (englishResult) {
                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);
            }
        });
    }
});