命令行翻译工具

github地址

npm包地址

(温馨提示:注意保持package.json中版本一致,复制packeage.json,yarn install)

使用百度翻译工具API

使用Commander.js自带的功能

创建一个空目录,叫teanslate-reagen(webstorm快捷键:ALT+1打开关闭项目目录;Alt + 2打开或关闭命令行)

yarn init -y 把它初始化为一个npm包,package.json中版本号改为0.0.1

tsc --init 生成tsconfig.json配置文件,否则会报错

创建src目录放源代码,并在下面创建main.ts(console.log(‘hi’))

ts-node-dev src/main.ts 控制台输出hi,说明TS代码是可以执行的

如果不想每次都写ts-node-dev,可以在package.json中配置

{
"name": "translate-reagen",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"start": "ts-node-dev src/main.ts"
},
"license": "MIT"
}
{

  "name": "translate-reagen",
  "version": "0.0.1",
  "main": "index.js",
  "scripts": {
    "start": "ts-node-dev src/main.ts"
  },
  "license": "MIT"
}
{ "name": "translate-reagen", "version": "0.0.1", "main": "index.js", "scripts": { "start": "ts-node-dev src/main.ts" }, "license": "MIT" }

直接运行yarn start即可

既然要做命令行就要使用commander.jsyarn add commander

既然做node的开发,就要安装node的类型声明文件yarn add --dev @types/node

image.png

这样在webstorm中使用node写代码的时候就会有条提示,且不会划线报错了

当看到typings/index.ts 说明自带了TS的声明文件,因此就不用安装额外的东西了

image.png

创建 src/cli.ts 用来控制命令行(ts其实就是js,只不过多了类型等其他API)

Commander.js v3.0.2的文档

接下来就是CRM学习法,先拷贝Commander.js官方文档中的代码 const program = new commander.Command(); Webstorm 会自动引入响应模块

声明一下版本

import commander from "commander";
const program = new commander.Command();
program.version('0.1.0')
// parse就是对参数进行解析
program.parse(process.argv);
import commander from "commander";



const program = new commander.Command();




program.version('0.1.0')



// parse就是对参数进行解析
program.parse(process.argv);
import commander from "commander"; const program = new commander.Command(); program.version('0.1.0') // parse就是对参数进行解析 program.parse(process.argv);

运行ts-node-dev src/cli.ts -h

image.png

尝试修改下显示的cli.ts

program.version('0.1.0')
.name('fy-bd')
program.version('0.1.0')

    .name('fy-bd')
program.version('0.1.0') .name('fy-bd')

image.png

尝试修改下 [options],添加『用法』,”<>”表示必传的参数,“[]”表示可传的参数

program.version('0.1.0')
.name('fy-bd')
.usage('<a word>')
program.version('0.1.0')

    .name('fy-bd')

    .usage('<a word>')
program.version('0.1.0') .name('fy-bd') .usage('<a word>')

image.png

我们可以获取到当前命令行工具的版本号

image.png

因此,Commander.js是一个声明式写法,这样我们就写好了命令行工具的版本、名称及参数

添加我们自己的功能

获取用户传的参数(单词),然后去查这个单词的意思

// cli.ts
import commander from "commander";
import {translate} from "./main";
const program = new commander.Command();
program.version('0.1.0')
.name('fy-bd')
.usage('<a Word>')
.arguments('<Word>')
.action(function (word) {
translate(word)
});
// parse就是对参数进行解析
program.parse(process.argv);
// cli.ts
import commander from "commander";
import {translate} from "./main";




const program = new commander.Command();


program.version('0.1.0')
    .name('fy-bd')
    .usage('<a Word>')
    .arguments('<Word>')
    .action(function (word) {
        translate(word)
    });



// parse就是对参数进行解析
program.parse(process.argv);
// cli.ts import commander from "commander"; import {translate} from "./main"; const program = new commander.Command(); program.version('0.1.0') .name('fy-bd') .usage('<a Word>') .arguments('<Word>') .action(function (word) { translate(word) }); // parse就是对参数进行解析 program.parse(process.argv);

image.png

translate函数哪里来的?在main.ts(主要功能实现)中写的

// main.ts
export const translate = (word:any) => {
console.log('word')
console.log(word)
}
// main.ts
export const translate = (word:any) => {
    console.log('word')
    console.log(word)
}
// main.ts export const translate = (word:any) => { console.log('word') console.log(word) }

image.png

这样我们就拿到了用户输入的单词

但是我们怎么把单词翻译出来?

接下来我们就要找一个免费翻译接口(推荐使用百度和有道的,国外的容易被墙)

image.png

如何使用呢?

拼接完整请求:

api.fanyi.baidu.com/api/trans/v…

<======输出举例 =====>

正确情况

{
"from": "en",
"to": "zh",
"trans_result": [
{
"src": "apple",
"dst": "苹果"
}
]
}
{

    "from": "en",
    "to": "zh",
    "trans_result": [
        {
            "src": "apple",
            "dst": "苹果"
        }
    ]
}
{    "from": "en",    "to": "zh",    "trans_result": [        {            "src": "apple",            "dst": "苹果"        }    ] }

异常情况

{
"error_code": "54001",
"error_msg": "Invalid Sign"
}
{
    "error_code": "54001",
    "error_msg": "Invalid Sign"
}
{    "error_code": "54001",    "error_msg": "Invalid Sign" }

备注

  1. 修改appid为自己的百度翻译开放平台appid
  2. sign就是要传自己的秘钥,当然不能直接传,需要appid+q+salt+密钥的MD5值,即要这四个值的MD5值

image.png

  1. 搞一个MD5生成器,Google: md5 Hash Generator

将appid、q(这里为apple)、salt就是个随机数这个随便(1435660288)、密钥放到md5自动生成器中,将得到的那一串东西,放到sign中

image.png

这样就在浏览器中实现了翻译效果了!!

但是我们怎么通过Node.js实现翻译效果呢?

我们需要借助Node.js中的https中的https.request()来实现在Node.js发请求,之前是用Node.js响应

使用Node.js调用百度翻译API

打开https.request文档

Node.js中构造查询参数

复制可运行的代码

import * as https from "https";
export const translate = (word:any) => {
const options = {
hostname: 'www.baidu,com',
port: 443,
path: '/',
method: 'GET'
};
const req = https.request(options, (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', (e) => {
console.error(e);
});
req.end();
}
import * as https from "https";




export const translate = (word:any) => {
    const options = {
        hostname: 'www.baidu,com',
        port: 443,
        path: '/',
        method: 'GET'
    };

    const req = https.request(options, (res) => {
        console.log('statusCode:', res.statusCode);
        console.log('headers:', res.headers);


        res.on('data', (d) => {
            process.stdout.write(d);
        });
    });

    req.on('error', (e) => {
        console.error(e);
    });
    req.end();
}
import * as https from "https"; export const translate = (word:any) => { const options = { hostname: 'www.baidu,com', port: 443, path: '/', method: 'GET' }; const req = https.request(options, (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.on('data', (d) => { process.stdout.write(d); }); }); req.on('error', (e) => { console.error(e); }); req.end(); }

运行一下,我们就得到了百度首页的源代码

image.png

那么接下来我们把访问地址改为百度翻译的地址(不要http),路径为/api/trans/vip/translate

那么查询参数(?...)我们怎么传?

我们需要借助Node.js中的querystring – 查询字符串

它有一个querystring.stringify方法

例子:

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// 返回 'foo=bar&baz=qux&baz=quux&corge='
querystring.stringify({ foo: 'bar', baz: 'qux' }, ';', ':');
// 返回 'foo:bar;baz:qux'
querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// 返回 'foo=bar&baz=qux&baz=quux&corge='


querystring.stringify({ foo: 'bar', baz: 'qux' }, ';', ':');
// 返回 'foo:bar;baz:qux'
querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' }); // 返回 'foo=bar&baz=qux&baz=quux&corge=' querystring.stringify({ foo: 'bar', baz: 'qux' }, ';', ':'); // 返回 'foo:bar;baz:qux'

我们不妨尝试console.log(querystring.stringify({name: 'LG', sign: '1234'}));

image.png

这样我们就构造出了我们想要的查询参数了

Node.js中构MD5

const query: string = querystring.stringify({
q: word,
from: 'en',
to: 'zh',
appid: '???',
salt: Math.random(),
sign: '???'
})
const query: string = querystring.stringify({
    q: word,
    from: 'en',
    to: 'zh',
    appid: '???',
    salt: Math.random(),
    sign: '???'
})
const query: string = querystring.stringify({ q: word, from: 'en', to: 'zh', appid: '???', salt: Math.random(), sign: '???' })
  • 类似appid和密钥不能直接写,因为代码要上传到github
  • sign是MD5算出来的,安装下md5yarn add md5,因为我们的文件是ts,因此要看下md5包文件夹下是否自带了ts相关的文件,我们发现没有ts文件,说明这个库不支持ts,如果在ts中手动引入md5,发现webstorm根本没提示,不知道它有哪些方法属性。那怎么办呢,借助前端社区力量,有些人帮MD5写了TS的类型声明文件,我们安装吧yarm add --dev @types/mdf,一般类型声明文件都使用--dev,因为只有开发者使用
  • 什么叫完美兼容TS,在webstorm中有相关API的用法,也有相关模块的ts的类型声明文件xxx.d.ts

image.png

image.png

这样我们的Node.js中也有MD5了

这时候Node.js中的sign只要像在浏览器中那样再实现一次就好了

import * as https from "https";
import * as querystring from "querystring";
import md5 from "md5";
export const translate = (word: any) => {
const appid = '???'
const appSecret = '???'
const salt = Math.random()
const sign = md5(appid + word + salt + appSecret)
const query: string = querystring.stringify({
q: word,
from: 'en',
to: 'zh',
appid,
salt,
sign,
})
const options = {
hostname: 'api.fanyi.baidu.com',
port: 443,
path: '/api/trans/vip/translate' + query,
method: 'GET'
};
const req = https.request(options, (res) => {
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', (e) => {
console.error(e);
});
req.end();
};
import * as https from "https";

import * as querystring from "querystring";
import md5 from "md5";




export const translate = (word: any) => {
    const appid = '???'
    const appSecret = '???'
    const salt = Math.random()
    const sign = md5(appid + word + salt + appSecret)
    const query: string = querystring.stringify({
        q: word,
        from: 'en',
        to: 'zh',
        appid,
        salt,
        sign,
    })
    const options = {
        hostname: 'api.fanyi.baidu.com',
        port: 443,
        path: '/api/trans/vip/translate' + query,
        method: 'GET'
    };

    const req = https.request(options, (res) => {
        res.on('data', (d) => {
            process.stdout.write(d);
        });
    });

    req.on('error', (e) => {
        console.error(e);
    });
    req.end();
};
import * as https from "https"; import * as querystring from "querystring"; import md5 from "md5"; export const translate = (word: any) => { const appid = '???' const appSecret = '???' const salt = Math.random() const sign = md5(appid + word + salt + appSecret) const query: string = querystring.stringify({ q: word, from: 'en', to: 'zh', appid, salt, sign, }) const options = { hostname: 'api.fanyi.baidu.com', port: 443, path: '/api/trans/vip/translate' + query, method: 'GET' }; const req = https.request(options, (res) => { res.on('data', (d) => { process.stdout.write(d); }); }); req.on('error', (e) => { console.error(e); }); req.end(); };

运行一下:

image.png

由于我们的appid和秘钥还没上传,所以暂时会返回报错信息

处理appid和秘钥 & 使用TypeScript声明BaiduResult

如何不让appid和密钥不被泄露

为了使得appid和密钥不被泄露,我们在src目录下新建private.ts,然后再在根目录下新建一个.gitignore文件,在里面添加private.ts,这样在提交文件的时候就不会提交了

虽然在git中没有上传,但是打包之后appid和密钥还是在代码里,实在是没办法纯前端实现不暴露(除非将加密的过程放到服务器中)

tips: 如果发现yarn 安装的命令,运行命令的时候提示找不到命令,就运行yarn global bin的,把得到的命令配置到path中

接着运行代码,我们把单词apple传给百度翻译,返回给我们的不是error_code了,翻译的结果就放在dst中,它就是apple的中文的utf-8的形式

image.png

image.png

得到response的消息体

之前在做静态服务器的时候,获取post的消息体是通过监听data方法,只要有data将它放到数组中即可,最后监听它的end方法

同理,这里获取response的消息体也是一样

const request = https.request(options, (response) => {
let chunks: any = []
// data是下载的翻译结果数据
response.on('data', (chunk) => {
chunks.push(chunk);
});
// end表示下载完了
response.on('end', () => {
const string = Buffer.concat(chunks).toString()
console.log(string)
const object = JSON.parse(string);
console.log(object)
})
});
const request = https.request(options, (response) => {

    let chunks: any = []
    // data是下载的翻译结果数据

    response.on('data', (chunk) => {

        chunks.push(chunk);

    });

    // end表示下载完了

    response.on('end', () => {

    const string = Buffer.concat(chunks).toString()
    console.log(string)
    const object = JSON.parse(string);
    console.log(object)
    })
});
const request = https.request(options, (response) => { let chunks: any = [] // data是下载的翻译结果数据 response.on('data', (chunk) => { chunks.push(chunk); }); // end表示下载完了 response.on('end', () => { const string = Buffer.concat(chunks).toString() console.log(string) const object = JSON.parse(string); console.log(object) }) });

这样就得到了纯粹的JSON字符串,再把它变成JS对象,再对它进行JS的后续操作

image.png

什么是Buffer(缓冲区)

缓冲区

缓冲区是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。 缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

为什么要引入缓冲区

比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。

那我们怎么知道得到的结果object有哪些属性呢?这是TS非常擅长的事情,通过下面的结果中的属性结合百度翻译文档中的说明,知道了这个对象拥有了哪些属性

image.png

image.png

注意:不要随便用简写,能用全写就全写,trans英文中有跨性别意思,即从一种性别转换到另一种性别

那么我们就用TS声明一个类型

const request = https.request(options, (response) => {
let chunks: any = []
// data是下载的翻译结果数据
response.on('data', (chunk) => {
chunks.push(chunk);
});
// end表示下载完了
response.on('end', () => {
const string = Buffer.concat(chunks).toString()
type BaiduResult = {
error_code?: string;
error_msg?: string;
from: string;
to: string;
trans_result:{
src: string;
dst: string;
}[]
}
const object:BaiduResult = JSON.parse(string);
console.log(object)
})
});
const request = https.request(options, (response) => {

    let chunks: any = []
    // data是下载的翻译结果数据

    response.on('data', (chunk) => {

        chunks.push(chunk);

    });

    // end表示下载完了

    response.on('end', () => {

        const string = Buffer.concat(chunks).toString()
        type BaiduResult = {
            error_code?: string;
            error_msg?: string;
            from: string;
            to: string;
            trans_result:{
                src: string;
                dst: string;
            }[]
        }
        const object:BaiduResult = JSON.parse(string);
        console.log(object)
    })
});
const request = https.request(options, (response) => { let chunks: any = [] // data是下载的翻译结果数据 response.on('data', (chunk) => { chunks.push(chunk); }); // end表示下载完了 response.on('end', () => { const string = Buffer.concat(chunks).toString() type BaiduResult = { error_code?: string; error_msg?: string; from: string; to: string; trans_result:{ src: string; dst: string; }[] } const object:BaiduResult = JSON.parse(string); console.log(object) }) });

备注:其中

  • error_code?表示这个属性在这个对象中可能有,也可能没有,这是TS的一个语法
  • trans_result:{ src: string; dst: string; }[]
    表示trans_result它是一个数组,数组中的每一项是一个对象

为什么要声明类型呢?

你本来就知道对象object有这些属性,只不过以前要通过看文档才知道,现在呢?现在把文档写在代码里!文档变了,我们只要改声明就行了

这样我们在object的时候就有完全不一样的感觉了,如果不写类型声明,我们想用吧、object下的属性object.,点什么呢,完全没提示,是不是又要去看文档?但是给object加上类型声明后object:BaiduResult

image.png

这样写代码就爽多了,不用频繁的在文档和代码之间来回的切换!!!

response.on('end', () => {
const string = Buffer.concat(chunks).toString()
type BaiduResult = {
error_code?: string;
error_msg?: string;
from: string;
to: string;
trans_result:{
src: string;
dst: string;
}[]
}
const object:BaiduResult = JSON.parse(string);
console.log(object.trans_result[0].dst);
})
response.on('end', () => {
    const string = Buffer.concat(chunks).toString()
    type BaiduResult = {
        error_code?: string;
        error_msg?: string;
        from: string;
        to: string;
        trans_result:{
            src: string;
            dst: string;
        }[]
    }
    const object:BaiduResult = JSON.parse(string);
    console.log(object.trans_result[0].dst);
})
response.on('end', () => { const string = Buffer.concat(chunks).toString() type BaiduResult = { error_code?: string; error_msg?: string; from: string; to: string; trans_result:{ src: string; dst: string; }[] } const object:BaiduResult = JSON.parse(string); console.log(object.trans_result[0].dst); })

image.png

处理报错

image.png

if(object.error_code){
if(object.error_msg === '52003'){
console.log('用户认证失败')
}else{
console.error(object.error_msg)
}
// 退出进程
process.exit(2)
}else{
// 获取到翻译的结果
console.log(object.trans_result[0].dst);
// 0表示没有错误
process.exit(0)
}
if(object.error_code){
    if(object.error_msg === '52003'){
        console.log('用户认证失败')
    }else{
        console.error(object.error_msg)
    }
    // 退出进程
    process.exit(2)
}else{
    // 获取到翻译的结果
    console.log(object.trans_result[0].dst);
    // 0表示没有错误
    process.exit(0)
}
if(object.error_code){ if(object.error_msg === '52003'){ console.log('用户认证失败') }else{ console.error(object.error_msg) } // 退出进程 process.exit(2) }else{ // 获取到翻译的结果 console.log(object.trans_result[0].dst); // 0表示没有错误 process.exit(0) }

备注

process.exit([code])

process.exit() 方法以结束状态码 code 指示Node.js同步终止进程。 如果 code 未提供,此exit方法要么使用’success’ 状态码 0 ,要么使用 process.exitCode 属性值,前提是此属性已被设置。

表驱动编程(map)消除多余的if

当项目逻辑越来越多,可能会出现下面这样弱鸡的代码:

if (object.error_msg === '52003') {
console.log('用户认证失败')
} else if (object.error_msg === '52004') {
console.log('...')
} else if (object.error_msg === '52005') {
console.log('...')
} else if (object.error_msg === '52006') {
console.log('...')
} else {
console.error(object.error_msg)
}
if (object.error_msg === '52003') {
    console.log('用户认证失败')
} else if (object.error_msg === '52004') {
    console.log('...')
} else if (object.error_msg === '52005') {
    console.log('...')
} else if (object.error_msg === '52006') {
    console.log('...')
} else {
    console.error(object.error_msg)
}
if (object.error_msg === '52003') { console.log('用户认证失败') } else if (object.error_msg === '52004') { console.log('...') } else if (object.error_msg === '52005') { console.log('...') } else if (object.error_msg === '52006') { console.log('...') } else { console.error(object.error_msg) }

如何消除这样弱鸡的代码呢?

在最外层写一个map

const errorMap:any = {
52003: '用户认证失败',
52004: 'error2',
52005: 'error3',
52006: 'error4',
other: '服务器繁忙'
}
if (object.error_code) {
console.log(errorMap[object.error_code] || object.error_msg)
// 退出进程
process.exit(2)
} else {
// 获取到翻译的结果
console.log(object.trans_result[0].dst);
// 0表示没有错误
process.exit(0)
}
const errorMap:any = {
    52003: '用户认证失败',
    52004: 'error2',
    52005: 'error3',
    52006: 'error4',
    other: '服务器繁忙'
}

if (object.error_code) {
    console.log(errorMap[object.error_code] || object.error_msg)
    // 退出进程
    process.exit(2)
} else {
    // 获取到翻译的结果
    console.log(object.trans_result[0].dst);
    // 0表示没有错误
    process.exit(0)
}
const errorMap:any = { 52003: '用户认证失败', 52004: 'error2', 52005: 'error3', 52006: 'error4', other: '服务器繁忙' } if (object.error_code) { console.log(errorMap[object.error_code] || object.error_msg) // 退出进程 process.exit(2) } else { // 获取到翻译的结果 console.log(object.trans_result[0].dst); // 0表示没有错误 process.exit(0) }

如果代码中有很多的if ... else ... ,那么一定是代码有问题,如果逻辑复杂,可以声明多个表,即多个map,表和表之间也可以关联

英译中 & 中译英

我们怎么知道用户是想英译中?还是想中译英?

把单词的第一个字母看一下呗~,借助一下正则中test

if (/[a-zA-Z]/.test(word[0])) {
// 英译中
from = 'en';
to = 'zh';
} else {
// 中译英
from = 'zh';
to = 'en';
}
if (/[a-zA-Z]/.test(word[0])) {
    // 英译中
    from = 'en';
    to = 'zh';
} else {
    // 中译英
    from = 'zh';
    to = 'en';
}
if (/[a-zA-Z]/.test(word[0])) { // 英译中 from = 'en'; to = 'zh'; } else { // 中译英 from = 'zh'; to = 'en'; }

image.png

如何发布TypeScript包到npm

将ts文件编译成对一个的js文件

我们要在package.json中做一些配置,声明我们翻译的命令叫czd(查字典)

注意:czd对应的不是"src/cli.js"

"bin": {
"czd": "src/cli.js"
},
"bin": {
  "czd": "src/cli.js"
},
"bin": { "czd": "src/cli.js" },

因为这个文件是不能被Node执行的,它只能被ts-node-dev执行

那怎么办?

我们得要把代码进行编译,要把ts文件编译成js

怎么编译呢?

在全局安装了typescript之后,就会得到一个tsc的命令

如果没有初始化,需要使用tsc --init 初始化一下,就会得到一个tsconfig.json的文件

我们要把我们编译后的代码放到dist目录,因此要修改tsconfig.json中的outdir

  • 编译:把开发的程序源码编译成可执行文件。
  • 打包:将你开发后的可执行文件和必要的文档(如使用说明等)使用打包工具(如InstallShield)制作成软件包,就是我们通常用来安装软件的那个东西。

接着使用tsc -p . ,意思是把当前目录作为项目来编译

处理TS中的报错

如果命令行中报错,那有可能是因为ts并不知道一些变量的类型是什么,我们得告诉它

如: let chunks: Buffer[] = [] 意思是chunks是Buffer类型的数组,如果我们不知道chunks的类型就,console.log(chunks.constructor),如果此时报错,执行不了console.log()那么就删除掉tsconfig.json再运行一次,直到搞清楚chunks的类型为止,实在不行就标记为any(如果你用any,还用什么TS,干嘛不用JS)

TS的理念就是一开始你就要把什么都搞清楚(变量的类型)再写代码,什么都不清楚就不要写代码,还是老老实实用JS吧

TS和Node不是完美兼容的,为什么这么说呢?既然chunks的类型是Buffer类型的数组,那么chunk肯定是Buffer,怎么能是any呢?这点我们可以从Node 中 on的定义可以看到(ctrl+打击就可以跳转):

let chunks: Buffer[] = []
// data是下载的翻译结果数据
response.on('data', (chunk) => {
chunks.push(chunk);
});
let chunks: Buffer[] = []
// data是下载的翻译结果数据
response.on('data', (chunk) => {
    chunks.push(chunk);
});
let chunks: Buffer[] = [] // data是下载的翻译结果数据 response.on('data', (chunk) => { chunks.push(chunk); });

image.png

这是因为Node偷懒了,它不想搞清楚chunk是什么类型,就写了any

一个TS的报错逻辑:

interface ErrorMap {
[key: string]: string
}
const errorMap: ErrorMap = {
52000: '成功',
52001: '请求超时,请重试',
52002: '系统错误,请重试',
52003: '未授权用户,请检查appid是否正确或服务是否开通',
54000: '必填参数为空,请检查是否少传参数 ',
54001: '签名错误,请检查您的签名生成方法 ',
54003: '访问频率受限,请降低您的调用频率,或进行身份认证后切换为高级版/尊享版 ',
54004: '账户余额不足,请前往管理控制台为账户充值 ',
54005: '长query请求频繁,请降低长query的发送频率,3s后再试 ',
58000: '客户端IP非法,检查个人资料里填写的IP地址是否正确,可前往开发者信息-基本信息修改',
58001: '译文语言方向不支持,检查译文语言是否在语言列表里',
58002: '服务当前已关闭,请前往管理控制台开启服务 ',
90107: '认证未通过或未生效,请前往我的认证查看认证进度 '
}
errorMap[object.error_code]
interface ErrorMap {
    [key: string]: string
}




const errorMap: ErrorMap = {
    52000: '成功',
    52001: '请求超时,请重试',
    52002: '系统错误,请重试',
    52003: '未授权用户,请检查appid是否正确或服务是否开通',
    54000: '必填参数为空,请检查是否少传参数 ',
    54001: '签名错误,请检查您的签名生成方法 ',
    54003: '访问频率受限,请降低您的调用频率,或进行身份认证后切换为高级版/尊享版 ',
    54004: '账户余额不足,请前往管理控制台为账户充值  ',
    54005: '长query请求频繁,请降低长query的发送频率,3s后再试 ',
    58000: '客户端IP非法,检查个人资料里填写的IP地址是否正确,可前往开发者信息-基本信息修改',
    58001: '译文语言方向不支持,检查译文语言是否在语言列表里',
    58002: '服务当前已关闭,请前往管理控制台开启服务 ',
    90107: '认证未通过或未生效,请前往我的认证查看认证进度 '
}

errorMap[object.error_code]
interface ErrorMap { [key: string]: string } const errorMap: ErrorMap = { 52000: '成功', 52001: '请求超时,请重试', 52002: '系统错误,请重试', 52003: '未授权用户,请检查appid是否正确或服务是否开通', 54000: '必填参数为空,请检查是否少传参数 ', 54001: '签名错误,请检查您的签名生成方法 ', 54003: '访问频率受限,请降低您的调用频率,或进行身份认证后切换为高级版/尊享版 ', 54004: '账户余额不足,请前往管理控制台为账户充值 ', 54005: '长query请求频繁,请降低长query的发送频率,3s后再试 ', 58000: '客户端IP非法,检查个人资料里填写的IP地址是否正确,可前往开发者信息-基本信息修改', 58001: '译文语言方向不支持,检查译文语言是否在语言列表里', 58002: '服务当前已关闭,请前往管理控制台开启服务 ', 90107: '认证未通过或未生效,请前往我的认证查看认证进度 ' } errorMap[object.error_code]

如果上面不声明errorMap的类型就会报错,报错的原因是,errorMap中的只有13个,你现在把object.error_code作为key,如果它不在13个key里面怎么办

因此我们需要告诉TS,它的key可以是任意的字符串,值也可以是任意的字符串

修改.gitginore && 添加shebang

编译完成后我们发现,src下的ts文件都被编译成了对应的js文件,dist文件夹中的文件是不用上传到github中的,因为它是编译后的文件,因此要修改.gitignore文件

src/private.ts
/dist
/node_modules
src/private.ts
/dist
/node_modules
src/private.ts /dist /node_modules

为了指定cli.js只能使用Node执行,因此我们需要在cli.ts中加上shebang(cli.ts源代码是不需要shenbang的)

然后再使用tsc 编译.ts扩展名的文件

准备发布

1. 指定要上传的文件

修改package.json我们只需要上传dist目录下的所有js文件即可

"files": [
"dist/**/*.js"
],
"files": [
  "dist/**/*.js"
],
"files": [ "dist/**/*.js" ],

2. 登录npm

注意:要把npm中使用的taobao切换为npm官方源

image.png

使用npm adduser登录npm,输入用户名、密码、邮箱和一次性密码

3. 发布

使用npm publish 发布

如果修改了代码,一定要修改版本号重新npm publish,同一个版本不能使用不同的代码,否则发布不出去

在发布的包文件中添加README.md,告诉别人如何使用你这个工具

使用下面的方法来更新版本也是可以的:

npm version patch
npm publish
npm version patch
npm publish
npm version patch npm publish

最终效果

Snipaste_2023-06-27_16-07-23.png

逐步完善功能

后续我们应该完善我们的翻译工具,对比优秀的翻译工具,发现不足和好的功能,逐步添加功能

比如npm中一个好用的翻译工具,额外还多了例句和发音

安装方法

yarn global add fanyi

给命令一个别名alias

如果不想分享出去,只想自己用,那么我们可以给命令添加一个alias

vi ~/.bashrc 或者打开这个文件进行编辑

alias fy='ts-node-dev /f/LG/node-snippet/translate-reagen/src/cli.ts'

注意绝对路径的格式

要使用绝对路径,如下图复制一下

image.png

运行source ~/.bashrc

再使用czd hello 就相当于ts-node-dev src/cli.ts hello

image.png

gitignore语法规范

package.json中的files的配置也是遵循这个规则

语法规范

  • 空行或是以#开头的行即注释行将被忽略。
  • 可以在前面添加正斜杠/来避免递归,下面的例子中可以很明白的看出来与下一条的区别。
  • 可以在后面添加正斜杠/来忽略文件夹,例如build/即忽略build文件夹。
  • 可以使用!来否定忽略,即比如在前面用了*.apk,然后使用!a.apk,则这个a.apk不会被忽略。
  • *用来匹配零个或多个字符,如*.[oa]忽略所有以”.o”或”.a”结尾,*~忽略所有以~结尾的文件(这种文件通常被许多编辑器标记为临时文件);[]用来匹配括号内的任一字符,如[abc],也可以在括号内加连接符,如[0-9]匹配0至9的数;?用来匹配单个字符。
# 忽略 .a 文件
*.a
# 但否定忽略 lib.a, 尽管已经在前面忽略了 .a 文件
!lib.a
# 仅在当前目录下忽略 TODO 文件, 但不包括子目录下的 subdir/TODO
/TODO
# 忽略 build/ 文件夹下的所有文件
build/
# 忽略 doc/notes.txt, 不包括 doc/server/arch.txt
doc/*.txt
# 忽略所有的 .pdf 文件 在 doc/ directory 下的
doc/**/*.pdf
# 忽略 .a 文件
*.a
# 但否定忽略 lib.a, 尽管已经在前面忽略了 .a 文件
!lib.a
# 仅在当前目录下忽略 TODO 文件, 但不包括子目录下的 subdir/TODO
/TODO
# 忽略 build/ 文件夹下的所有文件
build/
# 忽略 doc/notes.txt, 不包括 doc/server/arch.txt
doc/*.txt
# 忽略所有的 .pdf 文件 在 doc/ directory 下的
doc/**/*.pdf
# 忽略 .a 文件 *.a # 但否定忽略 lib.a, 尽管已经在前面忽略了 .a 文件 !lib.a # 仅在当前目录下忽略 TODO 文件, 但不包括子目录下的 subdir/TODO /TODO # 忽略 build/ 文件夹下的所有文件 build/ # 忽略 doc/notes.txt, 不包括 doc/server/arch.txt doc/*.txt # 忽略所有的 .pdf 文件 在 doc/ directory 下的 doc/**/*.pdf

备注

  1. 如果不使用还是最好将百度翻译api的服务功能停掉,否则如果由于账号的泄漏可能会导致付费

image.png

  1. 为什么代码中用module.export.x = ...导出,使用require导入呢?

这是历史遗留的问题

✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘

  • 最早是Node.js那波人,搞了一个CommonJs,并没有被写入规范
module.export.x = ...
const api = require('...')
api.x()
module.export.x = ...
const api = require('...')


api.x()
module.export.x = ... const api = require('...') api.x()
  • 另外一波人不服,因为这种方式只能同步的引入,所以另外一波人又搞了一个require.js学名叫AMD的模块导入导出的方法

✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘✘

  • 不用管上面两种模块导入导出的方法,它们都是垃圾,因为标准里写了一个标准的方法
import
export const x = ...
import 



export const x = ...   
import export const x = ...

默认都是使用标准的方法导入和导出,那为什么现在的代码中还有require呢?因为以前写的垃圾代码还没死,垃圾还在,比如:MD5的代码,还是用moudle.export来导出,并没有对代码进行升级,它没升级,那我们只能使用旧的方法去导入导出,总不能用新版的导入导出去导入导出旧版的代码吧

这点我们不用过于纠结,能用标准的导入导出就用标准的导入导出。或者我们直接使用模块就好了,其他的交给webstorm,它会帮我们使用不同的方法引入相应模块

  1. 项目中使用的TypeScript,因为我们是通过ts-node-dev来执行项目中的代码,只不过大部分和JS是相似的,如import * as xxx from yyy,TypeScript推荐这样写,由于Node.js使用的还是主流的CommonJS的导入导出方法,所以在使用TypeScript打包编译后的文件中,导入导出变成了exports.translate = function(){}

那Node.js能不能也支持import * as xxx from yyy这种写法呢?

搜索node esmodule 开启(Node9以上,兼容性差),乱七八糟的要求贼多,还不如直接使用TS

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYalVzdq' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片