微信小程序语音转文字

要在微信中实现语音转文字,可通过官方提供的「微信同声传译插件」或第三方工具完成。以下是具体方案

1. 接入流程

添加插件:登录微信公众平台( https://mp.weixin.qq.com ),进入「设置 → 第三方服务 → 插件管理」,搜索「微信同声传译」并添加,获取其 AppID(如wx069ba97219f66d99)和最新版本号
配置小程序:在app.json中配置插件信息,示例代码如下

{
"plugins": {
"WechatSI": {
"version": "0.3.4", // 填写实际版本号
"provider": "wx069ba97219f66d99"
}
}
}

  • 需确保版本号与插件详情页一致,否则可能导致功能异常

2. 代码实现

引入插件:在 JS 文件中引入插件并获取语音识别管理器

const plugin = requirePlugin('WechatSI');
const manager = plugin.getRecordRecognitionManager();

录音控制:通过按钮绑定录音事件,示例代码

// 开始录音
touchStart() {
this.setData({ recordState: true });
manager.start({ lang: 'zh_CN' }); // 支持zh_CN、en_US、zh_HK等语言
}

// 结束录音
touchEnd() {
this.setData({ recordState: false });
manager.stop();
}

监听识别结果:通过回调函数获取实时或最终识别文本

manager.onRecognize = (res) => {
this.setData({ content: res.result }); // 实时更新文本
};

manager.onStop = (res) => {
console.log('最终识别结果:', res.result); // 结束后完整文本
};

  • 需注意,onRecognize可能包含重复内容,需结合onStop结果进行去重或合并

3. 注意事项

权限申请:需在小程序后台的「用户隐私保护指引」中声明使用录音功能,否则无法获取麦克风权限
以下完整代码
  • app.json

    {
    "pages": [
    "pages/voiceToText/voiceToText"
    ],
    "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "微信同声传译示例",
    "navigationBarTextStyle": "black"
    },
    "plugins": {
    "WechatSI": {
    "version": "0.3.4",
    "provider": "wx069ba97219f66d99"
    }
    },
    "sitemapLocation": "sitemap.json"
    }

  • pages/voiceToText/voiceToText.json

    {
    "navigationBarTitleText": "语音转文字",
    "usingComponents": {}
    }

  • pages/voiceToText/voiceToText.ts

    // 引入微信同声传译插件
    const plugin = requirePlugin('WechatSI')
    // 获取全局唯一的语音识别管理器
    const manager = plugin.getRecordRecognitionManager()

    Page({
    /**
    * 页面的初始数据
    */
    data: {
    // 录音状态:false-未录音,true-正在录音
    isRecording: false,
    // 识别结果文本
    resultText: '',
    // 历史记录列表
    historyList: [] as string[],
    // 录音时长(秒)
    recordDuration: 0,
    // 定时器ID
    timer: 0 as number,
    // 支持的语言列表
    languages: [
    { code: 'zh_CN', name: '中文' },
    { code: 'en_US', name: '英语' },
    { code: 'zh_HK', name: '粤语' }
    ],
    // 当前选中的语言
    currentLang: 'zh_CN'
    },

    /**
    * 生命周期函数--监听页面加载
    */
    onLoad() {
    this.initRecordManager()
    // 从本地存储加载历史记录
    const history = wx.getStorageSync('voiceToTextHistory')
    if (history && Array.isArray(history)) {
    this.setData({ historyList: history })
    }
    },

    /**
    * 初始化录音管理器
    */
    initRecordManager() {
    // 识别中事件
    manager.onRecognize = (res: { result: string }) => {
    console.log('实时识别结果:', res.result)
    this.setData({
    resultText: res.result
    })
    }

    // 识别结束事件
    manager.onStop = (res: { result: string }) => {
    console.log('识别结束:', res)
    let text = res.result

    // 处理可能的空结果
    if (!text) {
    wx.showToast({
    title: '未识别到内容',
    icon: 'none',
    duration: 2000
    })
    text = ''
    } else {
    // 保存到历史记录
    this.addToHistory(text)
    }

    this.setData({
    resultText: text,
    isRecording: false,
    recordDuration: 0
    })

    // 清除定时器
    if (this.data.timer) {
    clearInterval(this.data.timer)
    }
    }

    // 识别错误事件
    manager.onError = (err: { errMsg: string }) => {
    console.error('识别错误:', err)
    wx.showToast({
    title: '识别失败: ' + err.errMsg,
    icon: 'none',
    duration: 3000
    })
    this.setData({
    isRecording: false,
    recordDuration: 0
    })

    if (this.data.timer) {
    clearInterval(this.data.timer)
    }
    }
    },

    /**
    * 开始录音
    */
    startRecord() {
    // 检查录音权限
    wx.getSetting({
    success: (res) => {
    if (!res.authSetting['scope.record']) {
    // 申请录音权限
    wx.authorize({
    scope: 'scope.record',
    success: () => {
    this.startRecording()
    },
    fail: () => {
    wx.showModal({
    title: '权限不足',
    content: '需要录音权限才能使用语音识别功能,请在设置中开启',
    confirmText: '去设置',
    success: (modalRes) => {
    if (modalRes.confirm) {
    wx.openSetting()
    }
    }
    })
    }
    })
    } else {
    this.startRecording()
    }
    }
    })
    },

    /**
    * 实际开始录音的逻辑
    */
    startRecording() {
    // 重置结果
    this.setData({
    resultText: '',
    isRecording: true,
    recordDuration: 0
    })

    // 开始录音
    manager.start({
    lang: this.data.currentLang,
    duration: 60000 // 最长录音时间,单位ms
    })

    // 开始计时
    const timer = setInterval(() => {
    this.setData({
    recordDuration: this.data.recordDuration + 1
    })
    }, 1000)

    this.setData({ timer })
    },

    /**
    * 停止录音
    */
    stopRecord() {
    if (this.data.isRecording) {
    manager.stop()
    }
    },

    /**
    * 添加到历史记录
    */
    addToHistory(text: string) {
    const newHistory = [text, ...this.data.historyList]
    // 限制历史记录数量为20条
    if (newHistory.length > 20) {
    newHistory.pop()
    }

    this.setData({ historyList: newHistory })
    // 保存到本地存储
    wx.setStorageSync('voiceToTextHistory', newHistory)
    },

    /**
    * 清空当前结果
    */
    clearResult() {
    this.setData({ resultText: '' })
    },

    /**
    * 复制结果到剪贴板
    */
    copyResult() {
    if (!this.data.resultText) {
    wx.showToast({
    title: '没有可复制的内容',
    icon: 'none'
    })
    return
    }

    wx.setClipboardData({
    data: this.data.resultText,
    success: () => {
    wx.showToast({
    title: '复制成功',
    icon: 'success'
    })
    }
    })
    },

    /**
    * 清空历史记录
    */
    clearHistory() {
    if (this.data.historyList.length === 0) return

    wx.showModal({
    title: '确认',
    content: '确定要清空所有历史记录吗?',
    success: (res) => {
    if (res.confirm) {
    this.setData({ historyList: [] })
    wx.removeStorageSync('voiceToTextHistory')
    }
    }
    })
    },

    /**
    * 选择语言
    */
    selectLanguage(e: { currentTarget: { dataset: { code: string } } }) {
    const langCode = e.currentTarget.dataset.code
    this.setData({ currentLang: langCode })
    },

    /**
    * 生命周期函数--监听页面卸载
    */
    onUnload() {
    // 页面卸载时停止录音并清除定时器
    if (this.data.isRecording) {
    manager.stop()
    }
    if (this.data.timer) {
    clearInterval(this.data.timer)
    }
    }
    })

  • pages/voiceToText/voiceToText.wxml


    语音转文字




    {{item.name}}











    {{isRecording ? '⏹️' : '🎤'}}
    {{isRecording ? '松开停止' : '按住说话'}}
    {{recordDuration}}s




    历史记录



    {{item}}



  • pages/voiceToText/voiceToText.wxss

    .container {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background-color: #f5f5f5;
    padding: 20rpx;
    }

    .title {
    font-size: 36rpx;
    font-weight: bold;
    text-align: center;
    margin: 30rpx 0;
    color: #333;
    }

    .language-selector {
    display: flex;
    justify-content: center;
    margin-bottom: 20rpx;
    flex-wrap: wrap;
    }

    .lang-item {
    padding: 10rpx 20rpx;
    margin: 0 10rpx 10rpx;
    background-color: #fff;
    border-radius: 20rpx;
    font-size: 28rpx;
    border: 1px solid #eee;
    }

    .lang-selected {
    background-color: #07c160;
    color: white;
    border-color: #07c160;
    }

    .result-container {
    background-color: #fff;
    border-radius: 16rpx;
    padding: 20rpx;
    margin-bottom: 30rpx;
    box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05);
    }

    .result-text {
    width: 100%;
    min-height: 200rpx;
    font-size: 30rpx;
    line-height: 1.6;
    padding: 10rpx;
    border: none;
    resize: none;
    color: #333;
    }

    .result-text::placeholder {
    color: #ccc;
    }

    .action-buttons {
    display: flex;
    justify-content: flex-end;
    margin-top: 10rpx;
    }

    .btn {
    padding: 10rpx 20rpx;
    margin-left: 15rpx;
    font-size: 26rpx;
    border-radius: 8rpx;
    }

    .clear-btn {
    background-color: #f5f5f5;
    color: #666;
    }

    .copy-btn {
    background-color: #07c160;
    color: white;
    }

    .record-control {
    display: flex;
    justify-content: center;
    margin: 40rpx 0 60rpx;
    }

    .record-button {
    width: 160rpx;
    height: 160rpx;
    border-radius: 50%;
    background-color: #ff4d4f;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    color: white;
    box-shadow: 0 5rpx 15rpx rgba(255,77,79,0.4);
    touch-action: manipulation;
    }

    .record-button.recording {
    background-color: #faad14;
    box-shadow: 0 5rpx 15rpx rgba(250,173,20,0.4);
    }

    .record-icon {
    font-size: 60rpx;
    margin-bottom: 5rpx;
    }

    .record-text {
    font-size: 24rpx;
    }

    .record-time {
    font-size: 22rpx;
    margin-top: 5rpx;
    }

    .history-section {
    margin-top: auto;
    background-color: #fff;
    border-radius: 16rpx;
    padding: 20rpx;
    box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
    }

    .history-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15rpx;
    padding-bottom: 10rpx;
    border-bottom: 1px solid #f5f5f5;
    }

    .history-header text {
    font-size: 30rpx;
    font-weight: 500;
    color: #666;
    }

    .clear-history-btn {
    font-size: 24rpx;
    color: #07c160;
    background: none;
    padding: 0;
    }

    .history-list {
    max-height: 300rpx;
    overflow-y: auto;
    }

    .history-item {
    padding: 15rpx 0;
    border-bottom: 1px solid #f5f5f5;
    font-size: 26rpx;
    color: #333;
    line-height: 1.5;
    }

    .history-item:last-child {
    border-bottom: none;
    }

posted @ 2025-09-11 10:49  不完美的完美  阅读(760)  评论(0)    收藏  举报