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_config与recognizer声明替换为:
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):
| 参数 | 取值 | 作用 |
|---|---|---|
subscription | speech_api_key | Speech 服务订阅密钥 |
region | location | 资源所在区域 |
speech_recognition_language | language(用户语言) | 麦克风输入按该语言进行识别 |
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。这段代码有三个值得深入理解的细节:
- 事件可能因多种原因触发。
recognized事件不仅在语音完成翻译时触发,在语音被识别但未翻译等情况下也会触发,所以必须先用args.result.reason == speech.ResultReason.TranslatedSpeech过滤出「已翻译」的事件,再处理译文。 - 译文字典的键是语言码而非完整 locale。
args.result.translations字典以 locale 的语言部分为键——例如请求翻译成fr-FR时,字典中的键是fr而不是fr-FR。因此代码用server_language.lower().startswith(l.lower())做前缀匹配来定位服务器语言对应的条目,而不是直接translations[server_language]精确查找。 - 译文随后经 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 创建
SpeechTranslationConfig与TranslationRecognizer(上行翻译); - L30-L41 的
recognized回调过滤TranslatedSpeech事件,从translations中取服务器语言译文并经 IoT Hub 上行; - L54-L74 的
translate_text封装 Translator REST API(下行翻译); - L76-L89 的
say在 SSML 生成前调用translate_text,并以用户语言音色播报。
- L23-L28 创建
- 云端函数应用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三个字段取值,说明该翻译逻辑在设备端与云端是可复用同构的。
- text-to-timer/init.py 用 LUIS 的
两个实现的关键差异也值得注意:设备端把语言硬编码为server_language→language的固定方向,而云端版本通过请求参数动态指定方向,更通用。
配置参数速查
| 配置项 | 出现位置 | 说明 |
|---|---|---|
speech_api_key/location | 设备端app.py | Speech 服务密钥与区域,用于识别与合成 |
language | 设备端app.py | 用户语言 locale(如fr-FR),决定识别语言、TTS 音色与to语言 |
server_language | 设备端app.py | 训练 LUIS 的语言 locale,决定from语言与上行译文筛选 |
translator_api_key | 设备端app.py | Translator 资源密钥,经Ocp-Apim-Subscription-Key头传递 |
target_languages | SpeechTranslationConfig | 必须包含原始语言,否则无译文输出 |
Ocp-Apim-Subscription-Region | Translator 请求头 | 区域经头传递,而非体现在 URL 中 |
from/to | Translator 查询参数 | 源语言与目标语言 |
小结
本文档实现的是一条完整的双向翻译流水线:上行用 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),仅供参考