在HarmonyOS中調(diào)用百度翻譯API
想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):
介紹
通過(guò)http請(qǐng)求和HarmonyOS自帶的加密框架,可以為移動(dòng)應(yīng)用實(shí)現(xiàn)調(diào)用百度翻譯API的功能。
完整示例
開(kāi)發(fā)環(huán)境要求
● DevEco Studio版本:DevEco Studio 3.1 Release
● HarmonyOS SDK版本:API version 9
工程要求
● API9
● Stage模型
正文
代碼結(jié)構(gòu)
main
├─ module.json5
├─ resources
│ ├─ zh_CN
│ ├─ rawfile //圖片資源目錄
│ │ ├─ image1.png
│ │ └─ image2.png
│ └─ base
└─ ets
├─ XL_Modules
│ └─ XL_Translation_Tool.ts //翻譯工具模塊
├─ pages
│ └─ Index.ets //主頁(yè)面
└─ entryability
└─ EntryAbility.ts
添加權(quán)限
在本案例中,我們是通過(guò)http數(shù)據(jù)請(qǐng)求的方式調(diào)用百度翻譯API的,這意味著應(yīng)用必須擁有聯(lián)網(wǎng)權(quán)限。所以我們需要在module.json5中申請(qǐng)“name”為"ohos.permission.INTERNET"的權(quán)限。
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
],
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ts",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
]
}
}
獲取百度翻譯開(kāi)放平臺(tái)的App Id和密鑰
開(kāi)發(fā)者能通過(guò)http數(shù)據(jù)請(qǐng)求調(diào)用百度翻譯API的前提是提供一些必要的認(rèn)證信息,即開(kāi)發(fā)者在百度翻譯開(kāi)放平臺(tái)的App Id與密鑰。
首先,進(jìn)入百度翻譯開(kāi)放平臺(tái)的網(wǎng)址,網(wǎng)頁(yè)鏈接為http://api.fanyi.baidu.com。隨后,注冊(cè)個(gè)人用戶(hù),并在注冊(cè)完成之后打開(kāi)開(kāi)發(fā)者信息的選項(xiàng),即可查看自己的App Id與密鑰。
最后,我們還需要在平臺(tái)中開(kāi)通通用文本翻譯API(這個(gè)是免費(fèi)申請(qǐng)的),成功開(kāi)通后可以進(jìn)入下一步。
翻譯工具模塊
在本案例中,調(diào)用百度翻譯API的關(guān)鍵代碼被封裝在翻譯工具類(lèi)中,而翻譯工具類(lèi)( XL_Translation_Tool)則被集成在文件 XL_Translation_Tool.ts中。在XL_Translation_Tool.ts中,首先需要導(dǎo)入cryptoFramework與http,前者用于MD5數(shù)據(jù)加密,后者用于http數(shù)據(jù)請(qǐng)求。接著編寫(xiě)兩個(gè)輔助函數(shù),分別為Generate_number_Sequence_10與Transform_10_To_16,前者用于生成十位隨機(jī)數(shù),后者則用于將10進(jìn)制整數(shù)序列轉(zhuǎn)換為16進(jìn)制整數(shù)序列。隨后,將MD5加密的代碼封裝在異步函數(shù)Generate_Sign內(nèi),并創(chuàng)建類(lèi)XL_Translation_Tool。在類(lèi)XL_Translation_Tool中,provideDeveloperInfo方法用于獲取開(kāi)發(fā)者在百度翻譯開(kāi)放平臺(tái)的App Id與密鑰,translate方法則是基于上述App Id與密鑰,以及待翻譯文本,來(lái)調(diào)用百度翻譯API。調(diào)用API需要的URL格式如下:
最后,導(dǎo)出一個(gè)XL_Translation_Tool實(shí)例。
//導(dǎo)入加密框架
import cryptoFramework from '@ohos.security.cryptoFramework';
//導(dǎo)入http模塊
import http from '@ohos.net.http';
/*
* 函數(shù)名: Generate_number_Sequence_10
* 描述: 隨機(jī)生成攜帶十個(gè)元素的數(shù)字序列(此序列的數(shù)據(jù)類(lèi)型為字符串)
*/
function Generate_number_Sequence_10():string{
var ret:string = ''
for(var i = 0; i < 10; ++i){
ret += Math.floor(Math.random()*9.999)
}
console.info('------[Random] '+ret)
return ret
}
/*
* 函數(shù)名: Transform_10_To_16
* 描述: 用于將10進(jìn)制整數(shù)序列(參數(shù)的數(shù)據(jù)類(lèi)型為Uint8Array)轉(zhuǎn)換為16進(jìn)制整數(shù)序列(輸出的數(shù)據(jù)類(lèi)型為字符串)的輔助函數(shù)
*/
function Transform_10_To_16(Sign_10:Uint8Array):string{
let Array_16 = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
let Sign_16:string = ''
for(var item of Sign_10) Sign_16 += Array_16[(item-item%16)/16]+Array_16[item%16]
console.info('------[MD5 16]: '+Sign_16.toString())
return Sign_16
}
/*
* 函數(shù)名: Generate_Sign
* 描述: 通過(guò)MD5模塊進(jìn)行信息加密, 以生成簽名
*/
async function Generate_Sign(query:string,appId:string, key:string, sequence_10:string){
//預(yù)定義輸出
let output = {
'ifComplete':false,
'Sign_16':null,
}
//創(chuàng)建md模塊
let MD5 = cryptoFramework.createMd('MD5')
//MD5加密的輸入值
let sign = appId + query + sequence_10 + key
//數(shù)據(jù)轉(zhuǎn)換
var arr = []
for(var i = 0 ; i < sign.length ; i++)arr.push(sign.charCodeAt(i))
var dataBlog = {
data:new Uint8Array(arr)
}
console.info('------[MD5 10] dataBlog: '+dataBlog.data.toString())
//開(kāi)始MD5加密
await MD5.update(dataBlog)
var SecretSign
await MD5.digest().then((mdOutput) => {
SecretSign = mdOutput.data
console.info("------[MD5 10] MD result: " + SecretSign.toString());
output.ifComplete = true
output.Sign_16 = SecretSign
}).catch((err) => console.error("------[MD5 10] err: " + err.code))
output.Sign_16 = Transform_10_To_16(SecretSign)
//輸出加密結(jié)果
return output
}
/*
* 枚舉名: TranslationForm
* 描述: 用于確定翻譯流程的始末
*/
export enum TranslationForm{
From_En_To_Zh = 0,
From_Zh_To_En = 1
}
/*
* 數(shù)據(jù)類(lèi)型名: TranslationResult
* 描述: 類(lèi)XL_Translation_Tool的translate方法返回的數(shù)據(jù)類(lèi)型
*/
declare type TranslationResult = {
ifComplete:boolean
result:string
description:string
}
class XL_Translation_Tool{
private TAG:string = '------[XL_Translation_Tool] '
private Baidu_Translation_API_URL_Prefix:string = 'https://fanyi-api.baidu.com/api/trans/vip/translate'
private Baidu_APPID:string = ''
private Baidu_Key:string = ''
private If_Provide_Developer_Info:boolean = false
/*
* 方法名: provideDeveloperInfo
* 描述: 為XL_Translation_Tool提供百度翻譯平臺(tái)的開(kāi)發(fā)者信息, 這是百度翻譯API得以被調(diào)用的前提
*/
public provideDeveloperInfo(appId:string, key:string){
this.Baidu_APPID = appId
this.Baidu_Key = key
this.If_Provide_Developer_Info = true
}
/*
* 方法名: translate
* 描述: 將輸入的字符串信息, 通過(guò)百調(diào)用度翻譯API進(jìn)行翻譯并返回
*/
public async translate(message:string, form:TranslationForm):Promise<TranslationResult>{
//預(yù)定義返回值
var ret:TranslationResult = {
ifComplete:false,
result:null,
description:''
}
//判斷appId和key是否被提供
if(!this.If_Provide_Developer_Info){
ret.description = '請(qǐng)為翻譯工具提供百度智能翻譯平臺(tái)的開(kāi)發(fā)者信息'
console.info(this.TAG+'Please provide the appId and key')
return ret
}
//通過(guò)http模塊實(shí)例化HttpRequest
var httpRequest = http.createHttp()
let from:string
let to:string
let query:string = message+''
if(form == TranslationForm.From_En_To_Zh){
from = 'en'
to = 'zh'
}else if(form == TranslationForm.From_Zh_To_En){
from = 'zh'
to = 'en'
}
//生成10位隨機(jī)整數(shù)序列
const RANDOM_SEQUENCE:string = Generate_number_Sequence_10()
var sign:string
//生成簽名
let msg = await Generate_Sign(query,this.Baidu_APPID,this.Baidu_Key,RANDOM_SEQUENCE)
if(msg.ifComplete){
sign = msg.Sign_16
}else {
ret.description = '加密過(guò)程出錯(cuò), 請(qǐng)檢查相關(guān)項(xiàng)'
return ret
}
//拼接URL
let url = this.Baidu_Translation_API_URL_Prefix
+'?q=' + encodeURI(query)
+'&from=' +from
+'&to='+to
+'&appid='+this.Baidu_APPID
+'&salt='+RANDOM_SEQUENCE
+'&sign='+sign
console.info(this.TAG+'url: '+url)
//調(diào)用request方法
await httpRequest.request(url,{
method:http.RequestMethod.POST,
header:{
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then((data)=>{
console.info(this.TAG+'Succeed in connecting to Internet')
// @ts-ignore
let trans_result = JSON.parse(data.result).trans_result
console.info(this.TAG+'Result:' + data.result);
console.info(this.TAG+'code:' + JSON.stringify(data.responseCode));
console.info(this.TAG+'Translate output:'+trans_result[0].dst)
ret.ifComplete = true
ret.description = '翻譯成功'
ret.result = trans_result[0].dst
}).catch(err => console.error(this.TAG+'err:'+JSON.stringify(err)))
//銷(xiāo)毀HttpRequest實(shí)例
httpRequest.destroy()
//返回翻譯結(jié)果
return ret
}
}
//導(dǎo)出本模塊
export default new XL_Translation_Tool()
主頁(yè)面
在頁(yè)面Index.ets中,我們需要設(shè)計(jì)一個(gè)自定義彈窗(名為dialog1)。該彈窗有一個(gè)用@Link裝飾器修飾的變量If_Provide_Developer_Info,表示此自定義彈窗組件與其父組件支持雙向數(shù)據(jù)傳遞。彈窗中有兩個(gè)文本輸入組件,分別用于輸入開(kāi)發(fā)者的App Id與密鑰,而內(nèi)容為"確定"的文本組件則用于調(diào)用XL_Translation_Tool實(shí)例的provideDeveloperInfo方法,將開(kāi)發(fā)者認(rèn)證信息載入XL_Translation_Tool實(shí)例中。
//導(dǎo)入自定義的翻譯工具
import XL_Translation_Tool, { TranslationForm } from 'ets/XL_Modules/XL_Translation_Tool'
//主頁(yè)面UI
@Entry
@Component
struct Index{
......
}
//自定義彈窗
@CustomDialog
struct dialog1{
dialogController:CustomDialogController
@Link If_Provide_Developer_Info:boolean
@State private Baidu_AppId:string = ''
@State private Baidu_Key:string = ''
build(){
Column(){
Text('APP Id: ')
.fontSize(20)
.fontWeight(600)
.fontColor(Color.Gray)
.margin({
top:20,
bottom:4
})
.width('86%')
TextInput()
.type(InputType.Number)
.width('95%')
.height(40)
.backgroundColor('#eeeeee')
.onChange((value:string) => this.Baidu_AppId = value)
Text('密鑰: ')
.fontSize(20)
.fontWeight(600)
.fontColor(Color.Gray)
.margin({
top:20,
bottom:4
})
.width('86%')
TextInput({
text:this.Baidu_Key
})
.type(InputType.Password)
.width('95%')
.height(40)
.backgroundColor('#eeeeee')
.onChange((value:string) => this.Baidu_Key = value)
Row(){
Text('確認(rèn)')
.fontSize(24)
.fontWeight(700)
.fontColor(Color.Blue)
.onClick(() => {
this.dialogController.close()
this.If_Provide_Developer_Info = true
XL_Translation_Tool.provideDeveloperInfo(this.Baidu_AppId, this.Baidu_Key)
})
Text('取消')
.fontSize(24)
.fontWeight(700)
.fontColor(Color.Red)
.onClick(() => {
this.dialogController.close()
})
}
.width('100%')
.margin({
top:30
})
.justifyContent(FlexAlign.SpaceEvenly)
}
.height(260)
.width('92%')
}
}
最后,編寫(xiě)頁(yè)面入口Index。dialogController表示自定義彈窗組件dialog1的控制器。
//導(dǎo)入自定義的翻譯工具
import XL_Translation_Tool, { TranslationForm } from 'ets/XL_Modules/XL_Translation_Tool'
//主頁(yè)面UI
@Entry
@Component
struct Index {
@State private If_Provide_Developer_Info:boolean = false
@State private translation_mode:number = TranslationForm.From_En_To_Zh
@State private translation_content:string = ''
@State private translation_result:string = ''
private dialogController = new CustomDialogController({
builder:dialog1({
If_Provide_Developer_Info:$If_Provide_Developer_Info
})
})
private async Begin_Translate(){
let result = await XL_Translation_Tool.translate(this.translation_content, this.translation_mode)
this.translation_result = result.result
}
build() {
Column() {
Text('百度翻譯API')
.fontSize(36)
.fontColor(Color.Pink)
.fontWeight(800)
.margin({
top:20
})
Row(){
Row() {
Text('英譯中')
.fontSize(20)
.fontColor('#8099ff')
.fontWeight(800)
Image($rawfile('image2.png'))
.height(30)
.width(30)
}
Text('提供密鑰')
.fontSize(20)
.fontColor('#ff6666')
.fontWeight(800)
.onClick(() => this.dialogController.open())
}
.margin({
top:20
})
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
Text('輸入')
.fontSize(20)
.fontWeight(600)
.fontColor(Color.Gray)
.margin({
top:30,
bottom:10
})
.width('86%')
TextArea()
.width('90%')
.height(120)
.border({
width:3,
})
.onChange((value:string) => this.translation_content = value)
.fontSize(21)
Text() {
Span('結(jié)果')
}
.fontSize(20)
.fontWeight(600)
.fontColor(Color.Gray)
.margin({
top: 30,
bottom: 10
})
.width('86%')
TextArea({
text:this.translation_result
})
.width('90%')
.height(150)
.border({
width:3,
})
.fontSize(21)
Row(){
Image($rawfile('image1.png'))
.height(40)
.width(40)
Text('翻譯')
.fontWeight(700)
.fontSize(24)
.fontColor(Color.White)
.margin({
left:20
})
}
.width('93%')
.height(50)
.backgroundColor('#8099ff')
.borderRadius(30)
.justifyContent(FlexAlign.Center)
.margin({
top:50
})
.onClick(() =>{
if(!this.If_Provide_Developer_Info){
this.dialogController.open()
return
}
console.info('------[ArkUI] '+'--'+this.translation_content+'--')
this.Begin_Translate()
})
}
.width('100%')
}
}
//自定義彈窗
@CustomDialog
......
這樣之后,調(diào)用百度API的案例便編寫(xiě)完成了,使用模擬器或真機(jī)可以查看運(yùn)行效果。受一些奇怪的bug的影響,本案例中的翻譯功能只支持英譯中。
總結(jié)
通過(guò)本次案例,相信你已經(jīng)學(xué)會(huì)如何在HarmonyOS中調(diào)用百度翻譯API。