对腾讯AI机器翻译接口的调用

在上一次文字识别代码的基础上,进行了改进,代码结构优化了一点(我认为···)

开完账号,加好应用能力,安装好依赖包,按代码最后的提示改成http(如果要在其他页面中访问,就需要设置 https),就可以启动服务了。浏览器输入 http://localhost:8203/trans/to/zh?text=hello 进行测试我测试用的前端代码也已给出(本来的目的是要实现一个小插件)其中Param 及其方法可以用于腾讯 AI 其他 Api 调用,例:
package main

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/unrolled/secure"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

/*
可以直接拿去使用
把自己开通好的 id 和 key 换了就可以,需要在应用中添加对应能力
返回的数据是没有进行json解析,添加自定义的 status 之后直接返回到前端解析
 */

var (
	// 自己去官网开账号,开应用
	appId  = "*"
	appKey = "*"
)

// 为了排序
type Param struct {
	key, value string
}

func SortParams(p []Param) {
	// 升序
	sort.Slice(p, func(i, j int) bool {
		if p[i].key < p[j].key {
			return true
		}
		return false
	})
}

func ParamsToString(p []Param) string {
	s := ""
	for _, v := range p {
		if v.value == "" {
			continue
		}
		// value 需要进行 url 编码
		s += v.key + "=" + url.QueryEscape(v.value) + "&"
	}
	return s[:len(s)-1]
}

func PostToTansApi(text, target string) (s []byte, ok bool) {
	client := http.Client{}
	params := make([]Param, 0, 10)
	params = append(params, Param{"app_id", appId})
	params = append(params, Param{"source", "auto"})
	params = append(params, Param{"target", target})
	params = append(params, Param{"text", text})

	// 随便啥都行,非空且长度小于32
	params = append(params, Param{"nonce_str", "asbfiuasbhjbcuicg"})
	params = append(params, Param{"time_stamp", fmt.Sprintf("%d", time.Now().Unix())})

	// 取得动态加密标志,获取body
	params = getSign(params)
	form := ParamsToString(params)
	body := bytes.NewBufferString(form)

	request, err := http.NewRequest("POST", "https://api.ai.qq.com/fcgi-bin/nlp/nlp_texttranslate", body)
	if err != nil {
		log.Println(err)
		return
	}

	// 重要的!!!
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	response, err := client.Do(request)
	if err != nil {
		log.Println(err)
		return
	}

	defer response.Body.Close()
	s, err = ioutil.ReadAll(response.Body)
	if err != nil {
		log.Println(err)
		return
	}

	return s, true
}

func getSign(p []Param) []Param {
	s := strings.Builder{}

	SortParams(p)
	s.WriteString(ParamsToString(p))

	s.WriteString("&app_key=" + appKey)

	// MD5
	hash := md5.New()
	hash.Write([]byte(s.String()))
	encodeToString := strings.ToUpper(hex.EncodeToString(hash.Sum(nil)))

	p = append(p, Param{"sign", encodeToString})
	return p
}

func TansToZHHandle(c *gin.Context) {
	text, ok := c.GetQuery("text")
	if !ok || text == "" {
		c.JSON(http.StatusOK, map[string]string{"status": "no", "msg": "参数呢"})
		return
	}

	s, ok := PostToTansApi(text, "zh")
	if !ok {
		c.JSON(http.StatusOK, map[string]string{"status": "no", "msg": "不可能出现的错误,多次失败请联系管理员"})
		return
	}
	c.JSON(http.StatusOK, map[string]string{"status": "ok", "msg": string(s)})
}

func TansToENHandle(c *gin.Context) {
	text, ok := c.GetQuery("text")
	if !ok || text == "" {
		c.JSON(http.StatusOK, map[string]string{"status": "no", "msg": "参数呢"})
		return
	}

	s, ok := PostToTansApi(text, "en")
	if !ok {
		c.JSON(http.StatusOK, map[string]string{"status": "no", "msg": "不可能出现的错误,多次失败请联系管理员"})
		return
	}
	c.JSON(http.StatusOK, map[string]string{"status": "ok", "msg": string(s)})
}

func AllowControl(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", "*")
}

func TlsHandler(c *gin.Context) {
	secureMiddleware := secure.New(secure.Options{
		SSLRedirect: true,
		SSLHost:     ":8203",
	})
	err := secureMiddleware.Process(c.Writer, c.Request)

	if err != nil {
		log.Println(err)
		return
	}
}

func main() {
	gin.SetMode(gin.ReleaseMode)
	r := gin.Default()
	r.Use(TlsHandler)
	r.Use(AllowControl)
	{
		r.GET("trans/to/zh", TansToZHHandle)
		r.GET("trans/to/en", TansToENHandle)
	}

	err := r.RunTLS(":8203", "*.pem", "*.key")
	if err != nil {
		panic(err)
	}
}

/*
本地测试注释掉
r.Use(TlsHandler())
改
err := r.RunTLS(":8203", "*.pem", "*.key")
为
err := r.Run(":8203")
*/

前端示例

  • 这里设置了按 ` 显示/隐藏 ESC下面的
  • 仅提供参考,如果我的实例不能访问,替换为本地开启的服务 http://localhost:8203/* 或其他。。。
  • 如果需要,样式大小自行修改
  • 嗯。。希望能有点用吧
<!doctype html>
<html lang="ch">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <style>
        #trans-container {
            top: 200px;
            left: 200px;
            margin: 0 auto;
            border-radius: 8px;
            box-shadow: 0 0 0 -20px rgba(0, 0, 0, .2), 0 24px 38px 3px rgba(0, 0, 0, .14), 0 9px 46px 8px rgba(0, 0, 0, .12);
            position: fixed;
            background: rgba(255, 255, 255, 0.95);
            width: 150px;
            max-height: 100px;
            overflow: auto;
            opacity: 40%;
            z-index: 999999999;
        }

        #trans-container div p,
        #trans-container div input {
            font-weight: 100;
            padding: 0;
            margin: 0;
        }

        #trans-container div input{
            border-width: 0;
        }

        #trans-container div input:focus{
            outline: none;
        }

        #trans-container::-webkit-scrollbar {
            display: none;
        }

        [v-cloak] {
            display: none
        }
    </style>
</head>
<body>
<div id="trans-container" @mousedown="move" v-cloak v-show="show">
    <div style="text-align: center;">
        <input type="text" v-model="text" placeholder="翻译,在此输入" @keypress.enter="send"
               style="width: 100%;text-align: center;">
    </div>
    <div>
        <p v-text="ret"></p>
    </div>
</div>
</body>
<script>
    let pattern = new RegExp("[\u4E00-\u9FA5]+");

    function getTrans(text) {
        if (pattern.test(text)) {
            if (window.top.transapp)
                getEN(text)
        } else {
            if (window.top.transapp)
                getZH(text)
        }
    }

    function getZH(text) {
        axios.get('https:ligaofeng.top:8203/trans/to/zh?text=' + text)
            .then(function (response) {
                let data = response.data;
                if (data["status"] == "ok") {
                    data = data.msg;
                    data = JSON.parse(data);
                    if (data.ret!=0){
                        console.log(data.msg)
                        window.top.transapp.ret = "翻译失败";
                        return
                    }
                    window.top.transapp.ret = data.data.target_text;
                } else {
                    console.log(data.msg)
                    window.top.transapp.ret = "翻译失败";
                }
            })
            .catch(function (error) {
                console.log(error);
                window.top.transapp.ret = "翻译失败";
            });
    }

    function getEN(text) {
        axios.get('https:ligaofeng.top:8203/trans/to/en?text=' + text)
            .then(function (response) {
                let data = response.data;
                if (data["status"] == "ok") {
                    data = data.msg;
                    data = JSON.parse(data);
                    if (data.ret!=0){
                        console.log(data.msg)
                        window.top.transapp.ret = "翻译失败";
                        return
                    }
                    window.top.transapp.ret = data.data.target_text;
                } else {
                    console.log(data.msg)
                    window.top.transapp.ret = "翻译失败";
                }
            })
            .catch(function (error) {
                console.log(error);
                window.top.transapp.ret = "翻译失败";
            });
    }

    window.top.addEventListener("keypress", function (ev) {
        switch (ev.key) {
            case "`":
                if (!window.top.transapp)
                    window.top.transapp = new Vue({
                        el: "#trans-container",
                        data: {
                            text: "",
                            ret: "",
                            positionX: 0,
                            positionY: 0,
                            show: true
                        },
                        methods: {
                            send() {
                                if (this.text) {
                                    this.ret = "获取中。。。"
                                    getTrans(this.text)
                                    this.text = ""
                                }
                            },
                            move(e) {
                                let odiv = document.getElementById("trans-container");
                                let disX = e.clientX - odiv.offsetLeft;
                                let disY = e.clientY - odiv.offsetTop;
                                document.onmousemove = (e) => {
                                    let left = e.clientX - disX;
                                    let top = e.clientY - disY;
                                    this.positionX = top;
                                    this.positionY = left;
                                    odiv.style.left = left + 'px';
                                    odiv.style.top = top + 'px';
                                };
                                document.onmouseup = () => {
                                    document.onmousemove = null;
                                    document.onmouseup = null;
                                };
                            },
                        }
                    })
                else
                    window.top.transapp.show = !window.top.transapp.show
                break
        }
    })
</script>
</html>