北冥翻译

北冥翻译:灵感来自金庸武侠小说北冥神功,据说可吸他人内力为自己所用,希望北冥翻译能将翻译结果为你所用。支持百度翻译、有道翻译、彩云小译,需要自行配置API才能使用。

简介

关于北冥翻译

北冥翻译是一款uTools聚合翻译插件,目前支持腾讯翻译、百度翻译、有道翻译、彩云小译(后期视情况添加其它翻译),除腾讯翻译外,其它翻译需要自行配置API后才能使用。

灵感来源

北冥翻译:灵感来自金庸武侠小说北冥神功,据说可吸他人内力为自己所用,希望北冥翻译能将翻译结果为你所用。

实现功能

联系我

安装与使用

uTools安装

目前仅支持uTools安装使用,后续可能会推出网页版本。打开uTools插件市场搜索“北冥翻译”即可安装。

获取API密钥

北冥翻译内置腾讯翻译可免费使用,每日限制50次请求。如果您有更大的需求,建议自行配置第三方API使用,因此需要先申请对应API密钥,目前第三方API支持百度、有道、彩云翻译,后续根据实际情况可能会接入更多API.

获取百度翻译API

申请通过后在后台开发者中心获取APP ID密钥

获取有道翻译API

前往有道智云进行申请,然后切换到自然语言服务 - 文本翻译,创建应用并获取应用ID应用秘钥

获取彩云小译API

前往彩云科技开放平台 注册并申请翻译令牌。彩云小译需要人工审核。

配置API

打开右下角设置按钮,根据提示填写第三方API信息。

使用

API配置完毕后即可使用,需要说明的是为了节省API调用次数,文本输入完毕后,需要点击手动翻译或者鼠标点击文本框外才会进行翻译,如下图。

当你复制了一段文本的时候,也可以呼出uTools,然后选择“北冥翻译”或自动帮你翻译内容,如下图:

还可以在uTools偏好设置 - 超级面板 - 快捷关键字中加入“北冥翻译”,这样你可以在网络上复制任意一段文本,然后按下鼠标中键(鼠标中键为uTools超级面板默认快捷键),即可帮你自动翻译。

效果如下图:

提示:如果您需要翻译韩语、日文等内容,源语言选择自动检测即可。

默认翻译设置

点击右下角设置按钮打开“北冥翻译API设置”拉到最底部可以设置“默认翻译”,设置后下次进入“北冥翻译”会优先默认使用此翻译接口。

关于价格

北冥翻译本身不收取任何费用,但是依赖的第三方API是需要收费的,请留意第三方API价格说明,以下为个人整理,仅供参考,具体信息以官方为准。

接口名称 免费额度 价格 申请地址
百度翻译 高级版: 每月前200万字符免费 超出免费额度后,49元/百万字符 百度翻译开放平台
有道翻译 无免费额度,注册送50代金券 48元/百万字符 有道智云.AI开放平台
彩云小译 每月翻译100万字之内都是免费的 如果每月超过100万字,按照 20元 / 100万字 的费率收费。 彩云科技开放平台

隐私说明

您的密钥等私有信息不会被北冥翻译上传到第三方保存,但是会保存在uTools的数据库中,北冥翻译使用JavaScript直接请求翻译API,中间不经过其它服务器,以下是核心js内容:

var app = new Vue({
	el:'#app',
	data:{
		'content':'',
		'results':'',
		'api_name':'tmt',//默认翻译
		'default_api_name':'',
		'is_click':{
			'baidu':false,
			'youdao':false,
			'sogou':false,
			'tmt':true,
			'caiyun':false
		},
		'api_list':{
			'baidu':'百度翻译',
			'youdao':'有道翻译',
			'caiyun':'彩云小译',
			'sogou':'搜狗翻译',
			'tmt':'腾讯翻译'
		},
		from:'auto',//源语言
		to:'',//目标语言
		is_load:false,//是否显示加载按钮,默认不显示
		is_active:'',//是否激活了设置按钮
		baidu:{
			api_url:'https://fanyi-api.baidu.com/api/trans/vip/translate',
			appid:'',
			secret_key:''
		},
		youdao:{
			api_url:'https://openapi.youdao.com/api',
			appid:'',
			secret_key:''
		},
		caiyun:{
			api_url:'https://api.interpreter.caiyunai.com/v1/translator',
			appid:'',
			secret_key:''
		},
		sogou:{

		},
		tmt:{
			api_url:'https://bm.rss.ink/v1/tmt',
			appid:'',
			secret_key:''
		},//错误消息
		err_msg:{
			'content':'',
			'status':false
		},//成功消息
		suc_msg:{
			'content':'',
			'status':false
		},//全局消息提示框
		g_msg:{
			'content':'',
			'status':'',
			'is_type':''
		}
	},
	methods: {
		//百度翻译
		baidu_func:function(){
			console.log('运行百度翻译!');
			this.check_api('baidu');
			var appid = this.baidu.appid;
			var key = this.baidu.secret_key;
			var salt = (new Date).getTime();
			var query = this.content.trim();
			// 多个query可以用\n连接  如 query='apple\norange\nbanana\npear'
			var from = document.getElementById("from").value;
			//
			var to = get_to_lang(this.content);
			var str1 = appid + query + salt + key;
			var sign = MD5(str1);

			const params = new URLSearchParams();
			params.append('q', query);
			params.append('appid', appid);
			params.append('salt', salt);
			params.append('from', from);
			params.append('to', to);
			params.append('sign', sign);


			axios
			.post(this.baidu.api_url,params)
			.then(
				response => (
					deal_baidu_results(response.data)
					
				)
			)
			.catch(function (error) { // 请求失败处理
				console.log(error);
			});
		},
		//有道翻译
		youdao_func:function(){
			this.check_api('youdao');
			var appKey = this.youdao.appid;
			var key = this.youdao.secret_key;//注意:暴露appSecret,有被盗用造成损失的风险
			var salt = (new Date).getTime();
			var curtime = Math.round(new Date().getTime()/1000);
			var query = this.content;
			// 多个query可以用\n连接  如 query='apple\norange\nbanana\npear'
			var from = 'auto';
			var to = get_to_lang(this.content);
			var str1 = appKey + truncate(query) + salt + curtime + key;

			var sign = CryptoJS.SHA256(str1).toString(CryptoJS.enc.Hex);

			const params = new URLSearchParams();
			params.append('q', query);
			params.append('appKey', appKey);
			params.append('salt', salt);
			params.append('from', from);
			params.append('to', to);
			params.append('sign', sign);
			params.append('signType', 'v3');
			params.append('curtime', curtime);

			axios
			.post(this.youdao.api_url,params)
			.then(
				response => (
					deal_youdao_results(response.data)
				)
			)
			.catch(function (error) { // 请求失败处理
				console.log(error);
			});
		},
		sogou_func:function(){
			this.is_load = false;
			console.log('用户切换到搜狗翻译!');
		},//彩云小译调用函数
		caiyun_func:function(){
			this.check_api('caiyun');
			var token = "token " + this.caiyun.secret_key;
			//待翻译文本
			var source = this.content.split("\n");
			//翻译语言
			var trans_type = this.from + '2' + get_to_lang(source);
			console.log(trans_type);
			var request_id = "demo";
			var detect = true;
			//this.is_load = false;

			//开始请求接口
			axios.post(this.caiyun.api_url,{
				"source":source,
				"trans_type":trans_type,
				"request_id":request_id,
				"detect":detect
			},{
				headers: {
					'Content-Type': 'application/json',
					'x-authorization':token
				}	
			})
			.then(
				response => (
					deal_caiyun_results(response.data)
				)
			)
			.catch(function (error) { // 请求失败处理
				app.is_load = false;
				app.err_msg.content = error;
				this.is_load = false;
				app.err_msg.status = true;
				//定时关闭提示信息
				window.setTimeout(function(){
					app.err_msg.status = false;
				},3000);
				
				return false;
			});
		}
		,//腾讯翻译调用函数
		tmt_func:function(){
			var SourceText = this.content;
			var Source = this.from;
			var Target = get_to_lang(SourceText);
			const params = new URLSearchParams();
			params.append('SourceText', SourceText);
			params.append('Source', Source);
			params.append('Target', Target);

			//开始请求接口
			axios.post(this.tmt.api_url,params)
			.then(
				response => (
					deal_tmt_results(response.data)
				)
			)
			.catch(function (error) { // 请求失败处理
				app.is_load = false;
				app.err_msg.content = error;
				this.is_load = false;
				app.err_msg.status = true;
				//定时关闭提示信息
				window.setTimeout(function(){
					app.err_msg.status = false;
				},3000);
				return false;
			});
		},
		//用户点击切换语言的时候重新触发翻译,第一个
		switch_lang:function(source){

			//判断点击的源语言还是目标语言
			if( source == 'from' ) {
				var from = document.getElementById(source).value;
				this.from = from;
			}
			else if( source == 'to' ) {
				var to = document.getElementById(source).value;
				this.to = to;
			}
			//每次点击语言切换的时候都运行一次翻译接口
			//显示加载按钮
			this.is_load = true;
			this.run_translation();
			
		},
		//设置接口属性
		choose_api:function(api_name){
			switch (api_name) {
				case 'baidu':
					this.api_name = 'baidu';
					console.log(this.api_name);
					//this.baidu_func();
					break;
				case 'youdao':
					this.api_name = 'youdao';
					console.log(this.api_name);
					//this.youdao_func();
					break;
				case 'sogou':
					this.api_name = 'sogou';
					console.log(this.api_name);
					break;
				case 'tmt':
					this.api_name = 'tmt';
					console.log(this.api_name);
					break;
				case 'caiyun':
					this.api_name = 'caiyun';
					console.log(this.api_name);
					break;
				default:
					this.api_name = 'baidu';
					console.log(this.api_name);
					break;
			}
		},
		//运行翻译接口
		run_translation:function(){
			console.log('运行了翻译接口!');
			console.log(this.api_name);
			//如果源内容是空的,不运行接口
			if( this.content == '' ){
				console.log('空内容,不运行接口!');
				//隐藏加载按钮
				this.is_load = false;
				return false;
			}
			switch (this.api_name) {
				case 'baidu':
					//console.log('运行百度接口!');
					//this.test();
					this.baidu_func();
					//console.log('运行百度接口!');
					break;
				case 'youdao':
					this.youdao_func();
					//console.log('运行有道接口!');
					break;
				case 'sogou':
					this.sogou_func();
					global_msg('还在开发中!');
					//console.log('运行有道接口!');
					break;
				case 'tmt':
					this.tmt_func();
					//global_msg('还在开发中!');
					//console.log('运行有道接口!');
					break;
				case 'caiyun':
					this.caiyun_func();
					//global_msg('还在开发中!');
					//console.log('运行有道接口!');
					break;
				default:
					this.baidu_func();
					break;
			}
		},//自动运行翻译接口
		auto_run:function(){
			document.getElementById("from_content").blur();
			this.is_load = true;
			this.run_translation();
			utools.removeSubInput();
		},
		//用户点击切换翻译接口时
		switch_api:function(api_name){
			this.is_load = true;
			//设置接口
			this.choose_api(api_name);
			//激活选项
			//批量将激活按钮设置为false
			for(let key in this.is_click){
				this.is_click[key] = false;
		  	}
			//然后再将点击了的选项激活
			this.is_click[api_name] = true;
			//运行对应接口
			this.run_translation();
		},//用户点击设置按钮
		setting:function(){
			
			
			//点击设置按钮,激活弹窗
			this.is_active = 'is-active';
		},
		//关闭设置按钮
		close_window:function(){
			this.is_active = '';
			
		},//点保存按钮
		save_setting:function(api){
			switch (api) {
				case 'baidu':
					u_set('bm_baidu_appid',deal_str(this.baidu.appid));
					u_set('bm_baidu_secret_key',deal_str(this.baidu.secret_key));
					global_msg('已保存!');
					console.log(this.baidu.appid);
					console.log('百度设置成功!');
					break;
				case 'youdao':
					u_set('bm_youdao_appid',deal_str(this.youdao.appid));
					u_set('bm_youdao_secret_key',deal_str(this.youdao.secret_key));
					global_msg('已保存!');
					console.log('有道设置成功!');
					break;
				case 'caiyun':
					u_set('bm_caiyun_secret_key',deal_str(this.caiyun.secret_key));
					global_msg('已保存!');
					console.log('有道设置成功!');
					break;
				case 'api_name':
					u_set('bm_default_api_name',document.getElementById('api_name').value);
					this.default_api_name = u_get('bm_default_api_name');
					global_msg('默认接口设置成功!');
					console.log('默认接口设置成功!');
					break;
				default:
					break;
			}

			console.log('保存成功');
		},//插件初始化
		init:function(){
			//设置百度API信息
			this.baidu.appid = u_get('bm_baidu_appid');
			this.baidu.secret_key = u_get('bm_baidu_secret_key');
			//设置有道API信息
			this.youdao.appid = u_get('bm_youdao_appid');
			this.youdao.secret_key = u_get('bm_youdao_secret_key');
			//设置彩云翻译API信息
			this.caiyun.secret_key = u_get('bm_caiyun_secret_key');

			//设置默认接口
			if( u_get('bm_default_api_name') != null ){
				var api_name = u_get('bm_default_api_name');
				this.api_name = u_get('bm_default_api_name');
				//批量将激活按钮设置为false
				for(let key in this.is_click){
					this.is_click[key] = false;
				}
				//然后再将点击了的选项激活
				this.is_click[api_name] = true;
				//设置默认接口属性
				this.default_api_name = u_get('bm_default_api_name');
				
			}
			else{
				this.default_api_name = 'tmt';
				
			}

			console.log('插件初始化完成!');
		},//打开url
		open_url:function(url){
			utools.shellOpenExternal(url);
		},//清空结果内容框
		clear_result:function(){
			if( this.content == '' ){
				this.results = '';
			}
		},//判断翻译是否为空
		check_api:function(api_name){
			switch (api_name) {
				case 'baidu':
					if( this.baidu.secret_key == '' ){
						global_msg('请先设置百度API接口!','is-danger');
						return false;
					}
					break;
				case 'youdao':
					if( this.youdao.secret_key == '' ){
						global_msg('请先设置有道API接口!','is-danger');
						return false;
					}
					break;
				case 'caiyun':
					if( this.caiyun.secret_key == '' ){
						global_msg('请先设置彩云小译API接口!','is-danger');
						return false;
					}
					break;
				default:
					break;
			}
		},//清空内容
		clear_content:function(){
			this.content = '';
			this.results = '';
			console.log('为什么不执行?');
		}
	}
	
});

function get_to_lang(content){
	//获取目标语言
	var to = document.getElementById("to").value;
	//如果目标语言不是auto,则直接返回
	if( to != 'auto' ) {
		return to;
	}
	var pattern = new RegExp("[\u4E00-\u9FA5]+");
	var pattern2 = new RegExp("[A-Za-z]+");
	//如果是中文则返回英文
	if(pattern.test(content)){
		return 'en';
	}
	else if(pattern2.test(content)){
		return 'zh';
	}
}

//将百度翻译结果处理拼接
function deal_baidu_results(results){
	console.log('百度翻译处理结果!');
	//关闭加载按钮
	app.is_load = false;
	console.log(results);
	if( results.error_code > 0 ) {
		console.log('百度接口发生了错误!');
		app.err_msg.content = results.error_msg;
		app.err_msg.status = true;
		//定时关闭提示信息
		window.setTimeout(function(){
			app.err_msg.status = false;
		},3000);
		return false;
	}
	else{
		results = results.trans_result;
		var content = '';
		for(i = 0;i < results.length;i++) {
			content += results[i].dst + "\n\n";
		}
		app.results = content;
	}
}
//将有道翻译结果处理拼接
function deal_youdao_results(results){
	console.log('有道翻译结果!');
	//关闭加载按钮
	app.is_load = false;
	//打印结果数据
	console.log(results);
	//当发生错误时
	if( results.errorCode > 0 ) {
		switch (results.errorCode) {
			case '101':
				error_msg = '缺少必填的参数!';
				break;
			case '102':
				error_msg = '不支持的语言类型!';
				break;
			case '103':
				error_msg = '翻译文本过长!';
				break;
			case '104':
				error_msg = '不支持的API类型!';
				break;
			case '105':
				error_msg = '不支持的签名类型!';
				break;
			case '106':
				error_msg = '不支持的响应类型!';
				break;
			case '107':
				error_msg = '不支持的传输加密类型!';
				break;
			case '108':
				error_msg = '应用ID无效!';
				break;
			case '109':
				error_msg = 'batchLog格式不正确!';
				break;
			case '109':
				error_msg = 'batchLog格式不正确!';
				break;
			case '111':
				error_msg = '开发者账号无效!';
				break;
			case '112':
				error_msg = '请求服务无效!';
				break;
			case '113':
				error_msg = 'q不能为空!';
				break;
			default:
				error_msg = '未知错误!';
				break;
		}
		console.log('有道接口发生了错误!');
		app.err_msg.content = error_msg;
		app.err_msg.status = true;
		//定时关闭提示信息
		window.setTimeout(function(){
			app.err_msg.status = false;
		},3000);
		return false;
	}
	else{
		results = results.translation;
		var content = '';
		for(i = 0;i < results.length;i++) {
			content += results[i] + "\n";
		}
		app.results = content;
	}
}

//将彩云翻译结果处理拼接
function deal_caiyun_results(results){
	console.log('彩云小译处理结果!');
	//关闭加载按钮
	app.is_load = false;
	console.log(results);
	if( results.target == '' ) {
		console.log('彩云接口发生了错误!');
		app.err_msg.content = results.error_msg;
		app.err_msg.status = true;
		//定时关闭提示信息
		window.setTimeout(function(){
			app.err_msg.status = false;
		},3000);
		return false;
	}
	else{
		results = results.target;
		var content = '';
		for(i = 0;i < results.length;i++) {
			content += results[i] + "\n";
		}
		app.results = content;
	}
}
//处理腾讯翻译结果
function deal_tmt_results(results){
	console.log('腾讯翻译处理结果!');
	//关闭加载按钮
	app.is_load = false;
	console.log('翻译失败' + results.TargetText);
	//如果翻译失败
	if( results.TargetText == undefined ){
		app.err_msg.content = results.Response.Error.Message;
		app.err_msg.status = true;
		//定时关闭提示信息
		window.setTimeout(function(){
			app.err_msg.status = false;
		},3000);
	}
	else{
		app.results = results.TargetText;
	}
	
}

//有道自身提供的内容处理
function truncate(q){
    var len = q.length;
    if(len<=20) return q;
    return q.substring(0, 10) + len + q.substring(len-10, len);
}

//简化utools官方的键值对
function u_get(key){
	var result = utools.db.get(key);
	if( result == null ){
		return null;
	}
	else{
		return utools.db.get(key).data;
	}
}
function u_set(key,value){
	//保存百度密钥
	var result = utools.db.get(key);
	if( result == null ){
		utools.db.put({
		  _id: key,
		  data: value
		});
	}
	else{
		utools.db.put({
		  _id: key,
		  data: value,
		  _rev: result._rev
		});
	}
}

//全局消息提示框
function global_msg(content,is_color = 'is-success'){
	if( is_color == null ) {
		app.g_msg.is_type = 'is-success';
	}
	else{
		app.g_msg.is_type = is_color;
	}
	//global_msg('dfdsf','is-message')this.g_msg.is_type = (is_type == null) ? 'is-success' : 'is-danger';
	app.g_msg.content = content;
	app.g_msg.status = true;
	window.setTimeout(function(){
		app.g_msg.status = false;
	},3000);
}

//复制结果
//复制链接
function copy_result(str_type){
    var copy = new clipBoard('', {
        beforeCopy: function() {
            //var content = app.results;
        },
        copy: function() {
			if( str_type == 'content' ){
				global_msg('内容已复制!');
				return app.content;
			}
			else if( str_type == 'results' ){
				global_msg('结果已复制!');
				return app.results;
			}
        },
        afterCopy: function() {

        }
    });
}

//字符过滤函数
function deal_str(str){
	//去除空白字符
	var result = str.trim();
	return result;
}
//字符串检查,这个函数暂时没用到
function check_str(str){
	console.log('zifuchuan:' + str.length);
	if ( str.length == 0 ) {
		global_msg('字符不能为空!','is-danger');
		console.log('检查字符串');
		return false;
	}
	else if( str.length < 8) {
		global_msg('字符长度不正确!','is-danger');
		return false;
	}
}

已知问题

已知问题在未来计划中进行修复。

  1. 当默认翻译为百度翻译时,超级面板唤出“北冥翻译”有一定几率出现Invalid Sign错误。