HarmonyOS NEXT AI 智能生活助手:AI 翻译助手

HarmonyOS NEXT AI 智能生活助手:AI 翻译助手

前言

第 10 篇中,我们实现了 OCR 文字识别。OCR 识别出文字后,最常见的后续操作就是翻译。本文将实现 HarmonyAI 的 AI 翻译助手。

AI 翻译 是 AI 智能助手的核心功能之一。不同于传统翻译软件的规则翻译,AI 翻译能理解上下文、识别语气、保持风格一致性,翻译质量远超传统方案。

本文将实现:

  1. 翻译页面:输入原文 → 选择语言 → 展示译文
  2. 多语言支持:中、英、日、韩、法、德等
  3. 自动识别:自动检测源语言
  4. TranslateManager:统一翻译管理
  5. 历史记录:保存翻译历史

图1:AI 翻译助手页面布局

一、翻译功能设计

1.1 页面布局

复制代码
┌─────────────────────────┐
│ ← 返回    AI 翻译    ⋮   │
├─────────────────────────┤
│                         │
│   ┌─────────────────┐   │
│   │ 中文 → 英文      │   │  ← 语言选择
│   └─────────────────┘   │
│                         │
│   ┌─────────────────┐   │
│   │ 输入文本...       │   │  ← 原文输入
│   │                  │   │
│   │                  │   │
│   └─────────────────┘   │
│                         │
│   ⟳ 翻译按钮            │
│                         │
│   ┌─────────────────┐   │
│   │ Translated       │   │  ← 译文输出
│   │ text here...     │   │
│   └─────────────────┘   │
│                         │
│   [复制] [朗读] [润色]   │
├─────────────────────────┤
│ 翻译历史                 │
│ ─────────────────────── │
│ Hello → 你好            │
│ Goodbye → 再见          │
└─────────────────────────┘

1.2 语言支持

语言 代码 支持方向 图标资源
中文(简体) zh-CN 源/目标 ic_flag_cn
英语 en 源/目标 ic_flag_en
日语 ja 源/目标 ic_flag_ja
韩语 ko 源/目标 ic_flag_ko
法语 fr 源/目标 ic_flag_fr
德语 de 源/目标 ic_flag_de
西班牙语 es 源/目标 ic_flag_es
俄语 ru 源/目标 ic_flag_ru
阿拉伯语 ar 源/目标 ic_flag_ar
葡萄牙语 pt 源/目标 ic_flag_pt

图标规范:本项目所有图标均使用 SVG 矢量图,不使用 emoji,确保在各设备上显示一致。


二、数据模型

2.1 翻译数据定义

typescript 复制代码
// model/Translation.ts
export interface TranslationRequest {
  text: string;
  sourceLang: string;    // 源语言,'auto' 表示自动检测
  targetLang: string;     // 目标语言
}

export interface TranslationResult {
  originalText: string;
  translatedText: string;
  sourceLang: string;
  targetLang: string;
  pronunciation?: string;  // 发音(可选)
  alternatives?: string[]; // 其他译法(可选)
  createTime: number;
}

export interface Language {
  code: string;
  name: string;
  nativeName: string;     // 母语名称
  icon: string;            // SVG 图标资源名
}

export const SUPPORTED_LANGUAGES: Language[] = [
  { code: 'zh-CN', name: '中文(简体)', nativeName: '简体中文', icon: 'ic_flag_cn' },
  { code: 'en', name: '英语', nativeName: 'English', icon: 'ic_flag_en' },
  { code: 'ja', name: '日语', nativeName: '日本語', icon: 'ic_flag_ja' },
  { code: 'ko', name: '韩语', nativeName: '한국어', icon: 'ic_flag_ko' },
  { code: 'fr', name: '法语', nativeName: 'Français', icon: 'ic_flag_fr' },
  { code: 'de', name: '德语', nativeName: 'Deutsch', icon: 'ic_flag_de' },
  { code: 'es', name: '西班牙语', nativeName: 'Español', icon: 'ic_flag_es' },
  { code: 'ru', name: '俄语', nativeName: 'Русский', icon: 'ic_flag_ru' },
  { code: 'ar', name: '阿拉伯语', nativeName: 'العربية', icon: 'ic_flag_ar' },
  { code: 'pt', name: '葡萄牙语', nativeName: 'Português', icon: 'ic_flag_pt' }
];

2.2 翻译记录持久化

typescript 复制代码
// model/TranslationRecord.ts
export interface TranslationRecord {
  id: number;
  originalText: string;
  translatedText: string;
  sourceLang: string;
  targetLang: string;
  createTime: number;
}

// 数据库表结构
export const TRANSLATION_TABLE = {
  tableName: 'translations',
  columns: [
    { name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
    { name: 'original_text', type: 'TEXT NOT NULL' },
    { name: 'translated_text', type: 'TEXT NOT NULL' },
    { name: 'source_lang', type: 'TEXT' },
    { name: 'target_lang', type: 'TEXT' },
    { name: 'create_time', type: 'INTEGER' }
  ]
};

三、核心架构设计

3.1 AIService 统一封装

本项目所有 AI 能力通过 AIService 统一封装,支持多 Provider 切换:

typescript 复制代码
// ai/AIService.ts
export class AIService {
  private static instance: AIService;
  private provider: BaseProvider;

  static getInstance(): AIService {
    if (!AIService.instance) {
      AIService.instance = new AIService();
    }
    return AIService.instance;
  }

  // 统一聊天接口
  async chat(messages: ChatMessage[]): Promise<ChatResponse> {
    return this.provider.chat(messages);
  }

  // 流式输出
  async *chatStream(messages: ChatMessage[]): AsyncGenerator<StreamChunk> {
    yield* this.provider.chatStream(messages);
  }

  // 获取当前 Provider
  getProvider(): BaseProvider {
    return this.provider;
  }

  // 切换 Provider
  setProvider(type: ProviderType): void {
    switch (type) {
      case 'openai': this.provider = new OpenAIProvider(); break;
      case 'deepseek': this.provider = new DeepSeekProvider(); break;
      case 'qwen': this.provider = new QwenProvider(); break;
      case 'zhipu': this.provider = new ZhipuProvider(); break;
      case 'doubao': this.provider = new DoubaoProvider(); break;
    }
  }
}

type ProviderType = 'openai' | 'deepseek' | 'qwen' | 'zhipu' | 'doubao';

Provider 支持:当前已集成 OpenAI、DeepSeek、Qwen、智谱、豆包五大主流 AI 服务,用户可在设置中自由切换。

3.2 PromptManager 版本控制

typescript 复制代码
// ai/PromptManager.ts
export class PromptManager {
  private static instance: PromptManager;
  private prompts: Map<string, PromptTemplate> = new Map();

  static getInstance(): PromptManager {
    if (!PromptManager.instance) {
      PromptManager.instance = new PromptManager();
    }
    return PromptManager.instance;
  }

  // 加载 prompt 模板
  loadPrompt(name: string, template: PromptTemplate): void {
    this.prompts.set(name, template);
  }

  // 构建 Prompt(支持变量替换和版本控制)
  buildPrompt(name: string, variables: Record<string, string>): string {
    const template = this.prompts.get(name);
    if (!template) return '';
    let prompt = template.content;
    for (const [key, value] of Object.entries(variables)) {
      prompt = prompt.replace(new RegExp(`{{${key}}}`, 'g'), value);
    }
    return prompt;
  }

  // 获取版本信息
  getVersion(name: string): string {
    return this.prompts.get(name)?.version || '1.0.0';
  }
}

interface PromptTemplate {
  name: string;
  version: string;
  content: string;
  description: string;
}

四、TranslateManager

4.1 核心翻译管理器

typescript 复制代码
// ai/TranslateManager.ts
import { display } from '@kit.ArkUI';

export class TranslateManager {
  private static instance: TranslateManager;
  private aiService = AIService.getInstance();
  private promptManager = PromptManager.getInstance();
  private history: TranslationResult[] = [];
  private readonly MAX_HISTORY = 50;

  static getInstance(): TranslateManager {
    if (!TranslateManager.instance) {
      TranslateManager.instance = new TranslateManager();
    }
    return TranslateManager.instance;
  }

  // 翻译(核心方法)
  async translate(request: TranslationRequest): Promise<TranslationResult> {
    // 1. 检查缓存
    const cached = CacheManager.getInstance().get<TranslationResult>(
      `translate_${request.text}_${request.sourceLang}_${request.targetLang}`
    );
    if (cached) return cached;

    // 2. 构建 Prompt
    const prompt = this.promptManager.buildPrompt('translate', {
      sourceLang: request.sourceLang === 'auto' ? '自动检测' : this.getLanguageName(request.sourceLang),
      targetLang: this.getLanguageName(request.targetLang)
    });

    // 3. 调用 AI 翻译
    const response = await this.aiService.chat([
      { role: 'system', content: prompt },
      { role: 'user', content: `请将以下文本翻译为${this.getLanguageName(request.targetLang)}:\n\n${request.text}` }
    ]);

    // 4. 解析结果
    const result: TranslationResult = {
      originalText: request.text,
      translatedText: response.content,
      sourceLang: request.sourceLang,
      targetLang: request.targetLang,
      createTime: Date.now()
    };

    // 5. 保存缓存和历史
    this.addToHistory(result);
    CacheManager.getInstance().set(
      `translate_${request.text}_${request.sourceLang}_${request.targetLang}`,
      result,
      30 * 60 * 1000 // 30分钟缓存
    );

    return result;
  }

  // 自动检测语言并翻译
  async autoTranslate(text: string, targetLang: string): Promise<TranslationResult> {
    const detectResult = await this.detectLanguage(text);
    return this.translate({
      text,
      sourceLang: detectResult,
      targetLang
    });
  }

  // 语言检测
  async detectLanguage(text: string): Promise<string> {
    const prompt = this.promptManager.buildPrompt('translate', {
      sourceLang: '自动检测',
      targetLang: '语言代码'
    });
    const response = await this.aiService.chat([
      { role: 'system', content: prompt + '\n仅返回语言代码,不要其他文字。' },
      { role: 'user', content: text }
    ]);
    const code = response.content.trim().toLowerCase();
    return SUPPORTED_LANGUAGES.some(l => l.code === code) ? code : 'en';
  }

  // 文本润色
  async polishText(text: string, style: 'formal' | 'casual' | 'professional' = 'formal'): Promise<string> {
    const prompt = this.promptManager.buildPrompt('translate', {
      sourceLang: '文本润色',
      targetLang: style === 'formal' ? '正式' : style === 'casual' ? '口语化' : '专业'
    });
    const response = await this.aiService.chat([
      { role: 'system', content: prompt },
      { role: 'user', content: text }
    ]);
    return response.content;
  }

  // 获取翻译历史
  getHistory(): TranslationResult[] {
    return [...this.history];
  }

  // 清除历史
  clearHistory(): void {
    this.history = [];
  }

  private addToHistory(result: TranslationResult): void {
    this.history.unshift(result);
    if (this.history.length > this.MAX_HISTORY) {
      this.history = this.history.slice(0, this.MAX_HISTORY);
    }
  }

  private getLanguageName(code: string): string {
    const lang = SUPPORTED_LANGUAGES.find(l => l.code === code);
    return lang ? lang.nativeName : code;
  }
}

4.2 安全区适配

typescript 复制代码
// utils/SafeAreaUtil.ts
import { display } from '@kit.ArkUI';

export class SafeAreaUtil {
  private static statusBarHeight: number = 0;
  private static navBarHeight: number = 0;

  static init(): void {
    // 使用 display 获取屏幕密度并转换 px 到 vp
    const screenDensity = display.getDefaultDisplaySync().densityPixels;
    this.statusBarHeight = AppStorage.get<number>('statusBarHeight') || 0;
    this.navBarHeight = AppStorage.get<number>('navBarHeight') || 0;
  }

  static getStatusBarHeight(): number {
    return this.statusBarHeight;
  }

  static getNavBarHeight(): number {
    return this.navBarHeight;
  }

  static getSafeAreaPadding(): Padding {
    return {
      top: this.statusBarHeight,
      bottom: this.navBarHeight
    };
  }
}

五、翻译页面

5.1 TranslatePage 实现

typescript 复制代码
// pages/TranslatePage.ets
import { display } from '@kit.ArkUI';

@Entry
@Component
struct TranslatePage {
  @State sourceText: string = '';
  @State translatedText: string = '';
  @State sourceLang: string = 'zh-CN';
  @State targetLang: string = 'en';
  @State isAutoDetect: boolean = true;
  @State isTranslating: boolean = false;
  @State history: TranslationResult[] = [];
  @State showHistory: boolean = false;
  @State statusBarHeight: number = 0;
  @State navBarHeight: number = 0;
  private translateManager = TranslateManager.getInstance();

  aboutToAppear() {
    // 从 AppStorage 获取安全区高度
    this.statusBarHeight = AppStorage.get<number>('statusBarHeight') || 0;
    this.navBarHeight = AppStorage.get<number>('navBarHeight') || 0;

    this.history = this.translateManager.getHistory();
    // 接收来自其他页面的文本
    const params = RouterUtil.getParams();
    if (params?.text) {
      this.sourceText = params.text as string;
      this.performTranslation();
    }
  }

  build() {
    Column() {
      // 状态栏占位
      Row().width('100%').height(this.statusBarHeight);

      // 导航栏
      Row() {
        Image($r('app.media.ic_back'))
          .width(24).height(24)
          .onClick(() => RouterUtil.back());
        Text('AI 翻译').fontSize(18).fontWeight(FontWeight.Bold).margin({ left: 12 });
        Blank();
        Image($r('app.media.ic_history'))
          .width(22).height(22)
          .onClick(() => { this.showHistory = !this.showHistory; });
      }
      .width('100%').height(56).padding({ left: 16, right: 16 });

      // 语言选择
      Row() {
        this.languageSelector(this.sourceLang, true);
        Image($r('app.media.ic_swap')).width(20).height(20).margin({ left: 8, right: 8 })
          .onClick(() => { this.swapLanguages(); });
        this.languageSelector(this.targetLang, false);
      }
      .width('100%').padding(16);

      // 自动检测开关
      Row() {
        Toggle({ type: ToggleType.Switch, isOn: this.isAutoDetect })
          .width(40).height(20);
        Text('自动检测语言').fontSize(14).fontColor(Color.Gray).margin({ left: 8 });
      }
      .width('100%').padding({ left: 16, right: 16 });

      // 原文输入
      TextArea({
        text: this.sourceText,
        placeholder: '输入文本或粘贴内容...'
      })
      .height(150)
      .backgroundColor(Color.White)
      .borderRadius(12)
      .padding(12)
      .fontSize(16)
      .margin(16)
      .onChange((value: string) => {
        this.sourceText = value;
      });

      // 翻译按钮
      Button() {
        if (this.isTranslating) {
          LoadingView({ text: '翻译中...' });
        } else {
          Row() {
            Image($r('app.media.ic_translate')).width(18).height(18);
            Text('翻译').fontSize(16).fontColor(Color.White).margin({ left: 6 });
          }
        }
      }
      .width('90%').height(48)
      .backgroundColor('#6C5CE7')
      .borderRadius(24)
      .disabled(!this.sourceText.trim() || this.isTranslating)
      .opacity(this.sourceText.trim() ? 1 : 0.5)
      .onClick(() => { this.performTranslation(); });

      // 译文结果
      if (this.translatedText) {
        Column() {
          Row() {
            Text('译文').fontSize(14).fontWeight(FontWeight.Bold);
            Blank();
            Text(this.getLanguageName(this.targetLang)).fontSize(12).fontColor(Color.Gray);
          }
          .width('100%').margin({ bottom: 8 });

          Text(this.translatedText)
            .fontSize(16).lineHeight(24).fontColor('#2D3436');

          Row() {
            this.actionIcon($r('app.media.ic_copy'), () => { this.copyText(); });
            this.actionIcon($r('app.media.ic_voice'), () => { /* TTS 预留 */ });
            this.actionIcon($r('app.media.ic_refine'), () => { this.polishText(); });
          }
          .margin({ top: 12 });
        }
        .width('100%').padding(16)
        .backgroundColor(Color.White).borderRadius(12).margin(16);
      }

      // 翻译历史
      if (this.showHistory && this.history.length > 0) {
        Text('翻译历史').fontSize(16).fontWeight(FontWeight.Bold).margin({ left: 16 });
        List() {
          ForEach(this.history.slice(0, 10), (item: TranslationResult) => {
            ListItem() {
              Column() {
                Text(item.originalText).fontSize(14).fontColor('#636E72')
                  .maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis });
                Text(item.translatedText).fontSize(15).fontColor('#2D3436')
                  .maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis }).margin({ top: 4 });
              }
              .padding(12).backgroundColor(Color.White).borderRadius(8).margin({ bottom: 6 })
              .onClick(() => {
                this.sourceText = item.originalText;
                this.translatedText = item.translatedText;
                this.sourceLang = item.sourceLang;
                this.targetLang = item.targetLang;
                this.showHistory = false;
              });
            }
          }, (item: TranslationResult) => item.createTime.toString());
        }
        .layoutWeight(1).padding({ left: 16, right: 16 });
      }

      // 底部导航栏占位
      Row().width('100%').height(this.navBarHeight);
    }
    .width('100%').height('100%').backgroundColor('#F5F6FA');
  }

  @Builder
  languageSelector(code: string, isSource: boolean) {
    Select([
      { value: 'zh-CN', icon: $r('app.media.ic_flag_cn') },
      { value: 'en', icon: $r('app.media.ic_flag_en') },
      { value: 'ja', icon: $r('app.media.ic_flag_ja') },
      { value: 'ko', icon: $r('app.media.ic_flag_ko') }
    ])
    .selected(code)
    .value(this.getLanguageName(code))
    .onSelect((index: number) => {
      const lang = SUPPORTED_LANGUAGES[index];
      if (isSource) this.sourceLang = lang.code;
      else this.targetLang = lang.code;
    });
  }

  @Builder
  actionIcon(icon: Resource, action: () => void) {
    Image(icon).width(22).height(22)
      .margin({ right: 16 }).onClick(() => action());
  }

  swapLanguages() {
    const temp = this.sourceLang;
    this.sourceLang = this.targetLang;
    this.targetLang = temp;
    const tempText = this.sourceText;
    this.sourceText = this.translatedText;
    this.translatedText = tempText;
  }

  async performTranslation() {
    if (!this.sourceText.trim()) return;
    this.isTranslating = true;

    try {
      const result = await this.translateManager.translate({
        text: this.sourceText,
        sourceLang: this.isAutoDetect ? 'auto' : this.sourceLang,
        targetLang: this.targetLang
      });
      this.translatedText = result.translatedText;
      this.history = this.translateManager.getHistory();
    } catch (error) {
      ToastUtil.show('翻译失败,请重试');
    } finally {
      this.isTranslating = false;
    }
  }

  async polishText() {
    if (!this.translatedText) return;
    try {
      const polished = await this.translateManager.polishText(this.translatedText, 'formal');
      this.translatedText = polished;
      ToastUtil.show('润色完成');
    } catch {
      ToastUtil.show('润色失败');
    }
  }

  async copyText() {
    try {
      const clipboard = getContext(this).clipboard;
      await clipboard.set({ primary: this.translatedText });
      ToastUtil.show('已复制');
    } catch {
      ToastUtil.show('复制失败');
    }
  }

  getLanguageName(code: string): string {
    if (code === 'auto') return '自动检测';
    const lang = SUPPORTED_LANGUAGES.find(l => l.code === code);
    return lang ? lang.name : code;
  }
}

六、Prompt 模板

6.1 translate.md

markdown 复制代码
---
name: translate
version: 1.3.0
description: AI 翻译系统 Prompt
---

你是一个专业的翻译助手和语言专家。你的任务是将文本从 {{sourceLang}} 翻译为 {{targetLang}}。

## 翻译规范

1. **准确性**:忠实传达原文含义,不添加、不遗漏
2. **流畅性**:译文符合目标语言表达习惯,不生硬
3. **风格一致**:保持原文的语气(正式/口语/文学/技术)
4. **术语统一**:专业术语使用标准译法
5. **文化适配**:涉及文化差异时适当本地化

## 翻译步骤

1. 理解原文的完整语境和意图
2. 识别文化特定表达和成语
3. 选择合适的词汇和句式
4. 检查译文的流畅度和准确性

## 输出格式

直接输出翻译结果,不要添加额外说明。
如果原文包含代码或专业术语,请保持其原样。

七、数据持久化与缓存

7.1 使用 relationalStore 持久化翻译历史

typescript 复制代码
// database/TranslationDatabase.ts
import { relationalStore } from '@kit.ArkData';

export class TranslationDatabase {
  private static instance: TranslationDatabase;
  private rdbStore: relationalStore.RdbStore | null = null;
  private readonly DB_NAME = 'translation.db';
  private readonly DB_VERSION = 1;

  static getInstance(): TranslationDatabase {
    if (!TranslationDatabase.instance) {
      TranslationDatabase.instance = new TranslationDatabase();
    }
    return TranslationDatabase.instance;
  }

  async init(context: Context): Promise<void> {
    const config: relationalStore.StoreConfig = {
      name: this.DB_NAME,
      securityLevel: relationalStore.SecurityLevel.S1
    };
    this.rdbStore = await relationalStore.getRdbStore(context, config);
    await this.createTable();
  }

  private async createTable(): Promise<void> {
    const sql = `CREATE TABLE IF NOT EXISTS translations (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      original_text TEXT NOT NULL,
      translated_text TEXT NOT NULL,
      source_lang TEXT,
      target_lang TEXT,
      create_time INTEGER
    )`;
    await this.rdbStore?.executeSql(sql);
  }

  async insert(record: TranslationRecord): Promise<number> {
    const valueBucket: relationalStore.ValuesBucket = {
      original_text: record.originalText,
      translated_text: record.translatedText,
      source_lang: record.sourceLang,
      target_lang: record.targetLang,
      create_time: record.createTime
    };
    return await this.rdbStore?.insert('translations', valueBucket) || -1;
  }

  async queryAll(): Promise<TranslationRecord[]> {
    const predicates = new relationalStore.RdbPredicates('translations');
    predicates.orderByDesc('create_time');
    const resultSet = await this.rdbStore?.query(predicates, ['id', 'original_text', 'translated_text', 'source_lang', 'target_lang', 'create_time']);
    const records: TranslationRecord[] = [];
    while (resultSet?.goToNextRow()) {
      records.push({
        id: resultSet.getLong(resultSet.getColumnIndex('id')),
        originalText: resultSet.getString(resultSet.getColumnIndex('original_text')),
        translatedText: resultSet.getString(resultSet.getColumnIndex('translated_text')),
        sourceLang: resultSet.getString(resultSet.getColumnIndex('source_lang')),
        targetLang: resultSet.getString(resultSet.getColumnIndex('target_lang')),
        createTime: resultSet.getLong(resultSet.getColumnIndex('create_time'))
      });
    }
    resultSet?.close();
    return records;
  }
}

7.2 CacheManager 缓存管理

typescript 复制代码
// cache/CacheManager.ts
export class CacheManager {
  private static instance: CacheManager;
  private memoryCache: Map<string, CacheEntry> = new Map();
  private readonly DEFAULT_TTL = 30 * 60 * 1000; // 30分钟

  static getInstance(): CacheManager {
    if (!CacheManager.instance) {
      CacheManager.instance = new CacheManager();
    }
    return CacheManager.instance;
  }

  // 内存缓存
  set<T>(key: string, value: T, ttl: number = this.DEFAULT_TTL): void {
    this.memoryCache.set(key, {
      data: value,
      expireAt: Date.now() + ttl
    });
  }

  get<T>(key: string): T | null {
    const entry = this.memoryCache.get(key);
    if (!entry) return null;
    if (Date.now() > entry.expireAt) {
      this.memoryCache.delete(key);
      return null;
    }
    return entry.data as T;
  }

  // 持久化缓存(使用 Preferences)
  async persist<T>(key: string, value: T): Promise<void> {
    const pref = await getPreferences(getContext(), 'translation_cache');
    await pref.put(key, JSON.stringify(value));
    await pref.flush();
  }

  async getPersisted<T>(key: string): Promise<T | null> {
    const pref = await getPreferences(getContext(), 'translation_cache');
    const json = await pref.get(key, '');
    if (!json) return null;
    try {
      return JSON.parse(json as string) as T;
    } catch {
      return null;
    }
  }

  clear(): void {
    this.memoryCache.clear();
  }
}

interface CacheEntry {
  data: unknown;
  expireAt: number;
}

缓存策略:采用内存缓存 + 持久化缓存双层架构。热点翻译结果缓存 30 分钟,历史记录持久化到 relationalStore,确保应用重启后数据不丢失。


八、性能优化

8.1 翻译缓存策略

typescript 复制代码
// 使用 LRU 缓存翻译结果
class TranslationCache {
  private cache: Map<string, TranslationResult> = new Map();
  private readonly MAX_SIZE = 100;

  getKey(request: TranslationRequest): string {
    return `${request.text}:${request.sourceLang}:${request.targetLang}`;
  }

  get(request: TranslationRequest): TranslationResult | undefined {
    const key = this.getKey(request);
    const result = this.cache.get(key);
    if (result) {
      this.cache.delete(key);
      this.cache.set(key, result); // 移到末尾(最近使用)
    }
    return result;
  }

  set(request: TranslationRequest, result: TranslationResult): void {
    const key = this.getKey(request);
    if (this.cache.size >= this.MAX_SIZE) {
      const firstKey = this.cache.keys().next().value;
      firstKey && this.cache.delete(firstKey);
    }
    this.cache.set(key, result);
  }

  clear(): void {
    this.cache.clear();
  }
}

8.2 请求防抖与合并

typescript 复制代码
export class TranslationBatcher {
  private queue: Map<string, Promise<TranslationResult>> = new Map();
  private readonly DEBOUNCE_MS = 300;

  async translate(request: TranslationRequest): Promise<TranslationResult> {
    const key = this.buildKey(request);
    
    // 防抖:300ms 内的相同请求合并
    if (this.queue.has(key)) {
      return this.queue.get(key)!;
    }

    const promise = this.delayedTranslate(request);
    this.queue.set(key, promise);
    
    setTimeout(() => {
      this.queue.delete(key);
    }, this.DEBOUNCE_MS);

    return promise;
  }

  private async delayedTranslate(request: TranslationRequest): Promise<TranslationResult> {
    await new Promise(r => setTimeout(r, this.DEBOUNCE_MS));
    return TranslateManager.getInstance().translate(request);
  }

  private buildKey(request: TranslationRequest): string {
    return `${request.text}:${request.sourceLang}:${request.targetLang}`;
  }
}

批量优化:相同文本的翻译请求在 300ms 内自动合并,减少重复 API 调用,提升性能并节省费用。


九、翻译质量评估

9.1 AI 翻译 vs 传统翻译

对比维度 AI 翻译 传统翻译(Google/百度)
上下文理解 理解完整语境 逐句翻译
成语翻译 意译准确 字面翻译
语气保持 保持原文风格 语气丢失
专业术语 术语准确 需要词典
长文本 前后一致 不一致
创造性 可润色改写 死板直译

9.2 语言对翻译质量评估

typescript 复制代码
export class TranslationQualityEvaluator {
  // BLEU 分数评估
  static evaluateBLEU(hypothesis: string, reference: string): number {
    const hypTokens = hypothesis.split('');
    const refTokens = reference.split('');
    
    let matches = 0;
    for (const token of hypTokens) {
      if (refTokens.includes(token)) {
        matches++;
      }
    }
    
    const precision = matches / hypTokens.length;
    const brevityPenalty = Math.min(1, refTokens.length / hypTokens.length);
    
    return precision * brevityPenalty;
  }

  // 语言对质量评分
  static readonly LANGUAGE_QUALITY: Record<string, number> = {
    'zh-CN': 0.95,  // 中文 → 英文 质量优秀
    'en': 0.92,     // 英文 → 中文 质量优秀
    'ja': 0.85,     // 日语 → 中文 质量良好
    'ko': 0.82,     // 韩语 → 中文 质量良好
    'fr': 0.88,     // 法语 → 中文 质量优秀
    'de': 0.86,     // 德语 → 中文 质量优秀
  };
}

十、Git 提交

bash 复制代码
git add .
git commit -m "feat(translate): 完成 AI 翻译助手

- 设计翻译数据模型(10 种语言)
- 实现 TranslateManager(翻译/检测/润色)
- 实现 TranslatePage(原文/译文/语言切换)
- 实现安全区适配(AppStorage + display)
- 集成 relationalStore 持久化翻译历史
- 实现 CacheManager 内存缓存 + 持久化缓存
- 支持 OpenAI/DeepSeek/Qwen/智谱/豆包 多 Provider
- 实现 PromptManager 版本控制
- 实现翻译缓存 LRU
- 实现翻译历史记录
- 10 种语言支持
- 自动语言检测
- 使用 SVG 矢量图替代 emoji

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>"

git tag v0.1.0

总结

本文实现了完整的 AI 翻译助手。核心要点:

  1. 10 种语言支持:中英日韩法德西俄阿葡
  2. 自动语言检测:AI 自动识别输入语言
  3. TranslateManager:翻译/润色/检测统一管理
  4. 多 Provider 支持:OpenAI、DeepSeek、Qwen、智谱、豆包自由切换
  5. PromptManager:独立管理 prompt,支持版本控制
  6. 安全区适配:通过 AppStorage 获取 statusBarHeight 和 navBarHeight
  7. 数据持久化:使用 relationalStore 保存翻译历史
  8. 双层缓存:CacheManager 内存缓存 + 持久化缓存
  9. SVG 图标:所有图标使用矢量图,不使用 emoji
  10. 翻译缓存:LRU 缓存提升重复翻译效率

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源

相关推荐
数智化转型推荐官1 小时前
泛微e-nation政务办公平台:AI驱动数智政务一网协同方案解析
人工智能·政务
fai厅的秃头姐!2 小时前
OpenCV实战篇
人工智能·opencv·计算机视觉
TMT星球3 小时前
虎鲸文娱春苗编剧计划全面升级,已孵化20个剧本进入前期开发
大数据·人工智能
世人万千丶9 小时前
鸿蒙日志体系高级应用:HiLog分级输出/隐私脱敏/远程日志采集/线上问题精准溯源方案
学习·harmonyos·鸿蒙
奔跑中的小象9 小时前
统信UOS + 天数AI卡部署SGLang服务手册
人工智能·uos·sglang·天数智芯
DevSecOps选型指南9 小时前
中国版Mythos,为何是悬镜安全灵脉CodeAI?
人工智能·安全
Shockang10 小时前
LangGraph 状态机实战
人工智能
刹那芳华199210 小时前
循环神经网络的从零开始实现(RNN)
人工智能·rnn·深度学习
阿童木写作10 小时前
跨境图片翻译工具多合一,批量图片视频字幕翻译加智能抠图
人工智能·python·音视频·语音识别