File size: 8,005 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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
const plugin = requirePlugin("WechatSI");
const pluginManager = plugin.getRecordRecognitionManager(); // For CN/EN ASR
const nativeRecorderManager = wx.getRecorderManager(); // For JA/KO Recording

Page({
    data: {
        languages: {
            'zh': { name: '中文', flag: 'cn', code: 'zh_CN', native: false },
            'en': { name: 'English', flag: 'us', code: 'en_US', native: false },
            'ja': { name: '日本語', flag: 'jp', code: 'ja_JP', native: true },
            'ko': { name: '한국어', flag: 'kr', code: 'ko_KR', native: true }
        },
        langCodes: ['zh', 'en', 'ja', 'ko'],
        sourceLang: 'zh',
        targetLang: 'en',
        transcript: '',
        outputText: '',
        isRecording: false,
        sourceLanguages: [],
        targetLanguages: [],
        hfSpaceUrl: 'https://dazaozi-wechat-translator-app.hf.space',
        currentManager: null // 'plugin' or 'native'
    },

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

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

    // --- Manager Initializations ---
    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) {
                this.setData({ transcript: '识别结果为空', outputText: '' });
                return;
            }
            this.setData({ transcript: res.result });
            const { sourceLang, targetLang } = this.data;
            const isChineseEnglishPair = (sourceLang === 'zh' || sourceLang === 'en') && (targetLang === 'zh' || targetLang === 'en');
            
            if (isChineseEnglishPair) {
                this.translateViaPlugin(res.result);
            } else {
                this.translateViaHfBridge(res.result, sourceLang, targetLang);
            }
        };
        pluginManager.onError = (res) => this.setData({ isRecording: false, transcript: '插件识别失败', outputText: 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: '原生录音失败', outputText: res.errMsg });
    },

    // --- Recording Logic ---
    startRecording: function () {
        const { sourceLang, languages } = this.data;
        const shouldUseNative = languages[sourceLang].native;

        this.setData({ isRecording: true, currentManager: shouldUseNative ? 'native' : 'plugin' });

        if (shouldUseNative) {
            nativeRecorderManager.start({ format: 'mp3', sampleRate: 16000, numberOfChannels: 1 });
        } else {
            pluginManager.start({ lang: languages[sourceLang].code, translate: false });
        }
    },

    stopRecording: function () {
        if (this.data.currentManager === 'native') {
            nativeRecorderManager.stop();
        } else {
            pluginManager.stop();
        }
    },

    // --- Translation Flows ---
    translateViaPlugin: function(text) {
        const { sourceLang, targetLang, languages } = this.data;
        if (sourceLang === targetLang) {
            this.setData({ outputText: text });
            return;
        }
        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: '插件翻译接口调用失败' })
        });
    },

    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识别请求失败' })
                });
            },
            fail: () => this.setData({ transcript: '读取录音文件失败' })
        });
    },

    translateViaHfBridge: function(text, source, target) {
        this.setData({ outputText: '翻译中 (HF)...' });
        if (source === target) {
            this.setData({ outputText: text });
            return;
        }
        this.translateViaHF(text, source, target, (result) => {
            if (result) {
                this.setData({ outputText: result });
            }
            // Error message is set within translateViaHF
        });
    },

    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 {
                    const errorMsg = res.data.error || `HF翻译失败`;
                    this.setData({ outputText: errorMsg });
                    callback(null);
                }
            },
            fail: () => {
                this.setData({ outputText: `HF翻译请求失败` });
                callback(null);
            }
        });
    }
});