news 2026/9/14 20:57:38

IoT-For-Beginners 多语言智能定时器实战:语音翻译与文本翻译的双向翻译架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
IoT-For-Beginners 多语言智能定时器实战:语音翻译与文本翻译的双向翻译架构

IoT-For-Beginners 多语言智能定时器实战:语音翻译与文本翻译的双向翻译架构

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

本篇基于 IoT-For-Beginners 课程第 24 课「Support multiple languages」的虚拟设备分支文档,讲解如何为 smart-timer(智能定时器)IoT 设备添加多语言支持:先利用 Speech 服务在语音识别时同步翻译语音,再调用 Translator 服务的 REST API 将回复文本翻译回用户语言。读完本文,你将掌握SpeechTranslationConfig/TranslationRecognizer的用法、TranslatedSpeech事件处理、Translator REST API 的完整请求结构与响应解析,并能完整复现一个「用户说外语 → 设备按服务器语言理解 → 以外语播报回复」的双向翻译系统。

整体架构:服务器语言与用户语言的双轨设计

该方案的核心理念是:核心业务逻辑只用一种语言(服务器语言)运行,语言理解、意图识别、回复文案全部基于该语言构建;用户语言只出现在入口(语音识别)和出口(语音合成)两端

以本课程为例:

  • 服务器语言server_language):用于训练 LUIS 的语言,也是构建回复消息所用的语言(例如英文)。
  • 用户语言language):用户实际说话的语言(例如fr-FR法语,zh-HK粤语)。

这样做的收益是:新增一种语言支持时,无需重新训练 LUIS 或重写业务逻辑,只需在两端插入翻译即可。仓库中同目录的 README 也介绍了这种「翻译加速多语言交付」的思路,并指出其局限——不同语言表达习惯不同,翻译结果可能与训练 LUIS 时给出的示例措辞略有差异。

设备端代码最终形态见仓库 code/virtual-iot-device/smart-timer/app.py,下文各步骤的改动最终都会对应该文件中的代码段。

使用 Speech 服务翻译语音(上行方向)

Speech 服务在语音识别时不仅可以转写为同语言文本,还可以把识别结果直接翻译成其他语言。需要注意:这一能力仅通过 Speech SDK 提供,REST API 并不内置翻译(见 README 中的说明),因此设备端必须使用 SDK 的翻译识别器。

引入翻译识别相关依赖

在 VS Code 中打开smart-timer项目并确认虚拟环境已加载后,在现有 import 语句下添加:

from azure.cognitiveservices import speech from azure.cognitiveservices.speech.translation import SpeechTranslationConfig, TranslationRecognizer import requests

前两个 import 提供语音翻译所需的类;requests库将在后续调用 Translator 服务的 REST API 时使用。对应 app.py 文件顶部的导入区。

定义双语言变量

智能定时器需要两个语言设定。将language变量更新为用户说话的语言,并新增server_language变量表示训练 LUIS 所用的语言

language = '<user language>' server_language = '<server language>'
  • <user language>替换为你将要说出的语言的 locale 名称,例如fr-FR(法语)、zh-HK(粤语)。
  • <server language>替换为训练 LUIS 时使用的语言的 locale 名称。

支持的语言及 locale 名称列表可在 Microsoft 文档的 Language and voice support 页面(Speech to Text 部分)查询。

💡 如果你不会说多种语言,可以使用翻译类应用(如 Bing Translate / Google Translate)把你熟悉语言的句子(例如 "set a 2 minute and 27 second timer")翻译成目标语言,再播放其合成音频对着麦克风朗读。注意语音识别器可能会忽略来自本机播放设备的音频,因此建议用另一台设备播放译文。

用 TranslationRecognizer 替换原有识别器

将原有的recognizer_configrecognizer声明替换为:

translation_config = SpeechTranslationConfig(subscription=speech_api_key, region=location, speech_recognition_language=language, target_languages=(language, server_language)) recognizer = TranslationRecognizer(translation_config=translation_config)

参数说明(对应 app.py L23-L28):

参数取值作用
subscriptionspeech_api_keySpeech 服务订阅密钥
regionlocation资源所在区域
speech_recognition_languagelanguage(用户语言)麦克风输入按该语言进行识别
target_languages(language, server_language)要求同时输出用户语言和服务器语言的译文

这里创建的TranslationRecognizer是「可翻译的语音识别器」:识别输出后会附带多语言译文。

⚠️ 关键陷阱:target_languages必须包含原始语言本身,否则拿不到任何译文。原文档特别强调了这一点,代码里target_languages=(language, server_language)正是这一要求的体现。

处理 TranslatedSpeech 事件

recognized函数的整个函数体替换为:

if args.result.reason == speech.ResultReason.TranslatedSpeech: language_match = next(l for l in args.result.translations if server_language.lower().startswith(l.lower())) text = args.result.translations[language_match] if (len(text) > 0): print(f'Translated text: {text}') message = Message(json.dumps({ 'speech': text })) device_client.send_message(message)

对应 app.py L30-L41。这段代码有三个值得深入理解的细节:

  1. 事件可能因多种原因触发recognized事件不仅在语音完成翻译时触发,在语音被识别但未翻译等情况下也会触发,所以必须先用args.result.reason == speech.ResultReason.TranslatedSpeech过滤出「已翻译」的事件,再处理译文。
  2. 译文字典的键是语言码而非完整 localeargs.result.translations字典以 locale 的语言部分为键——例如请求翻译成fr-FR时,字典中的键是fr而不是fr-FR。因此代码用server_language.lower().startswith(l.lower())做前缀匹配来定位服务器语言对应的条目,而不是直接translations[server_language]精确查找。
  3. 译文随后经 IoT Hub 上行。匹配到的译文被包装为{"speech": text}JSON 消息,通过device_client.send_message发送到 IoT Hub,交由云端函数链(LUIS 语言理解 → 计时触发 → TTS)处理。

运行验证(确保云端函数应用已在运行,然后用用户语言说出一条计时指令):

(.venv) ➜ smart-timer python app.py Connecting Connected Translated text: Set a timer of 2 minutes and 27 seconds.

使用 Translator 服务翻译文本(下行方向)

Speech 服务不支持把文本翻译后再转成语音(其翻译能力只覆盖「语音 → 多语言文本」方向),因此下行方向需要专门的 Translator 服务。该服务提供 REST API,可直接用 HTTP 调用,这也是本步骤选它的原因。

添加 Translator API Key

speech_api_key下方添加:

translator_api_key = '<key>'

<key>替换为你的 Translator 服务资源密钥。Translator 资源的创建命令(az cognitiveservices account create ... --kind TextTranslation及取密钥的az cognitiveservices account keys list)在同目录 README 的「Create a translator resource」小节中有完整说明。

定义 translate_text 函数

say函数上方定义translate_text,它将文本从服务器语言翻译成用户语言。完整实现(对应 app.py L54-L74):

def translate_text(text): url = f'https://api.cognitive.microsofttranslator.com/translate?api-version=3.0' headers = { 'Ocp-Apim-Subscription-Key': translator_api_key, 'Ocp-Apim-Subscription-Region': location, 'Content-type': 'application/json' } params = { 'from': server_language, 'to': language } body = [{ 'text' : text }] response = requests.post(url, headers=headers, params=params, json=body) return response.json()[0]['translations'][0]['text']

逐段解析各要素的设计原因:

URL 与 Headers——该 API 的 URL不是区域特定的api.cognitive.microsofttranslator.com全球统一),区域通过Ocp-Apim-Subscription-Region头传入;认证直接使用Ocp-Apim-Subscription-Key头携带 API key。这与 Speech 服务不同——Speech REST API 需要先向 token issuer(https://{location}.api.cognitive.microsoft.com/sts/v1.0/issuetoken)换取访问令牌,而 Translator 无需此步骤。仓库中云端函数 translate-text/init.py 采用完全相同的 URL 与头结构,可作为云端侧的交叉印证。

params 与 body——params定义源语言(from,服务器语言)和目标语言(to,用户语言);body是要翻译的文本,注意它是数组形式,因为同一次调用可以翻译多个文本块。

响应解析——响应是一个 JSON 数组,其中含一个元素,该元素内有一组translations数组(对应 body 中每个文本块各一条)。对于上述单条请求,响应形如:

[ { "translations": [ { "text": "Chronométrant votre minuterie de 2 minutes 27 secondes.", "to": "fr" } ] } ]

因此response.json()[0]['translations'][0]['text']依次取「第一个元素 → 第一个译文 → 文本字段」,即为翻译结果。

在 say 函数中接入翻译

更新say函数,在生成 SSML 之前先把待播报文本翻译成用户语言,并打印原文与译文便于调试:

print('Original:', text) text = translate_text(text) print('Translated:', text)

之后原有的 SSML 构建逻辑不变:以language(用户语言)和first_voice(匹配用户语言的音色)包裹翻译后的文本,调用speech_synthesizer.speak_ssml(ssml)播报。完整的say函数见 app.py L76-L89——注意其中在播报前会stop_continuous_recognition(),播报完成后再start_continuous_recognition(),避免设备「听到自己说话」。

运行完整程序(云端函数应用保持运行,用用户语言请求一个定时器):

(.venv) ➜ smart-timer python app.py Connecting Connected Translated text: Set a timer of 2 minutes and 27 seconds. Original: 2 minute 27 second timer started. Translated: 2 minute 27 seconde minute a commencé. Original: Times up on your 2 minute 27 second timer. Translated: Chronométrant votre minuterie de 2 minutes 27 secondes.

可以看到完整链路:用户法语音频 → 识别并翻译成英文(Translated text)→ 云端按英文完成 LUIS 理解与计时 → 英文回复文本被翻译回法语 → 以法语播报。

⚠️ 排障提示:由于不同语言表达方式不同,你收到的翻译文本可能与训练 LUIS 时给出的示例措辞不完全一致,导致意图识别失败。此时应向 LUIS 中补充更多该措辞的示例,重新训练(retrain)并重新发布(re-publish)模型。

源码视角:完整数据流与云端印证

从仓库源码结构看,整个多语言系统由设备端与云端函数两部分构成,本文档覆盖的是设备端app.py,而云端侧提供了可对照的实现:

  • 设备端app.py:
    • L23-L28 创建SpeechTranslationConfigTranslationRecognizer(上行翻译);
    • L30-L41 的recognized回调过滤TranslatedSpeech事件,从translations中取服务器语言译文并经 IoT Hub 上行;
    • L54-L74 的translate_text封装 Translator REST API(下行翻译);
    • L76-L89 的say在 SSML 生成前调用translate_text,并以用户语言音色播报。
  • 云端函数应用smart-timer-trigger:
    • text-to-timer/init.py 用 LUIS 的set timer意图与number/time unit实体解析计时时长(这就是为什么上行必须先翻译成服务器语言——LUIS 按服务器语言训练,见 local.settings.json 中的LUIS_*配置);
    • translate-text/init.py 是同一 Translator REST API 的 HTTP 触发封装,从TRANSLATOR_KEY/TRANSLATOR_LOCATION环境变量读取凭据,请求体从from_language/to_language/text三个字段取值,说明该翻译逻辑在设备端与云端是可复用同构的。

两个实现的关键差异也值得注意:设备端把语言硬编码为server_languagelanguage的固定方向,而云端版本通过请求参数动态指定方向,更通用。

配置参数速查

配置项出现位置说明
speech_api_key/location设备端app.pySpeech 服务密钥与区域,用于识别与合成
language设备端app.py用户语言 locale(如fr-FR),决定识别语言、TTS 音色与to语言
server_language设备端app.py训练 LUIS 的语言 locale,决定from语言与上行译文筛选
translator_api_key设备端app.pyTranslator 资源密钥,经Ocp-Apim-Subscription-Key头传递
target_languagesSpeechTranslationConfig必须包含原始语言,否则无译文输出
Ocp-Apim-Subscription-RegionTranslator 请求头区域经头传递,而非体现在 URL 中
from/toTranslator 查询参数源语言与目标语言

小结

本文档实现的是一条完整的双向翻译流水线:上行用 Speech SDK 的TranslationRecognizer在识别的同时拿到服务器语言译文并送入 IoT Hub;下行用 Translator REST API 把服务器语言的回复文本翻回用户语言,再经 SSML 合成语音。两个方向使用不同的服务,是因为 Speech 服务只覆盖「语音→译文」,而「文本→译文」需要 Translator 服务;同时TranslatedSpeech事件过滤、译文字典的语言码键名、以及翻译措辞与 LUIS 训练语料对齐这三点是实际落地时最容易踩坑的地方。

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 20:53:50

微电网中风光储能的优化配置与经济性分析

1. 微网中的可再生能源困境&#xff1a;当风电光伏变成"青春期熊孩子"在微电网系统中&#xff0c;风电和光伏发电就像一群处于青春期的孩子——情绪波动大、行为难以预测。今天还阳光明媚稳定输出&#xff0c;明天就可能阴云密布"摆烂"一整天。这种间歇性和…

作者头像 李华
网站建设 2026/9/14 20:52:52

P128冰雹数问题:从暴力模拟到记忆化搜索优化

P128这道题&#xff0c;圈内通常叫“冰雹数”&#xff0c;我最早是在洛谷上刷到的&#xff0c;题目本身不复杂&#xff0c;但它背后牵出来的考拉兹猜想&#xff08;Collatz conjecture&#xff09;能聊的东西特别多。单看题名&#xff0c;很多人以为就是个模拟题&#xff0c;照…

作者头像 李华
网站建设 2026/9/14 20:51:05

AI编程助手Claude与OpenClaw技术对比与实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华