HarmonyOS APP开发:语音翻译与实时同传

举报
Jack20 发表于 2026/06/21 13:53:39 2026/06/21
【摘要】 HarmonyOS APP开发:语音翻译与实时同传核心要点:掌握HarmonyOS语音翻译(Speech Translation)引擎配置、ASR+MT+TTS级联架构、实时流式翻译、同传模式实现,以及多语言对与翻译质量优化。 一、背景与动机你有没有看过那种国际会议的直播?台上嘉宾说着英语,屏幕下方实时出现中文字幕,几乎和说话同步——这就是同声传译(Simultaneous Interpr...

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通过以下策略优化:

  1. 增量翻译:只翻译新增的文本,不重复翻译已确认的部分
  2. 重排机制:当ASR修正时,MT只重排受影响的部分
  3. 置信度加权:低置信度的ASR结果翻译后标记为"暂定",高置信度的标记为"确认"
  4. 上下文缓存:保留前几句的翻译上下文,提高翻译连贯性

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 离线语言包管理

离线翻译需要下载语言包,注意以下事项:

  1. 存储空间:每个语言包50-100MB,下载前检查可用空间
  2. WiFi下载:语言包较大,建议WiFi环境下下载
  3. 版本更新:语言包定期更新,需要检查并更新到最新版本
  4. 多语言对:中→英和英→中是两个不同的语言包,需要分别下载
// 语言包管理工具
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语音智能的完整技术图谱。掌握这些能力,你就能构建出真正智能的语音交互应用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。