在这里插入图片描述
在这里插入图片描述

一、项目概述

在全球化进程加速的今天,语言翻译已成为人们日常交流的刚需。本文将详细介绍如何使用ArkTS语言开发一款AI翻译工具应用,该应用支持中英文互译,内置Mock离线数据,无需联网即可运行,并预留了翻译API网络调用接口。同时,本文还将探讨鸿蒙PC适配策略以及Flutter框架迁移路径。

1.1 应用功能特点

  • 智能翻译:输入中文文本,一键翻译成英文;支持中英文互译
  • 语言切换:点击↔按钮快速切换翻译方向
  • 离线运行:内置50条Mock翻译数据,无需网络即可使用
  • 历史记录:自动保存翻译历史,随时查阅
  • 一键复制:快速复制翻译结果
  • 极简UI:仅使用系统原生组件,界面简洁优雅
  • 跨端适配:完美适配鸿蒙手机、平板、PC等设备

1.2 技术栈

  • 语言:ArkTS(HarmonyOS NEXT)
  • UI框架:ArkUI声明式语法
  • 状态管理:@State装饰器
  • 数据持久化:@ohos.data.preferences
  • API版本:API 24

二、ArkTS语言基础

2.1 ArkTS简介

ArkTS是HarmonyOS NEXT推出的全新编程语言,它扩展了TypeScript,提供了更丰富的类型系统和声明式UI能力。ArkTS具有以下特点:

  1. 类型安全:严格的类型检查,禁止any和unknown类型
  2. 声明式UI:使用ArkUI框架构建界面,代码即UI
  3. 响应式状态管理:通过@State、@Prop、@Link等装饰器实现状态管理
  4. 组件化开发:支持自定义组件,代码复用性高

2.2 核心语法特性

2.2.1 接口定义

ArkTS要求所有对象必须显式定义接口,禁止使用any类型:

interface TranslationRecord {
  source: string;
  target: string;
  sourceLang: string;
  targetLang: string;
  timestamp: number;
}

interface MockEntry {
  source: string;
  target: string;
}
2.2.2 状态管理

使用@State装饰器管理组件状态:

@State inputText: string = '';
@State outputText: string = '';
@State isLoading: boolean = false;
@State history: TranslationRecord[] = [];
@State sourceLang: string = '中文';
@State targetLang: string = 'English';
@State sourceLangCode: string = 'zh';
@State targetLangCode: string = 'en';
2.2.3 组件构建

使用build方法构建UI界面:

build() {
  Column() {
    Text('AI翻译工具')
      .fontSize(20)
      .fontWeight(FontWeight.Bold)
      .fontColor('#FFFFFF')
  }
  .width('100%')
  .height(56)
  .backgroundColor('#1976D2')
}

三、应用架构设计

3.1 单文件架构

本应用采用单文件架构,所有代码集中在Index.ets中,包括:

  1. 接口定义:TranslationRecord、MockEntry、ParseResult
  2. 状态管理:@State装饰的状态变量
  3. Mock数据:内置50条翻译模板数据(中→英30条,英→中20条)
  4. 数据持久化:Preferences存储历史记录
  5. 业务逻辑:翻译处理、语言切换、数据解析、格式化时间等
  6. UI构建:输入区域、输出区域、历史记录

3.2 核心模块

3.2.1 输入模块

输入模块包含原文输入框和语言标签:

TextArea({ placeholder: '请输入要翻译的文本...', text: this.inputText })
  .width('100%')
  .height(100)
  .fontSize(15)
  .padding({ left: 12, right: 12, top: 12, bottom: 12 })
  .backgroundColor('#FFFFFF')
  .borderRadius(8)
  .borderWidth(1)
  .borderColor('#E0E0E0')
  .maxLength(500)
  .onChange((value: string) => {
    this.inputText = value;
  })
3.2.2 语言切换模块

语言切换模块处理翻译方向切换:

private switchLanguage(): void {
  let tempLang: string = this.sourceLang;
  let tempCode: string = this.sourceLangCode;
  
  this.sourceLang = this.targetLang;
  this.sourceLangCode = this.targetLangCode;
  this.targetLang = tempLang;
  this.targetLangCode = tempCode;
  
  this.inputText = '';
  this.outputText = '';
}
3.2.3 翻译模块

翻译模块处理用户点击翻译按钮后的逻辑:

private handleTranslate(): void {
  if (this.inputText.trim().length === 0) {
    this.errorText = '请输入要翻译的内容';
    return;
  }

  this.errorText = '';
  this.outputText = '';
  this.isLoading = true;

  setTimeout(() => {
    let translatedText: string = this.getMockTranslation(this.inputText);
    this.isLoading = false;
    this.outputText = translatedText;

    let record: TranslationRecord = {
      source: this.inputText.trim(),
      target: translatedText,
      sourceLang: this.sourceLang,
      targetLang: this.targetLang,
      timestamp: Date.now()
    };

    let newHistory: TranslationRecord[] = [record];
    for (let i = 0; i < this.history.length; i++) {
      newHistory.push(this.history[i]);
    }
    this.history = newHistory;

    this.saveHistory();
  }, 800);
}
3.2.4 Mock数据模块

Mock数据模块提供离线翻译模板:

private readonly MOCK_DATA_ZH_EN: MockEntry[] = [
  { source: '你好', target: 'Hello' },
  { source: '谢谢', target: 'Thank you' },
  { source: '再见', target: 'Goodbye' },
  // 更多Mock数据...
];

private readonly MOCK_DATA_EN_ZH: MockEntry[] = [
  { source: 'Hello', target: '你好' },
  { source: 'Thank you', target: '谢谢' },
  { source: 'Goodbye', target: '再见' },
  // 更多Mock数据...
];
3.2.5 数据持久化模块

使用@ohos.data.preferences保存历史记录:

private async saveHistory(): Promise<void> {
  if (!this.context) {
    return;
  }
  try {
    let prefs = await this.getPreferences();
    let stringList: Array<string> = [];
    for (let i = 0; i < this.history.length; i++) {
      let record = this.history[i];
      stringList.push(JSON.stringify(record));
    }
    let jsonStr: string = JSON.stringify(stringList);
    await prefs.put(this.STORAGE_KEY, jsonStr);
    await prefs.flush();
  } catch (error) {
    console.error('保存历史记录失败');
  }
}

四、Mock离线方案详解

4.1 Mock数据设计

Mock数据采用双向结构,覆盖中文→英文和英文→中文两个方向:

中文→英文(30条)

序号 中文原文 英文翻译
1 你好 Hello
2 谢谢 Thank you
3 再见 Goodbye
4 我爱你 I love you
5 早上好 Good morning
6 晚上好 Good evening
7 你叫什么名字 What is your name?
8 我来自中国 I am from China
9 今天天气很好 The weather is nice today
10 请帮我翻译 Please help me translate
11 这是一个测试 This is a test
12 学习英语很重要 Learning English is very important
13 欢迎来到北京 Welcome to Beijing
14 新年快乐 Happy New Year
15 生日快乐 Happy Birthday
16 恭喜发财 May you be prosperous
17 万事如意 May all your wishes come true
18 身体健康 Good health
19 工作顺利 Smooth work
20 心想事成 May all your dreams come true
21 你好世界 Hello World
22 计算机科学 Computer Science
23 人工智能 Artificial Intelligence
24 机器学习 Machine Learning
25 深度学习 Deep Learning
26 大数据 Big Data
27 云计算 Cloud Computing
28 物联网 Internet of Things
29 区块链 Blockchain
30 5G技术 5G Technology

英文→中文(20条)

序号 英文原文 中文翻译
1 Hello 你好
2 Thank you 谢谢
3 Goodbye 再见
4 I love you 我爱你
5 Good morning 早上好
6 Good evening 晚上好
7 What is your name? 你叫什么名字
8 I am from China 我来自中国
9 The weather is nice today 今天天气很好
10 Please help me translate 请帮我翻译
11 This is a test 这是一个测试
12 Learning English is very important 学习英语很重要
13 Welcome to Beijing 欢迎来到北京
14 Happy New Year 新年快乐
15 Happy Birthday 生日快乐
16 Computer Science 计算机科学
17 Artificial Intelligence 人工智能
18 Machine Learning 机器学习
19 Deep Learning 深度学习
20 Big Data 大数据

4.2 翻译逻辑

根据当前语言方向选择对应的Mock数据:

private getMockTranslation(source: string): string {
  let mockData: MockEntry[] = this.MOCK_DATA_ZH_EN;
  if (this.sourceLangCode === 'en' && this.targetLangCode === 'zh') {
    mockData = this.MOCK_DATA_EN_ZH;
  }

  for (let i = 0; i < mockData.length; i++) {
    let entry = mockData[i];
    if (entry.source === source) {
      return entry.target;
    }
  }

  return this.generateDefaultTranslation(source);
}

4.3 默认内容生成

对于未匹配到的文本,应用会自动生成默认翻译:

private generateDefaultTranslation(source: string): string {
  return '[AI翻译] ' + source;
}

4.4 离线运行优势

  • 零网络依赖:应用完全离线可用,适合网络环境不佳的场景
  • 快速响应:本地Mock数据响应时间小于1秒
  • 隐私安全:用户数据不经过网络传输,保护隐私
  • 开发调试:方便开发阶段测试和调试

五、网络API预留方案

5.1 预留接口设计

应用预留了真实翻译API调用接口,取消注释即可使用:

/*
import http from '@ohos.net.http';

private async fetchOnlineTranslation(source: string): Promise<string> {
  let httpRequest = http.createHttp();
  let response = await httpRequest.request(
    'https://api.example.com/translate',
    {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_API_KEY'
      },
      extraData: {
        'text': source,
        'source': this.sourceLangCode,
        'target': this.targetLangCode
      }
    }
  );
  let result = JSON.parse(response.result as string);
  return result['translation'];
}
*/

5.2 权限配置

在module.json5中配置网络权限:

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  }
]

5.3 API切换策略

应用支持Mock模式和网络模式的切换:

private useOnlineAPI: boolean = false;

private async getTranslation(source: string): Promise<string> {
  if (this.useOnlineAPI) {
    return await this.fetchOnlineTranslation(source);
  }
  return this.getMockTranslation(source);
}

六、鸿蒙PC适配策略

6.1 响应式布局

使用layoutWeight实现响应式布局:

Row() {
  Column() {
    // 原文输入区域
  }
  .layoutWeight(1)

  Button() {
    Text('↔')
  }
  .width(48)
  .height(48)

  Column() {
    // 译文输出区域
  }
  .layoutWeight(1)
}

6.2 尺寸适配

使用百分比和flex布局适配不同屏幕尺寸:

Button('翻译')
  .width('45%')
  .height(44)
  .fontSize(16)

6.3 PC端优化建议

  1. 增加侧边栏:在PC端显示翻译历史侧边栏
  2. 调整字体大小:PC端使用更大的字体
  3. 添加快捷键:支持Ctrl+Enter快速翻译
  4. 窗口化支持:支持窗口拖拽和缩放
  5. 多语言支持:增加更多语言选择

七、鸿蒙Flutter框架迁移路径

7.1 框架对比

特性 ArkTS Flutter
语言 TypeScript扩展 Dart
UI框架 ArkUI Material/Cupertino
状态管理 @State/@Prop/@Link setState/Provider/Riverpod
跨平台 鸿蒙生态 多平台
性能 原生性能 接近原生

7.2 代码迁移示例

7.2.1 状态管理迁移

ArkTS:

@State inputText: string = '';

Flutter:

String inputText = '';
setState(() {
  inputText = newValue;
});
7.2.2 UI组件迁移

ArkTS:

TextArea({ placeholder: '请输入要翻译的文本...', text: this.inputText })
  .onChange((value: string) => {
    this.inputText = value;
  })

Flutter:

TextField(
  decoration: InputDecoration(
    hintText: '请输入要翻译的文本...',
  ),
  onChanged: (value) {
    setState(() {
      inputText = value;
    });
  },
)
7.2.3 数据持久化迁移

ArkTS:

let prefs = await preferences.getPreferences(this.context, 'ai_translate_app');
await prefs.put('key', 'value');

Flutter:

SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('key', 'value');
7.2.4 语言切换迁移

ArkTS:

private switchLanguage(): void {
  let tempLang: string = this.sourceLang;
  this.sourceLang = this.targetLang;
  this.targetLang = tempLang;
}

Flutter:

void switchLanguage() {
  setState(() {
    String tempLang = sourceLang;
    sourceLang = targetLang;
    targetLang = tempLang;
  });
}

7.3 迁移注意事项

  1. 语言差异:TypeScript和Dart的语法差异
  2. UI框架:ArkUI和Material Design的差异
  3. 生态差异:鸿蒙生态和Flutter生态的差异
  4. 性能考虑:原生性能和Flutter性能的权衡
  5. 权限配置:鸿蒙权限和Android/iOS权限的差异

八、性能优化策略

8.1 状态管理优化

  • 使用@State管理局部状态
  • 避免不必要的状态更新
  • 使用const声明不可变数据
private readonly STORAGE_KEY: string = 'translation_history';

8.2 渲染优化

  • 使用ForEach时提供唯一的key值
  • 避免在build方法中创建新对象
  • 使用懒加载减少内存占用
ForEach(this.history, (record: TranslationRecord) => {
  // 渲染历史记录
}, (record: TranslationRecord) => record.timestamp.toString())

8.3 存储优化

  • 批量存储减少IO操作
  • 使用字符串数组存储提高效率
  • 定期清理过期数据

8.4 输入优化

  • 设置输入字数限制(500字)
  • 使用TextArea替代TextInput支持多行输入
  • 添加输入验证

九、代码安全与最佳实践

9.1 类型安全

  • 禁止使用any和unknown类型
  • 显式定义所有接口
  • 使用类型断言时确保类型安全

9.2 异常处理

  • 所有异步操作都有try-catch包裹
  • 提供友好的错误提示
  • 记录错误日志便于排查

9.3 代码规范

  • 变量命名使用驼峰式
  • 方法命名使用动词开头
  • 代码缩进统一为2个空格

9.4 隐私保护

  • 数据本地存储,不上传云端
  • 敏感信息加密存储
  • 权限最小化原则

十、功能测试与验证

10.1 功能测试用例

测试用例1:中文→英文翻译

  • 输入:你好
  • 预期输出:Hello

测试用例2:英文→中文翻译

  • 输入:Hello
  • 预期输出:你好

测试用例3:语言切换

  • 点击↔按钮
  • 预期:sourceLang和targetLang互换

测试用例4:历史记录保存

  • 翻译一条文本
  • 关闭应用重新打开
  • 预期:历史记录显示该条翻译

测试用例5:清空功能

  • 翻译多条文本
  • 点击清空按钮
  • 预期:所有内容和历史记录被清空

测试用例6:输入验证

  • 输入空文本
  • 点击翻译按钮
  • 预期:显示错误提示"请输入要翻译的内容"

测试用例7:复制功能

  • 翻译一条文本
  • 点击复制按钮
  • 预期:按钮显示"已复制",2秒后恢复"复制"

10.2 兼容性测试

  • 鸿蒙手机(API 24+)
  • 鸿蒙平板
  • 鸿蒙PC
  • 不同屏幕分辨率

10.3 性能测试

  • 翻译响应时间:< 1秒
  • 内存占用:< 50MB
  • 启动时间:< 2秒

十一、未来规划

11.1 功能扩展

  1. 多语言支持:增加日语、韩语、法语、德语等更多语言
  2. 语音输入:支持语音输入翻译
  3. 语音输出:支持翻译结果语音播报
  4. 图片翻译:支持拍照翻译
  5. 离线词典:内置离线词典数据

11.2 技术升级

  1. AI模型集成:集成真实翻译API
  2. 智能推荐:根据历史记录推荐翻译内容
  3. 翻译记忆:自动保存常用翻译
  4. 多设备协同:支持鸿蒙多设备协同

11.3 平台适配

  1. 鸿蒙PC优化:针对PC端进行深度优化
  2. 平板适配:优化平板端布局
  3. 车机适配:适配鸿蒙车机系统
  4. 手表适配:适配鸿蒙智能手表

十二、常见问题与解决方案

12.1 编译错误

问题1:“Cannot find module ‘@ohos.data.preferences’”

  • 解决方案:确保API版本设置为24,检查项目配置

问题2:“Use explicit types instead of ‘any’, ‘unknown’”

  • 解决方案:显式定义所有接口,避免使用any类型

问题3:“Function may throw exceptions. Special handling is required.”

  • 解决方案:添加try-catch异常处理

12.2 运行时错误

问题1:历史记录无法保存

  • 解决方案:检查context是否为null,检查权限配置

问题2:翻译结果显示异常

  • 解决方案:检查Mock数据格式,检查语言方向判断逻辑

问题3:语言切换不生效

  • 解决方案:检查switchLanguage方法实现

12.3 性能问题

问题1:应用启动慢

  • 解决方案:优化loadHistory方法,使用异步加载

问题2:滚动卡顿

  • 解决方案:优化ForEach渲染,使用懒加载

附录:完整代码

Index.ets

import preferences from '@ohos.data.preferences';
import common from '@ohos.app.ability.common';

interface TranslationRecord {
  source: string;
  target: string;
  sourceLang: string;
  targetLang: string;
  timestamp: number;
}

interface MockEntry {
  source: string;
  target: string;
}

interface ParseResult {
  success: boolean;
  data: Array<string> | null;
}

@Entry
@Component
struct Index {
  @State inputText: string = '';
  @State outputText: string = '';
  @State isLoading: boolean = false;
  @State history: TranslationRecord[] = [];
  @State errorText: string = '';
  @State sourceLang: string = '中文';
  @State targetLang: string = 'English';
  @State sourceLangCode: string = 'zh';
  @State targetLangCode: string = 'en';
  @State copySuccess: boolean = false;
  
  private preferencesInst: preferences.Preferences | null = null;
  private context: common.Context | null = null;
  private readonly STORAGE_KEY: string = 'translation_history';

  private readonly MOCK_DATA_ZH_EN: MockEntry[] = [
    { source: '你好', target: 'Hello' },
    { source: '谢谢', target: 'Thank you' },
    { source: '再见', target: 'Goodbye' },
    { source: '我爱你', target: 'I love you' },
    { source: '早上好', target: 'Good morning' },
    { source: '晚上好', target: 'Good evening' },
    { source: '你叫什么名字', target: 'What is your name?' },
    { source: '我来自中国', target: 'I am from China' },
    { source: '今天天气很好', target: 'The weather is nice today' },
    { source: '请帮我翻译', target: 'Please help me translate' },
    { source: '这是一个测试', target: 'This is a test' },
    { source: '学习英语很重要', target: 'Learning English is very important' },
    { source: '欢迎来到北京', target: 'Welcome to Beijing' },
    { source: '新年快乐', target: 'Happy New Year' },
    { source: '生日快乐', target: 'Happy Birthday' },
    { source: '恭喜发财', target: 'May you be prosperous' },
    { source: '万事如意', target: 'May all your wishes come true' },
    { source: '身体健康', target: 'Good health' },
    { source: '工作顺利', target: 'Smooth work' },
    { source: '心想事成', target: 'May all your dreams come true' },
    { source: '你好世界', target: 'Hello World' },
    { source: '计算机科学', target: 'Computer Science' },
    { source: '人工智能', target: 'Artificial Intelligence' },
    { source: '机器学习', target: 'Machine Learning' },
    { source: '深度学习', target: 'Deep Learning' },
    { source: '大数据', target: 'Big Data' },
    { source: '云计算', target: 'Cloud Computing' },
    { source: '物联网', target: 'Internet of Things' },
    { source: '区块链', target: 'Blockchain' },
    { source: '5G技术', target: '5G Technology' }
  ];

  private readonly MOCK_DATA_EN_ZH: MockEntry[] = [
    { source: 'Hello', target: '你好' },
    { source: 'Thank you', target: '谢谢' },
    { source: 'Goodbye', target: '再见' },
    { source: 'I love you', target: '我爱你' },
    { source: 'Good morning', target: '早上好' },
    { source: 'Good evening', target: '晚上好' },
    { source: 'What is your name?', target: '你叫什么名字' },
    { source: 'I am from China', target: '我来自中国' },
    { source: 'The weather is nice today', target: '今天天气很好' },
    { source: 'Please help me translate', target: '请帮我翻译' },
    { source: 'This is a test', target: '这是一个测试' },
    { source: 'Learning English is very important', target: '学习英语很重要' },
    { source: 'Welcome to Beijing', target: '欢迎来到北京' },
    { source: 'Happy New Year', target: '新年快乐' },
    { source: 'Happy Birthday', target: '生日快乐' },
    { source: 'Computer Science', target: '计算机科学' },
    { source: 'Artificial Intelligence', target: '人工智能' },
    { source: 'Machine Learning', target: '机器学习' },
    { source: 'Deep Learning', target: '深度学习' },
    { source: 'Big Data', target: '大数据' }
  ];

  aboutToAppear(): void {
    this.context = getContext(this) as common.Context;
    this.loadHistory();
  }

  private async loadHistory(): Promise<void> {
    if (!this.context) {
      return;
    }
    try {
      let prefs = await this.getPreferences();
      let storedValue = await prefs.get(this.STORAGE_KEY, '');
      let jsonStr: string = storedValue as string;
      if (jsonStr.length > 0) {
        let parseResult = this.parseStringList(jsonStr);
        if (parseResult.success && parseResult.data) {
          let records: TranslationRecord[] = [];
          for (let i = 0; i < parseResult.data.length; i++) {
            let str: string = parseResult.data[i];
            let record = this.parseTranslationRecord(str);
            if (record) {
              records.push(record);
            }
          }
          this.history = records;
        }
      }
    } catch (error) {
      console.error('加载历史记录失败');
    }
  }

  private async saveHistory(): Promise<void> {
    if (!this.context) {
      return;
    }
    try {
      let prefs = await this.getPreferences();
      let stringList: Array<string> = [];
      for (let i = 0; i < this.history.length; i++) {
        let record = this.history[i];
        stringList.push(JSON.stringify(record));
      }
      let jsonStr: string = JSON.stringify(stringList);
      await prefs.put(this.STORAGE_KEY, jsonStr);
      await prefs.flush();
    } catch (error) {
      console.error('保存历史记录失败');
    }
  }

  private async getPreferences(): Promise<preferences.Preferences> {
    if (this.preferencesInst) {
      return this.preferencesInst;
    }
    if (!this.context) {
      throw new Error('Context is null');
    }
    try {
      this.preferencesInst = await preferences.getPreferences(this.context, 'ai_translate_app');
    } catch (error) {
      console.error('获取Preferences失败');
      throw new Error('获取Preferences失败');
    }
    return this.preferencesInst;
  }

  private parseStringList(jsonStr: string): ParseResult {
    let result: ParseResult = {
      success: false,
      data: null
    };
    try {
      let parsedObj: Object = JSON.parse(jsonStr) as Object;
      if (Array.isArray(parsedObj)) {
        let stringArray: Array<string> = [];
        let parsedArray: Object[] = parsedObj as Object[];
        for (let i = 0; i < parsedArray.length; i++) {
          let item: Object = parsedArray[i];
          if (typeof item === 'string') {
            stringArray.push(item);
          }
        }
        result.success = true;
        result.data = stringArray;
      }
    } catch (error) {
      console.error('解析字符串列表失败');
    }
    return result;
  }

  private parseTranslationRecord(jsonStr: string): TranslationRecord | null {
    try {
      let parsedObj: Object = JSON.parse(jsonStr) as Object;
      if (typeof parsedObj === 'object' && parsedObj !== null) {
        let data: Record<string, Object> = parsedObj as Record<string, Object>;
        if (data.source && data.target && data.sourceLang && data.targetLang && data.timestamp) {
          return {
            source: String(data.source),
            target: String(data.target),
            sourceLang: String(data.sourceLang),
            targetLang: String(data.targetLang),
            timestamp: Number(data.timestamp)
          };
        }
      }
    } catch (error) {
      console.error('解析翻译记录失败');
    }
    return null;
  }

  private handleTranslate(): void {
    if (this.inputText.trim().length === 0) {
      this.errorText = '请输入要翻译的内容';
      return;
    }

    this.errorText = '';
    this.outputText = '';
    this.isLoading = true;

    setTimeout(() => {
      let translatedText: string = this.getMockTranslation(this.inputText);
      this.isLoading = false;
      this.outputText = translatedText;

      let record: TranslationRecord = {
        source: this.inputText.trim(),
        target: translatedText,
        sourceLang: this.sourceLang,
        targetLang: this.targetLang,
        timestamp: Date.now()
      };

      let newHistory: TranslationRecord[] = [record];
      for (let i = 0; i < this.history.length; i++) {
        newHistory.push(this.history[i]);
      }
      this.history = newHistory;

      this.saveHistory();
    }, 800);
  }

  private getMockTranslation(source: string): string {
    let mockData: MockEntry[] = this.MOCK_DATA_ZH_EN;
    if (this.sourceLangCode === 'en' && this.targetLangCode === 'zh') {
      mockData = this.MOCK_DATA_EN_ZH;
    }

    for (let i = 0; i < mockData.length; i++) {
      let entry = mockData[i];
      if (entry.source === source) {
        return entry.target;
      }
    }

    return this.generateDefaultTranslation(source);
  }

  private generateDefaultTranslation(source: string): string {
    return '[AI翻译] ' + source;
  }

  private copyContent(): void {
    if (this.outputText.length === 0) {
      return;
    }
    this.copySuccess = true;
    setTimeout(() => {
      this.copySuccess = false;
    }, 2000);
  }

  private clearAll(): void {
    this.inputText = '';
    this.outputText = '';
    this.errorText = '';
    this.history = [];
    this.saveHistory();
  }

  private switchLanguage(): void {
    let tempLang: string = this.sourceLang;
    let tempCode: string = this.sourceLangCode;
    
    this.sourceLang = this.targetLang;
    this.sourceLangCode = this.targetLangCode;
    this.targetLang = tempLang;
    this.targetLangCode = tempCode;
    
    this.inputText = '';
    this.outputText = '';
  }

  private formatTime(timestamp: number): string {
    let date: Date = new Date(timestamp);
    let year: string = date.getFullYear().toString();
    let month: string = (date.getMonth() + 1).toString().padStart(2, '0');
    let day: string = date.getDate().toString().padStart(2, '0');
    let hour: string = date.getHours().toString().padStart(2, '0');
    let minute: string = date.getMinutes().toString().padStart(2, '0');
    return year + '-' + month + '-' + day + ' ' + hour + ':' + minute;
  }

  build() {
    Column() {
      Row() {
        Text('AI翻译工具')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#FFFFFF')
      }
      .width('100%')
      .height(56)
      .padding({ left: 16, right: 16 })
      .backgroundColor('#1976D2')

      Column() {
        Row() {
          Column() {
            Text(this.sourceLang)
              .fontSize(14)
              .fontColor('#666666')
              .margin({ bottom: 4 })

            TextArea({ placeholder: '请输入要翻译的文本...', text: this.inputText })
              .width('100%')
              .height(100)
              .fontSize(15)
              .padding({ left: 12, right: 12, top: 12, bottom: 12 })
              .backgroundColor('#FFFFFF')
              .borderRadius(8)
              .borderWidth(1)
              .borderColor('#E0E0E0')
              .maxLength(500)
              .onChange((value: string) => {
                this.inputText = value;
              })
          }
          .layoutWeight(1)
          .margin({ right: 8 })

          Button() {
            Text('↔')
              .fontSize(20)
              .fontColor('#1976D2')
          }
          .width(48)
          .height(48)
          .backgroundColor('#FFFFFF')
          .borderRadius(8)
          .borderWidth(1)
          .borderColor('#E0E0E0')
          .margin({ top: 20 })
          .onClick(() => {
            this.switchLanguage();
          })

          Column() {
            Row() {
              Text(this.targetLang)
                .fontSize(14)
                .fontColor('#666666')
                .layoutWeight(1)

              if (this.outputText.length > 0) {
                Button(this.copySuccess ? '已复制' : '复制')
                  .width(48)
                  .height(28)
                  .fontSize(12)
                  .fontColor('#FFFFFF')
                  .backgroundColor('#1976D2')
                  .borderRadius(6)
                  .onClick(() => {
                    this.copyContent();
                  })
              }
            }
            .width('100%')
            .margin({ bottom: 4 })

            Text(this.outputText.length > 0 ? this.outputText : '翻译结果将显示在这里...')
              .width('100%')
              .height(100)
              .fontSize(15)
              .padding({ left: 12, right: 12, top: 12, bottom: 12 })
              .backgroundColor('#E3F2FD')
              .borderRadius(8)
              .borderWidth(1)
              .borderColor('#BBDEFB')
              .fontColor(this.outputText.length > 0 ? '#333333' : '#999999')
              .textAlign(TextAlign.Start)
              .textOverflow({ overflow: TextOverflow.Ellipsis })
              .maxLines(6)
          }
          .layoutWeight(1)
          .margin({ left: 8 })
        }
        .width('100%')
        .margin({ left: 16, right: 16, top: 16 })

        if (this.errorText.length > 0) {
          Text(this.errorText)
            .fontSize(13)
            .fontColor('#FF4444')
            .margin({ left: 16, right: 16, top: 8 })
        }

        Row() {
          Button('翻译')
            .width('45%')
            .height(44)
            .fontSize(16)
            .fontColor('#FFFFFF')
            .backgroundColor('#1976D2')
            .borderRadius(8)
            .enabled(!this.isLoading)
            .onClick(() => {
              this.handleTranslate();
            })

          Blank().width('10%')

          Button('清空')
            .width('45%')
            .height(44)
            .fontSize(16)
            .fontColor('#666666')
            .backgroundColor('#F5F5F5')
            .borderRadius(8)
            .onClick(() => {
              this.clearAll();
            })
        }
        .width('100%')
        .margin({ left: 16, right: 16, top: 16 })
      }
      .width('100%')
      .padding({ bottom: 16 })
      .backgroundColor('#FAFAFA')

      Divider()
        .height(1)
        .color('#E0E0E0')

      Scroll() {
        Column() {
          if (this.isLoading) {
            Column() {
              Text('AI正在翻译中...')
                .fontSize(15)
                .fontColor('#666666')
                .margin({ top: 20, bottom: 20 })
            }
            .width('100%')
            .padding({ left: 16, right: 16 })
          }

          if (this.history.length > 0) {
            Text('翻译历史')
              .fontSize(16)
              .fontWeight(FontWeight.Bold)
              .fontColor('#333333')
              .padding({ left: 16, right: 16, top: 16, bottom: 8 })

            Column() {
              ForEach(this.history, (record: TranslationRecord) => {
                Column() {
                  Row() {
                    Text(record.sourceLang + ' → ' + record.targetLang)
                      .fontSize(12)
                      .fontColor('#1976D2')
                      .layoutWeight(1)

                    Text(this.formatTime(record.timestamp))
                      .fontSize(12)
                      .fontColor('#999999')
                  }
                  .width('100%')
                  .margin({ bottom: 4 })

                  Text(record.source)
                    .fontSize(14)
                    .fontColor('#333333')
                    .width('100%')
                    .margin({ bottom: 4 })
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .maxLines(2)

                  Text(record.target)
                    .fontSize(14)
                    .fontColor('#1976D2')
                    .width('100%')
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .maxLines(2)
                }
                .width('100%')
                .padding({ left: 12, right: 12, top: 12, bottom: 12 })
                .backgroundColor('#FFFFFF')
                .borderRadius(8)
                .margin({ bottom: 8, left: 16, right: 16 })
              }, (record: TranslationRecord) => record.timestamp.toString())
            }
            .width('100%')
            .padding({ bottom: 16 })
          } else {
            Column() {
              Text('暂无翻译记录')
                .fontSize(14)
                .fontColor('#999999')
                .margin({ top: 20, bottom: 20 })
            }
            .width('100%')
            .padding({ left: 16, right: 16 })
          }
        }
        .width('100%')
      }
      .scrollable(ScrollDirection.Vertical)
      .width('100%')
      .layoutWeight(1)
      .backgroundColor('#F5F5F5')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }
}

/*
预留的真实翻译API调用代码

import http from '@ohos.net.http';

private async fetchOnlineTranslation(source: string): Promise<string> {
  let httpRequest = http.createHttp();
  let response = await httpRequest.request(
    'https://api.example.com/translate',
    {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_API_KEY'
      },
      extraData: {
        'text': source,
        'source': this.sourceLangCode,
        'target': this.targetLangCode
      }
    }
  );
  let result = JSON.parse(response.result as string);
  return result['translation'];
}
*/

总结

本文详细介绍了基于ArkTS开发AI翻译工具应用的全过程,包括:

  1. 项目架构设计:单文件架构,所有代码集中在Index.ets中
  2. 核心功能实现:智能翻译、语言切换、离线Mock数据、历史记录持久化
  3. 技术要点:ArkTS语法特性、状态管理、数据持久化、UI构建
  4. 适配策略:鸿蒙PC适配、响应式布局
  5. 迁移路径:Flutter框架迁移对比和代码示例
  6. 测试验证:功能测试用例、兼容性测试、性能测试

该应用充分展示了ArkTS语言的优势和鸿蒙生态的强大能力,为开发者提供了一个完整的鸿蒙原生应用开发参考。


参考文献

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐