HarmonyOS APP开发:语音翻译与实时同传
HarmonyOS APP开发:语音翻译与实时同传
核心要点:掌握HarmonyOS语音翻译(Speech Translation)引擎配置、ASR+MT+TTS级联架构、实时流式翻译、同传模式实现,以及多语言对与翻译质量优化。
一、背景与动机
你有没有看过那种国际会议的直播?台上嘉宾说着英语,屏幕下方实时出现中文字幕,几乎和说话同步——这就是同声传译(Simultaneous Interpretation),简称"同传"。
以前同传是高度专业的人类工作,译员需要经过数年训练,而且一场会议至少需要两名译员轮换。但现在,AI正在改变这一切。
语音翻译 = 语音识别(ASR)+ 机器翻译(MT)+ 语音合成(TTS),三者级联构成了完整的"听→翻→说"管线。HarmonyOS将这个管线封装成了统一的语音翻译API,开发者无需分别调用三个引擎。
语音翻译的应用场景非常广泛:
- 国际会议:实时字幕+语音同传,参会者各听各的语言
- 跨境旅游:对着手机说话,自动翻译成当地语言播放
- 外语学习:实时翻译对比,边听边学
- 跨国客服:客服说中文,客户听英文,实时双向翻译
- 影视字幕:自动生成多语言字幕
HarmonyOS的语音翻译能力有几个亮点:
- 端云协同:短句离线翻译,长句云端翻译,平衡延迟和准确率
- 流式输出:边听边翻,不等说完就出结果
- 多语言对:中英、中日、中韩等主流语言对全覆盖
- 同传模式:ASR+MT+TTS一体化,无需手动拼接
二、核心原理
2.1 语音翻译级联架构
语音翻译的核心挑战在于:ASR、MT、TTS三个模块如何高效协同?最直觉的方式是"串行级联",但延迟太高。HarmonyOS采用的是"流式级联"——三个模块并行运行,通过流式接口传递中间结果。
flowchart LR
A[语音输入] --> B[ASR流式识别]
B -->|中间文本| C[MT流式翻译]
B -->|最终文本| C
C -->|中间译文| D[TTS流式合成]
C -->|最终译文| D
D --> E[译文语音输出]
F[延迟优化] -.-> B
F -.-> C
F -.-> D
G[缓存与重排] -.-> C
classDef primary fill:#4FC3F7,stroke:#0288D1,color:#000
classDef warning fill:#FFB74D,stroke:#F57C00,color:#000
classDef error fill:#EF5350,stroke:#C62828,color:#FFF
classDef info fill:#81C784,stroke:#388E3C,color:#000
classDef purple fill:#CE93D8,stroke:#7B1FA2,color:#000
class A,E primary
class B,C,D purple
class F,G warning
2.2 串行级联 vs 流式级联
| 维度 | 串行级联 | 流式级联 |
|---|---|---|
| 流程 | ASR完成 → MT开始 → TTS开始 | ASR出中间结果 → MT开始 → TTS开始 |
| 延迟 | 3-5秒 | 0.5-1.5秒 |
| 准确率 | 高(完整句子翻译更准) | 中等(中间结果可能被修正) |
| 用户体验 | 等待感明显 | 近乎实时 |
| 实现复杂度 | 简单 | 复杂(需要处理结果修正) |
HarmonyOS默认使用流式级联,开发者也可以选择串行模式以获得更高准确率。
2.3 翻译质量优化策略
流式翻译最大的问题是中间结果不稳定——ASR的中间文本可能被修正,导致MT的翻译也需要修正。HarmonyOS通过以下策略优化:
- 增量翻译:只翻译新增的文本,不重复翻译已确认的部分
- 重排机制:当ASR修正时,MT只重排受影响的部分
- 置信度加权:低置信度的ASR结果翻译后标记为"暂定",高置信度的标记为"确认"
- 上下文缓存:保留前几句的翻译上下文,提高翻译连贯性
2.4 支持的语言对
| 源语言 | 目标语言 | 离线支持 | 在线支持 |
|---|---|---|---|
| 中文 | 英语 | ✅ | ✅ |
| 英语 | 中文 | ✅ | ✅ |
| 中文 | 日语 | ❌ | ✅ |
| 中文 | 韩语 | ❌ | ✅ |
| 中文 | 法语 | ❌ | ✅ |
| 中文 | 德语 | ❌ | ✅ |
| 英语 | 日语 | ❌ | ✅ |
| 英语 | 韩语 | ❌ | ✅ |
三、代码实战
3.1 基础语音翻译——中英互译
最基础的用法:说中文翻译成英文,或说英文翻译成中文。
// 基础语音翻译示例
import { speechTranslation } from '@kit.AISpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct BasicTranslationPage {
@State sourceText: string = ''; // 源语言文本
@State targetText: string = ''; // 目标语言文本
@State isTranslating: boolean = false;
@State sourceLang: string = 'zh-CN'; // 源语言
@State targetLang: string = 'en-US'; // 目标语言
@State translateMode: string = 'text'; // text: 仅文本 | voice: 语音同传
private translationEngine: speechTranslation.SpeechTranslation | null = null;
aboutToAppear(): void {
this.initTranslationEngine();
}
// 初始化翻译引擎
private initTranslationEngine(): void {
try {
const extraParams: Record<string, Object> = {
'locate': 'CN',
'sourceLanguage': this.sourceLang,
'targetLanguage': this.targetLang,
// 翻译模式:流式
'translateMode': 'streaming',
};
const initParams: speechTranslation.CreateEngineParams = {
sourceLanguage: this.sourceLang,
targetLanguage: this.targetLang,
extraParams: extraParams
};
this.translationEngine = speechTranslation.createEngine(initParams);
this.setupTranslationCallbacks();
console.info('[Translation] 引擎初始化成功');
} catch (error) {
const err = error as BusinessError;
console.error(`[Translation] 初始化失败: ${err.code} - ${err.message}`);
}
}
// 设置翻译回调
private setupTranslationCallbacks(): void {
if (!this.translationEngine) return;
// 源语言识别结果回调
this.translationEngine.on('sourceResult', (callback: speechTranslation.SourceResult) => {
if (callback.isFinal) {
this.sourceText = callback.result;
} else {
this.sourceText = callback.result + '...';
}
});
// 目标语言翻译结果回调——核心!
this.translationEngine.on('targetResult', (callback: speechTranslation.TargetResult) => {
if (callback.isFinal) {
this.targetText = callback.result;
this.isTranslating = false;
} else {
this.targetText = callback.result + '...';
}
});
// 错误回调
this.translationEngine.on('error', (callback: speechTranslation.Error) => {
this.isTranslating = false;
console.error(`[Translation] 错误: ${callback.code} - ${callback.message}`);
});
}
// 开始语音翻译
private startVoiceTranslation(): void {
if (!this.translationEngine) return;
this.isTranslating = true;
this.sourceText = '';
this.targetText = '';
try {
const translateParams: speechTranslation.TranslateParams = {
sessionId: `translate_${Date.now()}`,
extraParams: {
'mode': 'voice', // 语音翻译模式
'streaming': true, // 流式输出
}
};
this.translationEngine.startTranslating(translateParams);
console.info('[Translation] 开始语音翻译');
} catch (error) {
this.isTranslating = false;
console.error('[Translation] 启动失败');
}
}
// 停止翻译
private stopTranslation(): void {
if (!this.translationEngine) return;
try {
this.translationEngine.finish(`translate_${Date.now()}`);
this.isTranslating = false;
} catch (error) {
console.error('[Translation] 停止失败');
}
}
// 切换语言方向
private switchLanguageDirection(): void {
// 交换源语言和目标语言
const temp = this.sourceLang;
this.sourceLang = this.targetLang;
this.targetLang = temp;
// 需要重建引擎
if (this.translationEngine) {
this.translationEngine.off('sourceResult');
this.translationEngine.off('targetResult');
this.translationEngine.off('error');
this.translationEngine = null;
}
this.initTranslationEngine();
console.info(`[Translation] 切换方向: ${this.sourceLang} → ${this.targetLang}`);
}
build() {
Column({ space: 20 }) {
Text('语音翻译')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#E0E0E0')
// 语言方向指示
Row({ space: 12 }) {
Text(this.getLangName(this.sourceLang))
.fontSize(16)
.fontColor('#4FC3F7')
.fontWeight(FontWeight.Bold)
Text('→')
.fontSize(20)
.fontColor('#9E9E9E')
Text(this.getLangName(this.targetLang))
.fontSize(16)
.fontColor('#CE93D8')
.fontWeight(FontWeight.Bold)
Button('切换')
.height(32)
.fontSize(12)
.fontColor('#9E9E9E')
.backgroundColor('rgba(255,255,255,0.1)')
.borderRadius(16)
.onClick(() => { this.switchLanguageDirection(); })
}
// 源语言文本
Column() {
Text('原文')
.fontSize(12)
.fontColor('#9E9E9E')
.width('100%')
Text(this.sourceText || '等待语音输入...')
.fontSize(18)
.fontColor('#E0E0E0')
.width('100%')
.margin({ top: 8 })
}
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('rgba(79,195,247,0.08)')
.border({ width: 1, color: 'rgba(79,195,247,0.2)' })
.alignItems(HorizontalAlign.Start)
// 译文
Column() {
Text('译文')
.fontSize(12)
.fontColor('#9E9E9E')
.width('100%')
Text(this.targetText || '翻译结果将显示在这里...')
.fontSize(18)
.fontColor('#CE93D8')
.width('100%')
.margin({ top: 8 })
}
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('rgba(206,147,216,0.08)')
.border({ width: 1, color: 'rgba(206,147,216,0.2)' })
.alignItems(HorizontalAlign.Start)
// 控制按钮
Button(this.isTranslating ? '停止翻译' : '开始语音翻译')
.width('80%')
.height(56)
.fontSize(18)
.fontColor('#FFFFFF')
.backgroundColor(this.isTranslating ? '#EF5350' : '#4FC3F7')
.borderRadius(28)
.onClick(() => {
if (this.isTranslating) {
this.stopTranslation();
} else {
this.startVoiceTranslation();
}
})
}
.width('100%')
.height('100%')
.backgroundColor('#1A1A2E')
.justifyContent(FlexAlign.Center)
.padding({ left: 20, right: 20 })
}
// 获取语言名称
private getLangName(code: string): string {
const names: Record<string, string> = {
'zh-CN': '中文',
'en-US': 'English',
'ja-JP': '日本語',
'ko-KR': '한국어',
'fr-FR': 'Français',
'de-DE': 'Deutsch',
};
return names[code] || code;
}
aboutToDisappear(): void {
if (this.translationEngine) {
this.translationEngine.off('sourceResult');
this.translationEngine.off('targetResult');
this.translationEngine.off('error');
this.translationEngine = null;
}
}
}
3.2 实时同传模式——ASR+MT+TTS一体化
同传模式是语音翻译的终极形态:用户说话,系统实时翻译并用目标语言朗读出来。这需要ASR、MT、TTS三个模块紧密协同。
// 实时同传模式示例
import { speechTranslation } from '@kit.AISpeechKit';
import { textToSpeech } from '@kit.AISpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
interface TranslationSegment {
sourceText: string; // 原文
targetText: string; // 译文
isFinal: boolean; // 是否为最终结果
timestamp: number; // 时间戳
}
@Entry
@Component
struct SimultaneousInterpretPage {
@State isInterpreting: boolean = false;
@State currentSource: string = '';
@State currentTarget: string = '';
@State segments: TranslationSegment[] = [];
@State sourceLang: string = 'zh-CN';
@State targetLang: string = 'en-US';
@State autoSpeak: boolean = true; // 自动朗读译文
@State elapsedTime: string = '00:00';
private translationEngine: speechTranslation.SpeechTranslation | null = null;
private ttsEngine: textToSpeech.TextToSpeechEngine | null = null;
private startTime: number = 0;
private timerId: number = -1;
aboutToAppear(): void {
this.initEngines();
}
// 同时初始化翻译引擎和TTS引擎
private initEngines(): void {
// 初始化翻译引擎
try {
const extraParams: Record<string, Object> = {
'locate': 'CN',
'sourceLanguage': this.sourceLang,
'targetLanguage': this.targetLang,
'translateMode': 'streaming',
'simultaneousMode': true, // 同传模式
};
const initParams: speechTranslation.CreateEngineParams = {
sourceLanguage: this.sourceLang,
targetLanguage: this.targetLang,
extraParams: extraParams
};
this.translationEngine = speechTranslation.createEngine(initParams);
this.setupSimultaneousCallbacks();
} catch (error) {
console.error('[Simultaneous] 翻译引擎初始化失败');
}
// 初始化TTS引擎(用于朗读译文)
try {
const ttsExtraParams: Record<string, Object> = {
'locate': this.targetLang === 'en-US' ? 'US' : 'CN',
'language': this.targetLang,
};
const ttsInitParams: textToSpeech.CreateEngineParams = {
language: this.targetLang,
person: 0,
extraParams: ttsExtraParams
};
this.ttsEngine = textToSpeech.createEngine(ttsInitParams);
this.ttsEngine.on('complete', () => {});
this.ttsEngine.on('error', () => {});
} catch (error) {
console.error('[Simultaneous] TTS引擎初始化失败');
}
}
// 设置同传回调
private setupSimultaneousCallbacks(): void {
if (!this.translationEngine) return;
// 源语言识别结果
this.translationEngine.on('sourceResult', (callback: speechTranslation.SourceResult) => {
if (callback.isFinal) {
this.currentSource = callback.result;
} else {
this.currentSource = callback.result + '...';
}
});
// 目标语言翻译结果——同传核心!
this.translationEngine.on('targetResult', (callback: speechTranslation.TargetResult) => {
if (callback.isFinal) {
this.currentTarget = callback.result;
// 保存翻译段落
this.segments.push({
sourceText: this.currentSource,
targetText: callback.result,
isFinal: true,
timestamp: Date.now()
});
// 自动朗读译文
if (this.autoSpeak && this.ttsEngine) {
this.speakTranslation(callback.result);
}
} else {
this.currentTarget = callback.result + '...';
}
});
this.translationEngine.on('error', (callback: speechTranslation.Error) => {
this.isInterpreting = false;
clearInterval(this.timerId);
console.error(`[Simultaneous] 错误: ${callback.code}`);
});
}
// 朗读译文
private speakTranslation(text: string): void {
if (!this.ttsEngine) return;
try {
const speakParams: textToSpeech.SpeakParams = {
requestId: `tts_${Date.now()}`,
extraParams: {
'volume': 1,
'speed': 1.1, // 译文稍快朗读,跟上说话节奏
'pitch': 1,
}
};
this.ttsEngine.speak(text, speakParams);
} catch (error) {
console.error('[Simultaneous] TTS朗读失败');
}
}
// 开始同传
private startSimultaneous(): void {
if (!this.translationEngine) return;
this.isInterpreting = true;
this.segments = [];
this.currentSource = '';
this.currentTarget = '';
this.startTime = Date.now();
// 计时器
this.timerId = setInterval(() => {
const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
const min = String(Math.floor(elapsed / 60)).padStart(2, '0');
const sec = String(elapsed % 60).padStart(2, '0');
this.elapsedTime = `${min}:${sec}`;
}, 1000);
try {
const translateParams: speechTranslation.TranslateParams = {
sessionId: `simultaneous_${Date.now()}`,
extraParams: {
'mode': 'voice',
'streaming': true,
'simultaneousMode': true,
'vadEnd': 2000,
'autoRestart': true, // 自动重启,实现连续同传
}
};
this.translationEngine.startTranslating(translateParams);
console.info('[Simultaneous] 开始同传');
} catch (error) {
this.isInterpreting = false;
}
}
// 停止同传
private stopSimultaneous(): void {
if (this.translationEngine) {
this.translationEngine.finish(`simultaneous_${Date.now()}`);
}
if (this.ttsEngine) {
this.ttsEngine.stop();
}
this.isInterpreting = false;
clearInterval(this.timerId);
}
build() {
Column({ space: 16 }) {
// 顶部状态栏
Row({ space: 12 }) {
Text('实时同传')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#E0E0E0')
.layoutWeight(1)
if (this.isInterpreting) {
Row({ space: 6 }) {
Circle({ width: 8, height: 8 }).fill('#EF5350')
Text(this.elapsedTime)
.fontSize(14)
.fontColor('#EF5350')
.fontFamily('monospace')
}
}
}
.width('90%')
// 语言方向
Row({ space: 8 }) {
Text(this.getLangName(this.sourceLang))
.fontSize(14)
.fontColor('#4FC3F7')
Text('⟷')
.fontSize(18)
.fontColor('#9E9E9E')
Text(this.getLangName(this.targetLang))
.fontSize(14)
.fontColor('#CE93D8')
}
// 实时翻译区域
Column({ space: 8 }) {
// 当前原文
Column() {
Text('原文(实时)')
.fontSize(12)
.fontColor('#9E9E9E')
.width('100%')
Text(this.currentSource || '等待语音输入...')
.fontSize(18)
.fontColor('#E0E0E0')
.width('100%')
.margin({ top: 4 })
}
.width('100%')
.padding(12)
.borderRadius(8)
.backgroundColor('rgba(79,195,247,0.08)')
// 当前译文
Column() {
Text('译文(实时)')
.fontSize(12)
.fontColor('#9E9E9E')
.width('100%')
Text(this.currentTarget || '翻译中...')
.fontSize(18)
.fontColor('#CE93D8')
.width('100%')
.margin({ top: 4 })
}
.width('100%')
.padding(12)
.borderRadius(8)
.backgroundColor('rgba(206,147,216,0.08)')
}
.width('90%')
// 翻译历史
Scroll() {
Column({ space: 8 }) {
ForEach(this.segments, (seg: TranslationSegment, index: number) => {
Row({ space: 12 }) {
// 原文
Text(seg.sourceText)
.fontSize(14)
.fontColor('#B0BEC5')
.layoutWeight(1)
// 箭头
Text('→')
.fontSize(14)
.fontColor('#616161')
// 译文
Text(seg.targetText)
.fontSize(14)
.fontColor('#CE93D8')
.layoutWeight(1)
}
.width('100%')
.padding(8)
.borderRadius(6)
.backgroundColor('rgba(255,255,255,0.03)')
}, (seg: TranslationSegment, index: number) => `${index}`)
}
}
.width('90%')
.layoutWeight(1)
// 控制区
Row({ space: 12 }) {
// 自动朗读开关
Row({ space: 6 }) {
Toggle({ type: ToggleType.Switch, isOn: this.autoSpeak })
.onChange((isOn: boolean) => { this.autoSpeak = isOn; })
.selectedColor('#CE93D8')
Text('朗读译文')
.fontSize(12)
.fontColor('#9E9E9E')
}
// 开始/停止按钮
Button(this.isInterpreting ? '停止同传' : '开始同传')
.height(48)
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor(this.isInterpreting ? '#EF5350' : '#4FC3F7')
.borderRadius(24)
.layoutWeight(1)
.onClick(() => {
if (this.isInterpreting) {
this.stopSimultaneous();
} else {
this.startSimultaneous();
}
})
}
.width('90%')
}
.width('100%')
.height('100%')
.backgroundColor('#1A1A2E')
.justifyContent(FlexAlign.Start)
.padding({ top: 40, left: 20, right: 20, bottom: 20 })
}
private getLangName(code: string): string {
const names: Record<string, string> = {
'zh-CN': '中文', 'en-US': 'English', 'ja-JP': '日本語', 'ko-KR': '한국어',
};
return names[code] || code;
}
aboutToDisappear(): void {
clearInterval(this.timerId);
if (this.translationEngine) {
this.translationEngine.off('sourceResult');
this.translationEngine.off('targetResult');
this.translationEngine.off('error');
this.translationEngine = null;
}
if (this.ttsEngine) {
this.ttsEngine.off('complete');
this.ttsEngine.off('error');
this.ttsEngine.shutdown();
this.ttsEngine = null;
}
}
}
3.3 离线翻译与语言包管理
在无网络环境下(如出国旅行),离线翻译至关重要。HarmonyOS支持下载离线语言包,实现端侧翻译。
// 离线翻译与语言包管理示例
import { speechTranslation } from '@kit.AISpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
interface LanguagePack {
id: string;
sourceLang: string;
targetLang: string;
size: string; // 语言包大小
downloaded: boolean; // 是否已下载
version: string; // 版本号
}
@Entry
@Component
struct OfflineTranslationPage {
@State isTranslating: boolean = false;
@State sourceText: string = '';
@State targetText: string = '';
@State downloadProgress: number = 0;
@State isDownloading: boolean = false;
// 可用语言包列表
@State languagePacks: LanguagePack[] = [
{ id: 'zh-en', sourceLang: 'zh-CN', targetLang: 'en-US', size: '85MB', downloaded: true, version: '2.1' },
{ id: 'en-zh', sourceLang: 'en-US', targetLang: 'zh-CN', size: '82MB', downloaded: true, version: '2.1' },
{ id: 'zh-ja', sourceLang: 'zh-CN', targetLang: 'ja-JP', size: '95MB', downloaded: false, version: '1.8' },
{ id: 'zh-ko', sourceLang: 'zh-CN', targetLang: 'ko-KR', size: '78MB', downloaded: false, version: '1.5' },
{ id: 'zh-fr', sourceLang: 'zh-CN', targetLang: 'fr-FR', size: '90MB', downloaded: false, version: '1.3' },
];
private translationEngine: speechTranslation.SpeechTranslation | null = null;
aboutToAppear(): void {
this.initOfflineEngine();
}
// 初始化离线翻译引擎
private initOfflineEngine(): void {
try {
const extraParams: Record<string, Object> = {
'locate': 'CN',
'sourceLanguage': 'zh-CN',
'targetLanguage': 'en-US',
// 关键:指定离线模式
'translateMode': 'offline',
// 离线语言包路径
'modelPath': '/data/translation/models/zh-en/',
};
const initParams: speechTranslation.CreateEngineParams = {
sourceLanguage: 'zh-CN',
targetLanguage: 'en-US',
extraParams: extraParams
};
this.translationEngine = speechTranslation.createEngine(initParams);
this.setupOfflineCallbacks();
console.info('[OfflineTranslation] 离线引擎初始化成功');
} catch (error) {
const err = error as BusinessError;
console.error(`[OfflineTranslation] 初始化失败: ${err.code}`);
}
}
// 设置离线翻译回调
private setupOfflineCallbacks(): void {
if (!this.translationEngine) return;
this.translationEngine.on('sourceResult', (callback: speechTranslation.SourceResult) => {
this.sourceText = callback.isFinal ? callback.result : callback.result + '...';
});
this.translationEngine.on('targetResult', (callback: speechTranslation.TargetResult) => {
if (callback.isFinal) {
this.targetText = callback.result;
this.isTranslating = false;
} else {
this.targetText = callback.result + '...';
}
});
this.translationEngine.on('error', (callback: speechTranslation.Error) => {
this.isTranslating = false;
console.error(`[OfflineTranslation] 错误: ${callback.code}`);
});
}
// 下载语言包
private downloadLanguagePack(pack: LanguagePack): void {
this.isDownloading = true;
this.downloadProgress = 0;
// 模拟下载过程
const downloadInterval = setInterval(() => {
this.downloadProgress += 10;
if (this.downloadProgress >= 100) {
clearInterval(downloadInterval);
this.isDownloading = false;
// 更新语言包状态
const index = this.languagePacks.findIndex(p => p.id === pack.id);
if (index >= 0) {
this.languagePacks[index].downloaded = true;
}
console.info(`[OfflineTranslation] 下载完成: ${pack.id}`);
}
}, 300);
}
// 切换到指定语言对的离线引擎
private switchOfflineEngine(sourceLang: string, targetLang: string): void {
// 释放旧引擎
if (this.translationEngine) {
this.translationEngine.off('sourceResult');
this.translationEngine.off('targetResult');
this.translationEngine.off('error');
this.translationEngine = null;
}
try {
const extraParams: Record<string, Object> = {
'locate': 'CN',
'sourceLanguage': sourceLang,
'targetLanguage': targetLang,
'translateMode': 'offline',
'modelPath': `/data/translation/models/${sourceLang.substring(0, 2)}-${targetLang.substring(0, 2)}/`,
};
const initParams: speechTranslation.CreateEngineParams = {
sourceLanguage: sourceLang,
targetLanguage: targetLang,
extraParams: extraParams
};
this.translationEngine = speechTranslation.createEngine(initParams);
this.setupOfflineCallbacks();
console.info(`[OfflineTranslation] 切换引擎: ${sourceLang} → ${targetLang}`);
} catch (error) {
console.error('[OfflineTranslation] 切换引擎失败');
}
}
// 开始离线翻译
private startOfflineTranslation(): void {
if (!this.translationEngine) return;
this.isTranslating = true;
this.sourceText = '';
this.targetText = '';
try {
const translateParams: speechTranslation.TranslateParams = {
sessionId: `offline_${Date.now()}`,
extraParams: {
'mode': 'voice',
'streaming': true,
}
};
this.translationEngine.startTranslating(translateParams);
} catch (error) {
this.isTranslating = false;
}
}
build() {
Scroll() {
Column({ space: 20 }) {
Text('离线语音翻译')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#E0E0E0')
Text('无需网络,数据不出设备,出国旅行必备')
.fontSize(14)
.fontColor('#9E9E9E')
.width('90%')
// 翻译结果
Row({ space: 12 }) {
Column() {
Text('原文')
.fontSize(12)
.fontColor('#9E9E9E')
Text(this.sourceText || '等待输入')
.fontSize(16)
.fontColor('#E0E0E0')
.margin({ top: 4 })
}
.layoutWeight(1)
.padding(12)
.borderRadius(8)
.backgroundColor('rgba(79,195,247,0.08)')
Column() {
Text('译文')
.fontSize(12)
.fontColor('#9E9E9E')
Text(this.targetText || '翻译结果')
.fontSize(16)
.fontColor('#CE93D8')
.margin({ top: 4 })
}
.layoutWeight(1)
.padding(12)
.borderRadius(8)
.backgroundColor('rgba(206,147,216,0.08)')
}
.width('90%')
// 开始翻译
Button(this.isTranslating ? '停止' : '开始离线翻译')
.width('80%')
.height(52)
.fontSize(18)
.fontColor('#FFFFFF')
.backgroundColor(this.isTranslating ? '#EF5350' : '#81C784')
.borderRadius(26)
.onClick(() => {
if (this.isTranslating) {
this.translationEngine?.finish(`offline_${Date.now()}`);
this.isTranslating = false;
} else {
this.startOfflineTranslation();
}
})
// 语言包管理
Text('离线语言包')
.fontSize(16)
.fontColor('#9E9E9E')
.width('90%')
.margin({ top: 8 })
// 下载进度
if (this.isDownloading) {
Column() {
Text('正在下载语言包...')
.fontSize(14)
.fontColor('#FFB74D')
Progress({ value: this.downloadProgress, total: 100, type: ProgressType.Linear })
.width('100%')
.color('#FFB74D')
.margin({ top: 8 })
}
.width('90%')
.padding(12)
.borderRadius(8)
.backgroundColor('rgba(255,183,77,0.1)')
}
// 语言包列表
ForEach(this.languagePacks, (pack: LanguagePack) => {
Row({ space: 12 }) {
Column({ space: 2 }) {
Text(`${this.getLangShort(pack.sourceLang)} → ${this.getLangShort(pack.targetLang)}`)
.fontSize(16)
.fontColor('#E0E0E0')
Text(`${pack.size} | v${pack.version}`)
.fontSize(12)
.fontColor('#9E9E9E')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
if (pack.downloaded) {
Button('使用')
.height(32)
.fontSize(12)
.fontColor('#81C784')
.backgroundColor('rgba(129,199,132,0.15)')
.borderRadius(16)
.onClick(() => {
this.switchOfflineEngine(pack.sourceLang, pack.targetLang);
})
} else {
Button('下载')
.height(32)
.fontSize(12)
.fontColor('#FFB74D')
.backgroundColor('rgba(255,183,77,0.15)')
.borderRadius(16)
.onClick(() => {
this.downloadLanguagePack(pack);
})
}
}
.width('90%')
.padding(12)
.borderRadius(8)
.backgroundColor(pack.downloaded ? 'rgba(129,199,132,0.05)' : 'rgba(255,255,255,0.05)')
}, (pack: LanguagePack) => pack.id)
}
.padding({ top: 40, bottom: 40 })
}
.width('100%')
.height('100%')
.backgroundColor('#1A1A2E')
}
private getLangShort(code: string): string {
const names: Record<string, string> = {
'zh-CN': '中文', 'en-US': '英语', 'ja-JP': '日语', 'ko-KR': '韩语', 'fr-FR': '法语',
};
return names[code] || code;
}
aboutToDisappear(): void {
if (this.translationEngine) {
this.translationEngine.off('sourceResult');
this.translationEngine.off('targetResult');
this.translationEngine.off('error');
this.translationEngine = null;
}
}
}
四、踩坑与注意事项
4.1 翻译延迟优化
语音翻译的延迟由三部分组成:ASR延迟 + MT延迟 + TTS延迟。流式级联虽然降低了总延迟,但仍需注意以下优化点:
| 优化手段 | 效果 | 实现方式 |
|---|---|---|
| 流式级联 | 降低50%延迟 | ASR出中间结果即触发MT |
| 增量翻译 | 避免重复翻译 | 只翻译新增文本片段 |
| 预测翻译 | 再降20%延迟 | 根据上下文预测未说完的内容 |
| TTS语速加快 | 跟上说话节奏 | 译文以1.1-1.3倍速朗读 |
4.2 翻译质量与领域适配
通用翻译模型在专业领域(医疗、法律、金融)表现不佳,需要领域适配:
// 领域适配翻译配置
const DOMAIN_CONFIGS = {
// 通用领域
general: {
domain: 'general',
description: '日常对话、新闻、社交媒体',
},
// 医疗领域
medical: {
domain: 'medical',
description: '医学术语、诊断报告',
extraParams: {
'domain': 'medical',
'terminology': '/data/translation/terminology/medical.json',
}
},
// 法律领域
legal: {
domain: 'legal',
description: '法律条文、合同条款',
extraParams: {
'domain': 'legal',
'terminology': '/data/translation/terminology/legal.json',
}
},
// IT领域
it: {
domain: 'it',
description: '技术文档、API说明',
extraParams: {
'domain': 'it',
'terminology': '/data/translation/terminology/it.json',
}
},
};
4.3 离线语言包管理
离线翻译需要下载语言包,注意以下事项:
- 存储空间:每个语言包50-100MB,下载前检查可用空间
- WiFi下载:语言包较大,建议WiFi环境下下载
- 版本更新:语言包定期更新,需要检查并更新到最新版本
- 多语言对:中→英和英→中是两个不同的语言包,需要分别下载
// 语言包管理工具
class LanguagePackManager {
// 检查存储空间
async checkStorage(requiredMB: number): Promise<boolean> {
// 检查设备可用存储空间
const availableMB = 500; // 简化示例
return availableMB >= requiredMB;
}
// 检查语言包版本
async checkUpdate(packId: string): Promise<boolean> {
// 对比本地版本和服务器版本
return false; // 简化示例
}
// 下载语言包
async downloadPack(packId: string, onProgress: (progress: number) => void): Promise<boolean> {
// 下载并安装语言包
return true;
}
}
4.4 同传模式的连续性
同传模式需要实现"说完一句自动开始下一句"的连续翻译。关键在于:
// 连续同传的关键:自动重启
this.translationEngine.on('complete', () => {
if (this.isInterpreting) {
// 自动重启下一轮翻译
setTimeout(() => {
this.translationEngine?.startTranslating({
sessionId: `simultaneous_${Date.now()}`,
extraParams: { 'mode': 'voice', 'streaming': true }
});
}, 300); // 300ms间隔,避免重叠
}
});
4.5 网络状态与降级策略
在线翻译依赖网络,弱网环境下需要降级到离线模式:
// 网络状态监听与降级
import { connection } from '@kit.NetworkKit';
class TranslationFallback {
private isOnline: boolean = true;
// 监听网络状态
watchNetworkStatus(): void {
connection.createNetConnection().on('netLost', () => {
this.isOnline = false;
console.info('[Fallback] 网络断开,切换到离线模式');
this.switchToOffline();
});
connection.createNetConnection().on('netAvailable', () => {
this.isOnline = true;
console.info('[Fallback] 网络恢复,切换到在线模式');
this.switchToOnline();
});
}
private switchToOffline(): void {
// 重建引擎为离线模式
}
private switchToOnline(): void {
// 重建引擎为在线模式
}
}
五、HarmonyOS 6适配
5.1 API变更
| 变更项 | HarmonyOS 5 | HarmonyOS 6 |
|---|---|---|
| 语言对数量 | 8对 | 20+对,新增东南亚语言 |
| 离线翻译 | 中英互译 | 扩展到6对离线语言对 |
| 流式翻译 | 基础流式 | 新增预测翻译(提前翻译未说完的内容) |
| 同传模式 | ASR+MT+TTS手动拼接 | 新增一体化同传API |
| 翻译质量 | 通用模型 | 新增领域适配(医疗/法律/IT) |
5.2 一体化同传API(HarmonyOS 6新增)
HarmonyOS 6将ASR+MT+TTS封装为一体化同传API,开发者无需分别管理三个引擎:
// HarmonyOS 6一体化同传API
const simultaneousParams = {
sourceLanguage: 'zh-CN',
targetLanguage: 'en-US',
mode: 'simultaneous', // 一体化同传模式
autoSpeak: true, // 自动朗读译文
speakSpeed: 1.2, // 译文朗读速度
// 内部自动管理ASR→MT→TTS的级联
};
this.translationEngine.startTranslating({
sessionId: `simultaneous_${Date.now()}`,
extraParams: simultaneousParams
});
5.3 预测翻译(HarmonyOS 6新增)
预测翻译根据上下文预测用户未说完的内容,提前开始翻译:
// HarmonyOS 6预测翻译
const extraParams: Record<string, Object> = {
'predictiveTranslation': true,
// 预测窗口:预测未来2-3个词
'predictionWindow': 3,
// 预测置信度阈值:低于此值不输出预测结果
'predictionConfidence': 0.7,
};
六、总结
mindmap
root((语音翻译))
核心架构
ASR语音识别
MT机器翻译
TTS语音合成
流式级联降低延迟
基础翻译
中英互译
流式输出中间结果
语言方向切换
串行 vs 流式级联
实时同传
ASR+MT+TTS一体化
自动朗读译文
连续翻译自动重启
译文语速加快跟上节奏
离线翻译
语言包下载管理
存储空间检查
版本更新
端侧模型推理
质量优化
增量翻译
领域适配
术语表
上下文缓存
踩坑要点
翻译延迟优化
网络降级策略
语言包管理
同传连续性
HarmonyOS 6
20+语言对
一体化同传API
预测翻译
领域适配
6对离线语言包
classDef primary fill:#4FC3F7,stroke:#0288D1,color:#000
classDef warning fill:#FFB74D,stroke:#F57C00,color:#000
classDef error fill:#EF5350,stroke:#C62828,color:#FFF
classDef info fill:#81C784,stroke:#388E3C,color:#000
classDef purple fill:#CE93D8,stroke:#7B1FA2,color:#000
| 知识点 | 关键内容 |
|---|---|
| 引擎创建 | speechTranslation.createEngine(),指定sourceLanguage和targetLanguage |
| 流式翻译 | 通过on('targetResult')获取中间和最终翻译结果 |
| 同传模式 | ASR+MT+TTS级联,自动朗读译文,连续翻译自动重启 |
| 离线翻译 | 下载语言包,指定translateMode: 'offline'和modelPath |
| 延迟优化 | 流式级联、增量翻译、预测翻译、TTS加速 |
| 质量优化 | 领域适配、术语表、上下文缓存 |
| 网络降级 | 监听网络状态,自动切换在线/离线模式 |
| HarmonyOS 6 | 一体化同传API、预测翻译、20+语言对、领域适配 |
语音翻译是语音智能的"巴别塔"——让不同语言的人能够实时沟通。从ASR的"听",到TTS的"说",到语音唤醒的"守",到声纹识别的"认",再到语音翻译的"通",这五篇文章构成了HarmonyOS语音智能的完整技术图谱。掌握这些能力,你就能构建出真正智能的语音交互应用。
- 点赞
- 收藏
- 关注作者
评论(0)