asp.net网站开发工程师(c,做网站地图,主机免费云服务器如何,企业管理平台系统网站鸿蒙5.0项目开发——接入有道大模型翻译
【高心星出品】
项目效果图 项目功能 文本翻译功能 支持文本输入和翻译结果显示 使用有道翻译API进行翻译 支持自动检测语言#xff08;auto#xff09; 支持双向翻译#xff08;源语言和目标语言可互换#xff09; 文本操作…鸿蒙5.0项目开发——接入有道大模型翻译
【高心星出品】
项目效果图 项目功能 文本翻译功能 支持文本输入和翻译结果显示 使用有道翻译API进行翻译 支持自动检测语言auto 支持双向翻译源语言和目标语言可互换 文本操作功能 支持文本复制 支持文本全选 支持长按选择文本 支持滚动查看长文本 生词本功能 可以将翻译结果保存到生词本 支持保存选中文本的翻译 支持保存整个翻译结果 记录保存时间 用户界面特点 采用上下布局上方为输入区下方为结果显示区 支持实时翻译 提供清晰的视觉反馈 支持长按菜单操作 数据存储 使用鸿蒙系统的数据存储能力 支持生词本的本地存储 支持历史记录保存 网络功能 集成有道翻译API 支持HTTP请求 支持错误处理 支持数据流式传输 安全特性 使用API密钥进行身份验证 支持数据加密传输 实现签名验证机制 其他功能 支持剪贴板操作 提供操作提示Toast提示 支持文本格式化 支持多语言界面
大模型翻译 API 简介
大型模型翻译翻译的好助手使用此服务可以完成翻译、润色、扩写等功能。API可以处理各种复杂的语言结构、词汇和语境提供高质量的翻译结果。 同时可以根据用户的需 求和偏好进行定制化的翻译。用户可以通过调整参数、提供上下文信息或者进行反馈使翻译结果更符合个人或特 定领域的要求从而实现更加精准、个性化的翻译体验。
接入有道翻译过程 首先要注册成为有道智云的开发者并创建应用https://ai.youdao.com/doc.s#guide就可以拿到 应用ID 和 应用密钥 。 大模型翻译API HTTPS地址 https://openapi.youdao.com/llm_trans请求方式
规则描述传输方式HTTPS请求方式GET/POST字符编码统一使用UTF-8 编码请求格式表单响应格式text/event-stream
请求参数
字段名类型含义必填备注itext待翻译文本True必须是UTF-8编码限制5000字符prompttext提示词False必须是UTF-8编码限制1200字符、400单词fromtext源语言True参考下方支持语言 (可设置为auto)totext目标语言True参考下方支持语言streamTypetext流式返回类型False参考下方 流式返回类型appKeytext应用IDTrue可在应用管理 查看salttext随机字符串可使用UUID进行生产Trueuuid (可使用uuid生成)signtext签名Truesha256(应用IDinputsaltcurtime应用密钥)signTypetext签名类型Truev3curtimetext当前UTC时间戳(秒)TrueTimeStamphandleOptiontext处理模式选项False参考下方 处理模式选项polishOptiontext润色选项False参考下方 润色选项expandOptiontext扩写选项False参考下方 扩写选项 签名生成方法如下 signTypev3 signsha256(应用IDinputsaltcurtime应用密钥) 其中input的计算方式为inputi前10个字符 i长度 i后10个字符当i长度大于20或 inputi字符串当i长度小于等于20 流式返回类型SSE
event:begindata:{requestId:11,type:zh-CHS2en}event:messagedata:{transFull:null,transIncre:The}event:messagedata:{transFull:null,transIncre: w}...............event:enddata:{requestId:11,type:zh-CHS2en,eventTokenUsage:{inputToken:5,outputToken:7,totalToken:12}}网络请求工具封装
由于大模型翻译获取的是增量的翻译结果一次应答只能获取部分翻译结果所以我们发送请求的方式要用requestInStream发起流式请求然后在 req.on(‘dataReceive’, (data) {}中获取每次返回的SSE结果。
此工具封装最复杂的就是请求参数的获取时间戳curtime要获取当前系统时间的秒级结果并且服务器会将服务器时间与发送请求的时间戳进行对比如果差距超过15分钟请求就会失败所以要关注一下运行该段代码设备的时间。
/*** 生成请求参数* param q 待翻译的文本* returns 包含签名等信息的请求参数对象*/
export function genparm(q: string): reqparam {q q.trim()let salt util.generateRandomUUID()let curtime Math.round(new Date().getTime() / 1000) let param: reqparam {i: q,q: q,from: auto,to: auto,appKey: APPKEY,curtime: curtime,signType: v3,salt: salt,sign: sign(q, curtime, salt)}return param
}/*** 生成签名* param q 待翻译的文本* param curtime 当前时间戳* param salt 随机字符串* returns SHA256加密后的签名*/
function sign(q: string, curtime: string, salt: string) {let str APPKEY getinput(q) salt curtime APPSECRETlet result: string try {let mdAlgName SHA256; // 使用SHA256算法let md cryptoFramework.createMd(mdAlgName);// 更新数据md.updateSync({ data: new Uint8Array(buffer.from(str, utf-8).buffer) });let mdResult md.digestSync();// 将摘要结果转换为十六进制字符串result Array.from(mdResult.data).map(byte byte.toString(16).padStart(2, 0)).join();} catch (error) {console.error(SHA256编码失败:, error);}return result
}/*** 处理输入文本* param q 待处理的文本* returns 处理后的文本* 如果文本长度大于20则取前10个字符和后10个字符中间加上长度*/
function getinput(q: string) {let len q.lengthlet result: stringif (len 20) {result q} else {let startstr q.substring(0, 10)let endstr q.substring(len - 10, len)result startstr len endstr}return result
}/*** 发送LLM翻译请求* param q 待翻译的文本* param recievedata 接收数据的回调函数* returns Promisenumber 请求ID*/
export function postllm(q: string, recievedata: (data: string) void) {let req http.createHttp()let param genparm(q)let opt: http.HttpRequestOptions {method: http.RequestMethod.POST,header: {Content-Type: application/x-www-form-urlencoded},extraData: i${param.i}q${param.q}fromautotoautoappKey${param.appKey}curtime${param.curtime}signTypev3salt${param.salt}sign${param.sign}}let strs: string[] []req.on(dataReceive, (data) {// 处理接收到的数据let result buffer.from(data).toString()console.log(gxxt result , result)if (!result.endsWith(\n)) {strs.push(result.substring(result.lastIndexOf(\n) 1))result result.substring(0, result.lastIndexOf(\n) 1)} else {if (strs.length 0) {result strs.join() resultstrs []}}console.log(gxxt newresult , result)recievedata(result)})return new Promisenumber((resolve, reject) {req.requestInStream(BASEURL, opt).then((num) {resolve(num)}).catch((e: Error) {reject(e.message)})})
}
流式返回数据的处理
目前鸿蒙还没有支持解析SSE数据的工具需要开发者自己封装其实就是对于字符串的处理。鉴于SSE的结构可以按照\n\n双换行来切割获取不同的事件类型然后只处理event:message事件即可。
/*** 解析服务器返回的SSE(Server-Sent Events)数据* param data 服务器返回的原始数据字符串* returns 解析后的翻译结果数组如果发生错误则返回undefined*/export function getResult(data: string) {let ret: string[] []// 检查是否包含错误信息if (data.lastIndexOf(event:error) ! -1) {return}// 按事件分割数据let events data.split(\n\n)events.forEach((item: string) {// 处理消息事件if (item.indexOf(event:message) ! -1) {let data1 item.split(event:message\n)data1.forEach((itemn: string) {// 提取JSON数据并解析if (itemn.length 1 itemn.indexOf({) ! -1) {let jsondata itemn.substring(itemn.indexOf({), itemn.indexOf(}) 1)let res JSON.parse(jsondata) as resdataret.push(res.transIncre)}})}})return ret}
主界面代码
这是一个名为星星翻译的鸿蒙应用主界面界面设计简洁实用主要分为三个部分
顶部是标题栏显示星星翻译的应用名称采用简洁的白色背景设计。
中间是核心的翻译区域采用上下分栏布局 上方是文本输入区用户可以在这里输入需要翻译的内容 下方是翻译结果显示区实时显示翻译结果 两个区域之间有一个翻译按钮点击即可执行翻译操作 翻译结果支持长按选择文本可以进行复制、全选等操作
底部是功能操作栏提供两个主要功能按钮 复制按钮可以将翻译结果一键复制到剪贴板 生词本按钮可以将当前的翻译内容保存到生词本中方便后续复习
/*** 星星翻译应用主页面* 提供文本翻译、复制结果、生词本记录等功能*/
import { postllm } from ../utils/HttpUtils;
import { getResult, gettime } from ../utils/StringUtils;
import { contentview } from ../views/contentview;
import { footerview } from ../views/footerview;
import { headerview } from ../views/headerview;
import { pasteboard } from kit.BasicServicesKit;
import { promptAction } from kit.ArkUI;
import { saveshengci } from ../utils/Dbutils;
import { common } from kit.AbilityKit;/*** 翻译应用主页面组件*/
Entry
Component
struct Index {// 源文本和翻译结果状态State Watch(clear) sourcetext: string // 源文本监听变化State targettext: string // 翻译结果private context: common.UIAbilityContext getContext(this) as common.UIAbilityContext // 应用上下文/*** 源文本清空时的监听函数* 当源文本为空时清空翻译结果*/clear() {if (this.sourcetext ) {this.targettext }}/*** 翻译结果回调函数* param data 翻译返回的数据* description 处理翻译结果并更新到目标文本*/cb(data: string) {getResult(data)?.forEach((item) {this.targettext item})}/*** 执行翻译操作* description * 1. 检查源文本是否为空* 2. 清空之前的翻译结果* 3. 调用翻译API* 4. 处理可能的错误*/to: () void () {if (this.sourcetext) {this.targettext postllm(this.sourcetext, this.cb.bind(this)).catch((e: string) {console.error(gxxt , e)})} else {AlertDialog.show({title: 提示, message: 请输入要翻译的内容, confirm: {value: 确定, action: () {}}})}}/*** 复制翻译结果到剪贴板* description * 1. 检查是否有翻译结果* 2. 创建剪贴板数据* 3. 设置到系统剪贴板* 4. 显示操作结果提示*/cpck: () void () {if (this.targettext ! ) {let pasttext pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.targettext)let jianqieban pasteboard.getSystemPasteboard()jianqieban.setDataSync(pasttext)promptAction.showToast({ message: 已经复制到剪切板 })} else {promptAction.showToast({ message: 没有翻译结果无法复制到剪切板 })}}/*** 保存到生词本* description * 1. 记录翻译内容* 2. 记录原文* 3. 记录保存时间*/tobeiwanglu: () void () {saveshengci({content: this.targettext,trans: this.sourcetext,time: gettime()}, this.context)}/*** 构建UI界面* description * 1. 顶部标题栏* 2. 中间内容区域源文本和翻译结果* 3. 底部操作栏复制和生词本功能*/build() {Column() {headerview({ text: 星星翻译, isleft: false })contentview({ sourcetext: this.sourcetext, targettext: this.targettext, transopt: this.to })footerview({ clipck: this.cpck, tobeiwanglu: this.tobeiwanglu })}.width(100%).height(100%)}
}子组件contentview核心代码
/*** contentview.ets* 翻译内容视图组件* 提供文本输入、翻译结果显示、文本选择、复制、生词本等功能*/import { promptAction } from kit.ArkUI
import { pasteboard } from kit.BasicServicesKit
import { saveshengci } from ../utils/Dbutils
import { posttxt } from ../utils/HttpUtils
import { common } from kit.AbilityKit
import { gettime } from ../utils/StringUtils/***作者gxx*时间2025/5/6 14:51*功能**/
Preview
Component
export struct contentview {// 源文本用于存储用户输入的待翻译文本Link sourcetext: string// 目标文本用于存储翻译结果Link targettext: string// 获取UI上下文private context: common.UIAbilityContext getContext(this) as common.UIAbilityContext// 翻译操作回调函数transopt: () void () {}// 当前选中的文本private selecttext: string // 是否全选状态State isquanxuan: boolean false// 是否显示菜单State ismenushow: boolean truebuild() {Stack() {Column() {// 输入区域Column() {TextArea({ placeholder: 要翻译的文本 }).width(100%).height(100%).backgroundColor(Color.Transparent).onChange((value) {this.sourcetext value.trim()})}.width(100%).height(45%).padding(10).border({ width: 2, color: #eee }).borderRadius({ topLeft: 10, topRight: 10 }).backgroundColor(Color.White)// 翻译结果区域Column() {Scroll() {Text(this.targettext ? 翻译结果 : this.targettext).width(100%).copyOption(CopyOptions.InApp).fontWeight(FontWeight.Bolder).backgroundColor(Color.Transparent).fontColor(this.targettext ? Color.Gray : Color.Black)// 绑定长按菜单.bindSelectionMenu(TextSpanType.TEXT, this.genselectmenu(), TextResponseType.LONG_PRESS, {onDisappear: () {this.ismenushow truethis.isquanxuan false}})// 文本选择变化监听.onTextSelectionChange((start: number, end: number) {this.selecttext this.targettext.substring(start, end)})// 全选状态控制.selection(this.isquanxuan ? 0 : -1, this.isquanxuan ? this.targettext.length : 0)}.scrollable(ScrollDirection.Vertical)}.width(100%).height(45%).padding(10).backgroundColor(#eee).borderRadius({ bottomLeft: 10, bottomRight: 10 })}.width(100%).height(100%).justifyContent(FlexAlign.SpaceBetween)// 翻译按钮SymbolGlyph($r(sys.symbol.reverse_order)).fontSize(35).fontWeight(FontWeight.Bolder).border({ width: 2, radius: 10 }).padding(5).stateStyles({normal: {.backgroundColor(Color.White)},pressed: {.backgroundColor(Color.Gray)}}).onClick(() {this.transopt()})}.width(100%).height(80%).padding(10)}/*** 生成文本选择菜单* 包含复制、全选、添加到生词本等功能*/Buildergenselectmenu() {Row({ space: 15 }) {// 复制按钮Text(复制).fontSize(12).onClick(() {let pasttext pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.selecttext)let jianqieban pasteboard.getSystemPasteboard()jianqieban.setDataSync(pasttext)promptAction.showToast({ message: 已经复制到剪切板 })this.ismenushow false})// 全选按钮仅在非全选状态显示if (!this.isquanxuan) {Text(全选).fontSize(12).onClick(() {this.isquanxuan true})}// 生词本按钮Text(生词本).fontSize(12).onClick(() {this.ismenushow falseif (this.isquanxuan) {// 全选状态下保存整个翻译结果saveshengci({content: this.targettext,trans: this.sourcetext,time: gettime()}, this.context)} else {// 选中状态下翻译选中文本并保存posttxt(this.selecttext).then((value) {let target value.query// 拼接好的翻译结果let source value.translation.join(,)saveshengci({content: target,trans: source,time: gettime()}, this.context)}).catch((e: Error) {console.error(gxxt 文本翻译结果 , e.message)})}})}.backgroundColor(Color.White).padding(10).borderRadius(20).border({ width: 1 }).visibility(this.ismenushow ? Visibility.Visible : Visibility.None)}
}生词本页面代码
这是一个生词本界面采用简洁现代的设计风格。界面顶部是标题栏显示生词本标题左侧配有返回按钮方便用户返回上一页面。
主体部分是一个可滚动的生词列表每个生词条目以卡片形式展示包含两个主要信息上方显示生词内容采用较大字号和粗体样式下方显示对应的翻译内容使用灰色字体。每个条目右侧都有一个箭头图标提示用户可以点击查看详情。
用户可以通过左滑生词条目来显示删除按钮点击删除按钮会弹出确认对话框防止误操作。点击生词条目会弹出一个详情对话框以更大的字体展示完整的生词内容和翻译并显示保存时间。如果内容较长对话框支持滚动查看。
/*** shengciben.ets* 生词本页面组件* 提供生词列表展示、详情查看、删除等功能*/import { shengci } from ../model/shengci;
import { delbyid, querylimit } from ../utils/Dbutils;
import { common } from kit.AbilityKit;
import { headerview } from ../views/headerview;
import { ComponentContent, router } from kit.ArkUI;/*** 生成生词详情对话框* param p 对话框参数包含生词信息、删除回调、滚动高度等*/
Builder
function gendialog(p: param) {Stack() {// 关闭按钮SymbolGlyph($r(sys.symbol.xmark)).fontSize(20).onClick(() {p.delck()}).zIndex(2)Column({ space: 10 }) {// 标题Text(生词详情).fontSize(25).fontWeight(FontWeight.Bolder)Divider().color(Color.Grey).margin({ top: 10, bottom: 10 })// 内容区域Scroll() {Column({ space: 10 }) {// 生词内容Text(p.item.content).fontSize(18).fontWeight(FontWeight.Bold)// 翻译内容Text(p.item.trans).fontSize(14).fontColor(Color.Gray)}.alignItems(HorizontalAlign.Start)}.height(p.scrollheight)Divider().color(Color.Grey).margin({ top: 10, bottom: 10 })// 时间信息Text(时间:).fontSize(18).fontWeight(FontWeight.Bold)Text(p.item.time).fontSize(14).fontColor(Color.Gray)}.width(100%).alignItems(HorizontalAlign.Start)}.width(80%).borderRadius(10).alignContent(Alignment.TopEnd).backgroundColor(Color.White).border({ width: 1 }).padding(10)
}/*** 对话框参数接口*/
interface param {item: shengci, // 生词信息delck: () void, // 删除回调函数scrollheight: Length, // 滚动区域高度
}/*** 生词本页面组件*/
Entry
Component
struct Shengciben {State message: string Hello World;State datas: shengci[] [] // 生词列表数据private builder: ComponentContentparam | null nullprivate context: common.UIAbilityContext getContext(this) as common.UIAbilityContext/*** 组件即将出现时加载数据*/aboutToAppear(): void {querylimit(this.context, 1).then((value) {this.datas value}).catch((e: Error) {console.error(gxxt 查询生词本错误: , e.message)})}build() {Column() {// 顶部标题栏headerview({text: 生词本, isright: false, leftck: () {router.back()}})// 生词列表List({ space: 5 }) {ForEach(this.datas, (item: shengci, index: number) {ListItem() {this.genlistitem(item)}.swipeAction({ end: this.genitemend(item.id, index) }) // 左滑显示删除按钮.onClick(() {// 点击显示详情弹窗let p: param {item: item,delck: () {this.getUIContext().getPromptAction().closeCustomDialog(this.builder)},scrollheight: item.content.length item.trans.length 150 ? 300 : auto}this.builder new ComponentContent(this.getUIContext(), wrapBuilder[param](gendialog), p)this.getUIContext().getPromptAction().openCustomDialog(this.builder, {alignment: DialogAlignment.Center})})})}.margin({ top: 5 })}.width(100%).height(100%)}/*** 生成列表项右滑删除按钮* param id 生词ID* param index 列表索引*/Buildergenitemend(id: number, index: number) {Row() {SymbolGlyph($r(sys.symbol.trash_fill)).fontSize(30).fontColor([Color.Red])}.padding({left: 10,top: 5,bottom: 5,right: 10}).border({ width: 1, radius: 10 }).margin(5).onClick(() {// 删除确认对话框AlertDialog.show({title: 提示,message: 确定要删除吗?,primaryButton: {value: 确定, action: () {delbyid(id, this.context).then(() {this.datas.splice(index, 1)})}},secondaryButton: {value: 取消, action: () {}}})})}/*** 生成列表项内容* param item 生词信息*/Buildergenlistitem(item: shengci) {Row() {Column({ space: 5 }) {// 生词内容Text(item.content).fontSize(20).fontWeight(FontWeight.Bolder).width(60%).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1)// 翻译内容Text(item.trans).fontSize(14).fontColor(Color.Gray).width(60%).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1)}Blank()// 右箭头图标SymbolGlyph($r(sys.symbol.chevron_right)).fontSize(20)}.width(100%).padding({left: 5,top: 10,bottom: 10,right: 5}).border({ width: 1, radius: 5 })}
}完整项目代码
https://download.csdn.net/download/gao_xin_xing/90892395