├── .commitlintrc.json
├── .eslintignore
├── .eslintrc.js
├── .github
├── FUNDING.yml
└── workflows
│ └── release.yml
├── .gitignore
├── .prettierrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appcast.json
├── assets
└── translate-screenshot.png
├── package.json
├── rollup.config.js
├── scripts
├── bundle.js
└── update-appcast.js
├── src
├── api.ts
├── config.ts
├── main.ts
├── types.ts
└── utils.ts
├── tsconfig.eslint.json
├── tsconfig.json
├── types
└── global.d.ts
└── yarn.lock
/.commitlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@commitlint/config-angular"],
3 | "rules": {
4 | "subject-case": [
5 | 2,
6 | "always",
7 | ["sentence-case", "start-case", "pascal-case", "upper-case", "lower-case", "camel-case"]
8 | ],
9 | "type-enum": [
10 | 2,
11 | "always",
12 | [
13 | "build",
14 | "chore",
15 | "ci",
16 | "docs",
17 | "feat",
18 | "fix",
19 | "perf",
20 | "refactor",
21 | "revert",
22 | "style",
23 | "test",
24 | "sample"
25 | ]
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build
2 | release
3 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { es6: true, node: true },
4 | parser: '@typescript-eslint/parser',
5 | parserOptions: {
6 | ecmaVersion: 2020,
7 | sourceType: 'module',
8 | project: './tsconfig.eslint.json',
9 | },
10 | plugins: ['@typescript-eslint', 'prettier'],
11 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
12 | globals: {
13 | $log: false,
14 | $info: false,
15 | $option: false,
16 | $http: false,
17 | $file: false,
18 | $data: false,
19 | },
20 | rules: {
21 | '@typescript-eslint/ban-ts-comment': 'off',
22 | '@typescript-eslint/no-extra-semi': 'off',
23 | '@typescript-eslint/no-var-requires': 'off',
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: geekdada
2 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | release:
10 | runs-on: ${{ matrix.os }}
11 | strategy:
12 | matrix:
13 | os: [ubuntu-latest]
14 | node-version: [12]
15 | steps:
16 | - name: Fetch repository
17 | uses: actions/checkout@v2
18 |
19 | - name: Use Node.js ${{ matrix.node-version }}
20 | uses: actions/setup-node@v1
21 | with:
22 | node-version: ${{ matrix.node-version }}
23 |
24 | - name: Cache node modules
25 | uses: actions/cache@v2
26 | with:
27 | path: node_modules
28 | key: ${{ runner.OS }}-build-${{ hashFiles('**/yarn.lock') }}
29 | restore-keys: |
30 | ${{ runner.OS }}-build-${{ env.cache-name }}-
31 | ${{ runner.OS }}-build-
32 | ${{ runner.OS }}-
33 |
34 | - name: yarn install, build, bundle
35 | run: |
36 | yarn install
37 | yarn make-release
38 |
39 | - name: Commit files
40 | run: |
41 | git config --global user.name 'Roy Li'
42 | git config --global user.email 'me@dada.li'
43 | git commit -am "chore: update appcast.json"
44 |
45 | - name: Push changes
46 | uses: ad-m/github-push-action@master
47 | with:
48 | github_token: ${{ secrets.GITHUB_TOKEN }}
49 |
50 | - uses: ncipollo/release-action@v1
51 | with:
52 | artifacts: 'release/*.bobplugin'
53 | token: ${{ secrets.GITHUB_TOKEN }}
54 | draft: true
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | .idea
4 |
5 | # dependencies
6 | /node_modules
7 | /.pnp
8 | .pnp.js
9 |
10 | # testing
11 | /coverage
12 |
13 | # production
14 | /build
15 | /release
16 |
17 | # misc
18 | .DS_Store
19 | .env.local
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 |
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | .vercel
29 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | singleQuote: true,
4 | trailingComma: 'all',
5 | jsxBracketSameLine: true,
6 | };
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.8.1](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.8.0...v0.8.1) (2023-02-25)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * DeepL API authorization ([711851e](https://github.com/geekdada/bob-plugin-deepl-translate/commit/711851efb41d1c4e24c9b43f2a137919b0f3e368))
7 |
8 |
9 |
10 | # [0.8.0](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.7.0...v0.8.0) (2021-05-08)
11 |
12 |
13 | ### Features
14 |
15 | * add support for deepl free api token ([b1b65d5](https://github.com/geekdada/bob-plugin-deepl-translate/commit/b1b65d58eb7ef7e853abc79aa6a4982d0bb07663))
16 |
17 |
18 |
19 | # [0.7.0](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.6.0...v0.7.0) (2021-02-14)
20 |
21 |
22 | ### Features
23 |
24 | * remove support for a translator ([08eaca1](https://github.com/geekdada/bob-plugin-deepl-translate/commit/08eaca1c2a6152a4e321e859b05c36d16e2f9211))
25 |
26 |
27 |
28 | # [0.6.0](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.5.0...v0.6.0) (2020-12-25)
29 |
30 |
31 | ### Features
32 |
33 | * support new api endpoint ([f1b363a](https://github.com/geekdada/bob-plugin-deepl-translate/commit/f1b363a1207aacaf0e9642ecc6ba0b9577f4a4c0))
34 |
35 |
36 |
37 | # [0.5.0](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.4.2...v0.5.0) (2020-12-04)
38 |
39 |
40 | ### Features
41 |
42 | * rename api ([655526b](https://github.com/geekdada/bob-plugin-deepl-translate/commit/655526b410e6c3690b595633a868d149776cfc26))
43 |
44 |
45 |
46 | ## [0.4.2](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.4.1...v0.4.2) (2020-12-03)
47 |
48 |
49 | ### Bug Fixes
50 |
51 | * no formality for certain languages ([fde6417](https://github.com/geekdada/bob-plugin-deepl-translate/commit/fde6417c38424d8b55adae2be01187db78829095))
52 |
53 |
54 |
55 | ## [0.4.1](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.4.0...v0.4.1) (2020-11-26)
56 |
57 |
58 | ### Bug Fixes
59 |
60 | * remove glue code ([37c61d5](https://github.com/geekdada/bob-plugin-deepl-translate/commit/37c61d58db210958a67318a49dfe69c6075a6eb9))
61 |
62 |
63 |
64 | # [0.4.0](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.3.2...v0.4.0) (2020-11-21)
65 |
66 |
67 | ### Features
68 |
69 | * change api endpoint ([f721f01](https://github.com/geekdada/bob-plugin-deepl-translate/commit/f721f0146d1a66e9528ac72704351c470d9dd691))
70 |
71 |
72 |
73 | ## [0.3.2](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.3.1...v0.3.2) (2020-11-19)
74 |
75 |
76 | ### Bug Fixes
77 |
78 | * api domain ([d7fa477](https://github.com/geekdada/bob-plugin-deepl-translate/commit/d7fa477099b6e0133b30652dfefd666ab4ebbdcc))
79 |
80 |
81 |
82 | ## [0.3.1](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.3.0...v0.3.1) (2020-11-19)
83 |
84 |
85 |
86 | # [0.3.0](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.2.1...v0.3.0) (2020-11-19)
87 |
88 |
89 | ### Features
90 |
91 | * support sub-deepl ([f65cf49](https://github.com/geekdada/bob-plugin-deepl-translate/commit/f65cf49dc4a50cef076a93f2f007ddf7ecc74fcb))
92 |
93 |
94 |
95 | ## [0.2.1](https://github.com/geekdada/bob-plugin-deepl-translate/compare/v0.2.0...v0.2.1) (2020-11-17)
96 |
97 |
98 |
99 | # 0.2.0 (2020-11-17)
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Roy Li
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bob-plugin-deepl-translate
2 |
3 | 这是一个 [Bob](https://ripperhe.gitee.io/bob/#/) 使用的 DeepL 翻译插件。
4 |
5 | 你需要有一个 DeepL 的 API Token 才可以使用本插件。
6 |
7 |

8 |
9 | ## 安装
10 |
11 | 1. 安装 Bob (version >= 0.5.0)
12 | 2. 下载插件 [releases](https://github.com/geekdada/bob-plugin-deepl-translate/releases)
13 | 3. [安装插件](https://ripperhe.gitee.io/bob/#/general/quickstart/plugin?id=%e5%ae%89%e8%a3%85%e6%8f%92%e4%bb%b6)
14 |
15 | ## 配置
16 |
17 | - `token`:DeepL API Token(需自己购买)。
18 | - `formality` 正式程度: 设置翻译后的文本是倾向于正式语言还是非正式语言。此功能目前适用于所有目标语言,除了英语、英式英语、美式英语、西班牙语、汉语和日语。
19 |
20 | ## 注意
21 |
22 | ### 字符是如何计费的?
23 |
24 | > DeepL API 计划对你的翻译量没有限制,并按字符数计算你的使用量。 计算的字符是源文本中的字符,也就是你要翻译的原文。
25 | >
26 | > 一个字符对应于一个 unicode 代码点,其中一个字符可以由多个字节组成。 例如,字符 é 在 UTF-8 中被表示为两个字节。 然而,DeepL 只将其视为一个字符。 隐形字符,如空格、制表符、换行符等也会被计算在内。
27 |
--------------------------------------------------------------------------------
/appcast.json:
--------------------------------------------------------------------------------
1 | {
2 | "identifier": "dev.royli.bob-plugin-deepl-translate",
3 | "versions": [
4 | {
5 | "version": "0.8.1",
6 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/tag/v0.8.1",
7 | "sha256": "73cfece4b953ddadbad00bc33e5000d24e8acfd2f4b73fd9b3216da7eef44a94",
8 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.8.1/bob-plugin-deepl-translate.bobplugin",
9 | "minBobVersion": "0.5.0"
10 | },
11 | {
12 | "version": "0.8.0",
13 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/tag/v0.8.0",
14 | "sha256": "7d14cb1f447c80aa843e58f49916f442031355eac80d6fc118864d7266f9390d",
15 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.8.0/bob-plugin-deepl-translate.bobplugin",
16 | "minBobVersion": "0.5.0"
17 | },
18 | {
19 | "version": "0.7.0",
20 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/tag/v0.7.0",
21 | "sha256": "2f7acffeb598f32ff498392eb2eb72c666ba4b0c61861d88428091748b297fdb",
22 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.7.0/bob-plugin-deepl-translate.bobplugin",
23 | "minBobVersion": "0.5.0"
24 | },
25 | {
26 | "version": "0.6.0",
27 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/tag/v0.6.0",
28 | "sha256": "ae0f1c9fff74668b289278df4b9569f1e95a2a1f9ccd366aa89b6db3643c1e59",
29 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.6.0/bob-plugin-deepl-translate.bobplugin",
30 | "minBobVersion": "0.5.0"
31 | },
32 | {
33 | "version": "0.5.0",
34 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/blob/master/CHANGELOG.md",
35 | "sha256": "1293d1e3f492f6ea62ff7fa6f85ce6e26d9a19de277b364d33839b97ce669f00",
36 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.5.0/bob-plugin-deepl-translate.bobplugin",
37 | "minBobVersion": "0.5.0"
38 | },
39 | {
40 | "version": "0.4.2",
41 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/blob/master/CHANGELOG.md",
42 | "sha256": "ed7b8a0758a4dfe4660a24509b10323eb456c84b10a8624fd31fefdfb123cae5",
43 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.4.2/bob-plugin-deepl-translate.bobplugin",
44 | "minBobVersion": "0.5.0"
45 | },
46 | {
47 | "version": "0.4.1",
48 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/blob/master/CHANGELOG.md",
49 | "sha256": "39d44479fb6805c4a4175e7933e0aa772d2559cac970a3f3972bcb410c47ccef",
50 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.4.1/bob-plugin-deepl-translate-v0.4.1.bobplugin",
51 | "minBobVersion": "0.5.0"
52 | },
53 | {
54 | "version": "0.4.0",
55 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/blob/master/CHANGELOG.md",
56 | "sha256": "002c725f0541fae988ad52767480ec37aae8a27db2428d1bd1f2f0b060bf5c41",
57 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.4.0/bob-plugin-deepl-translate-v0.4.0.bobplugin",
58 | "minBobVersion": "0.5.0"
59 | },
60 | {
61 | "version": "0.3.2",
62 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/blob/master/CHANGELOG.md",
63 | "sha256": "02d4477f88055426d43b7078ed6519ebc4bad56f28e55fc45956dd6c36890f03",
64 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.3.2/bob-plugin-deepl-translate-v0.3.2.bobplugin",
65 | "minBobVersion": "0.5.0"
66 | },
67 | {
68 | "version": "0.3.1",
69 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/blob/master/CHANGELOG.md",
70 | "sha256": "43fbf1400aa02746ea847680c143a9e4eb797c779eb0d4aa2014acdf6cd662cb",
71 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.3.1/bob-plugin-deepl-translate-v0.3.1.bobplugin",
72 | "minBobVersion": "0.5.0"
73 | },
74 | {
75 | "version": "0.2.1",
76 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/blob/master/CHANGELOG.md",
77 | "sha256": "cf49feef70efc0e11e8371c69793b028b2e16c9f31f9103fd2f53b83972aecae",
78 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.2.1/bob-plugin-deepl-translate-v0.2.1.bobplugin",
79 | "minBobVersion": "0.5.0"
80 | },
81 | {
82 | "version": "0.2.0",
83 | "desc": "https://github.com/geekdada/bob-plugin-deepl-translate/blob/master/CHANGELOG.md",
84 | "sha256": "ee987385061471c5ca0e684d934c7dee7ac6d0354cecc755c59d7bca166fe7a1",
85 | "url": "https://github.com/geekdada/bob-plugin-deepl-translate/releases/download/v0.2.0/bob-plugin-deepl-translate-v0.2.0.bobplugin",
86 | "minBobVersion": "0.5.0"
87 | }
88 | ]
89 | }
90 |
--------------------------------------------------------------------------------
/assets/translate-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geekdada/bob-plugin-deepl-translate/8321dea1ef4758e705f29c8372075d1d77fc55a0/assets/translate-screenshot.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bob-plugin-deepl-translate",
3 | "version": "0.8.1",
4 | "author": "Roy Li ",
5 | "homepage": "https://github.com/geekdada/bob-plugin-deepl-translate",
6 | "repository": "https://github.com/geekdada/bob-plugin-deepl-translate.git",
7 | "license": "MIT",
8 | "private": true,
9 | "scripts": {
10 | "clean": "rimraf build && rimraf release",
11 | "build": "run-s clean && cross-env NODE_ENV=production rollup -c rollup.config.js",
12 | "bundle": "cross-env NODE_ENV=production node scripts/bundle.js",
13 | "bundle:watch": "cross-env NODE_ENV=development nodemon --watch build scripts/bundle.js",
14 | "make-release": "run-s build bundle update-appcast",
15 | "dev": "run-s clean && cross-env NODE_ENV=development rollup -c rollup.config.js --watch",
16 | "type-check": "tsc --noEmit",
17 | "update-appcast": "node scripts/update-appcast.js",
18 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
19 | "pub": "np --no-publish --no-release-draft",
20 | "version": "npm run changelog && git add ."
21 | },
22 | "dependencies": {
23 | "@commitlint/cli": "^11.0.0",
24 | "@commitlint/config-angular": "^11.0.0",
25 | "@rollup/plugin-commonjs": "^16.0.0",
26 | "@rollup/plugin-json": "^4.1.0",
27 | "@rollup/plugin-node-resolve": "^10.0.0",
28 | "@rollup/plugin-replace": "^2.3.4",
29 | "@rollup/plugin-typescript": "^6.1.0",
30 | "@types/adm-zip": "^0.4.33",
31 | "@types/jsonfile": "^6.0.0",
32 | "@types/node": "^12",
33 | "@typescript-eslint/eslint-plugin": "^4.8.0",
34 | "@typescript-eslint/parser": "^4.8.0",
35 | "adm-zip": "^0.4.16",
36 | "conventional-changelog-cli": "^2.1.1",
37 | "cross-env": "^7.0.2",
38 | "eslint": "^7.13.0",
39 | "eslint-plugin-prettier": "^3.1.4",
40 | "form-data": "^3.0.0",
41 | "fs-extra": "^9.0.1",
42 | "husky": "^4.3.0",
43 | "lint-staged": "^10.4.0",
44 | "nodemon": "^2.0.6",
45 | "np": "^7.0.0",
46 | "npm-run-all": "^4.1.5",
47 | "prettier": "^2.1.2",
48 | "rollup": "^2.28.2",
49 | "rollup-plugin-copy": "^3.3.0",
50 | "rollup-plugin-node-polyfills": "^0.2.1",
51 | "ts-node": "^9.0.0",
52 | "tslib": "^2.0.3",
53 | "type-fest": "^0.19.0",
54 | "typescript": "^4.0.5"
55 | },
56 | "nodemonConfig": {
57 | "ignore": [
58 | "build/info.json"
59 | ]
60 | },
61 | "husky": {
62 | "hooks": {
63 | "pre-commit": "lint-staged",
64 | "commit-msg": "commitlint -c .commitlintrc.json -E HUSKY_GIT_PARAMS"
65 | }
66 | },
67 | "lint-staged": {
68 | "*.js": "eslint",
69 | "*.ts": "eslint --ext .ts"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { join } from 'path'
2 | import copy from 'rollup-plugin-copy'
3 | import json from '@rollup/plugin-json'
4 | import commonjs from '@rollup/plugin-commonjs'
5 | import resolve from '@rollup/plugin-node-resolve'
6 | import nodePolyfills from 'rollup-plugin-node-polyfills'
7 | import typescript from '@rollup/plugin-typescript'
8 | import replace from '@rollup/plugin-replace'
9 |
10 | export default {
11 | input: join(__dirname, './src/main.ts'),
12 | output: {
13 | format: 'cjs',
14 | exports: 'auto',
15 | dir: join(__dirname, `./build`),
16 | preserveModules: true,
17 | },
18 | plugins: [
19 | copy({
20 | targets: [
21 | // { src: './src/info.json', dest: `build` },
22 | ],
23 | }),
24 | json({ namedExports: false }),
25 | resolve({
26 | extensions: ['.js', '.ts'],
27 | preferBuiltins: true,
28 | }),
29 | commonjs({
30 | include: ['node_modules/**'],
31 | }),
32 | replace({
33 | values: {
34 | 'process.env.NODE_ENV': JSON.stringify(
35 | process.env.NODE_ENV || 'development',
36 | ),
37 | 'process.env.__VERSION__': JSON.stringify(
38 | require('./package.json').version,
39 | ),
40 | },
41 | preventAssignment: true,
42 | }),
43 | nodePolyfills(),
44 | typescript({}),
45 | ],
46 | external: [
47 | 'crypto-js',
48 | '$util',
49 | '$http',
50 | '$info',
51 | '$option',
52 | '$log',
53 | '$data',
54 | '$file',
55 | ],
56 | }
57 |
--------------------------------------------------------------------------------
/scripts/bundle.js:
--------------------------------------------------------------------------------
1 | const { join } = require('path')
2 | const AdmZip = require('adm-zip')
3 | const fs = require('fs-extra')
4 | const { promisify } = require('util')
5 |
6 | function generateInfo() {
7 | return {
8 | identifier: 'dev.royli.bob-plugin-deepl-translate',
9 | category: 'translate',
10 | name: 'DeepL Translate',
11 | summary: '',
12 | icon: '115',
13 | author: 'Roy Li',
14 | homepage: 'https://github.com/geekdada/bob-plugin-deepl-translate.git',
15 | appcast:
16 | 'https://github.com/geekdada/bob-plugin-deepl-translate/raw/master/appcast.json',
17 | minBobVersion: '0.5.0',
18 | options: [
19 | {
20 | identifier: 'token',
21 | type: 'text',
22 | title: 'Token',
23 | },
24 | {
25 | identifier: 'api',
26 | type: 'menu',
27 | title: 'API',
28 | defaultValue: 'deepl-pro',
29 | menuValues: [
30 | {
31 | title: 'Pro',
32 | value: 'deepl-pro',
33 | },
34 | {
35 | title: 'Free',
36 | value: 'deepl-free',
37 | },
38 | ],
39 | },
40 | {
41 | identifier: 'formality',
42 | type: 'menu',
43 | title: 'Formality',
44 | defaultValue: 'default',
45 | menuValues: [
46 | {
47 | title: 'Default',
48 | value: 'default',
49 | },
50 | {
51 | title: 'More formal',
52 | value: 'more',
53 | },
54 | {
55 | title: 'More informal',
56 | value: 'less',
57 | },
58 | ],
59 | },
60 | ],
61 | }
62 | }
63 |
64 | async function main() {
65 | const pkgName = 'bob-plugin-deepl-translate'
66 | const version = require('../package.json').version
67 | const buildDir = join(__dirname, '../build')
68 | const releaseDir = join(__dirname, '../release')
69 | const pkg = join(releaseDir, `${pkgName}.bobplugin`)
70 | const info = {
71 | ...generateInfo(),
72 | version,
73 | }
74 |
75 | await fs.writeJson(join(buildDir, 'info.json'), info)
76 |
77 | const zip = new AdmZip()
78 | zip.addLocalFolder(buildDir)
79 | await promisify(zip.writeZip)(pkg)
80 | }
81 |
82 | main().catch((err) => {
83 | console.error(err)
84 | process.exit(1)
85 | })
86 |
--------------------------------------------------------------------------------
/scripts/update-appcast.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const fs = require('fs-extra')
3 | const crypto = require('crypto')
4 |
5 | const pkg = require('../package.json')
6 | const plugAppcast = require('../appcast.json')
7 | const githubRelease = `https://github.com/geekdada/bob-plugin-deepl-translate/releases/download`
8 |
9 | function main() {
10 | const pkgName = 'bob-plugin-deepl-translate'
11 | const pkgPath = path.join(__dirname, `../release/${pkgName}.bobplugin`)
12 | const appcastPath = path.join(__dirname, '../appcast.json')
13 |
14 | const fileBuffer = fs.readFileSync(pkgPath)
15 | const sum = crypto.createHash('sha256')
16 | sum.update(fileBuffer)
17 | const hex = sum.digest('hex')
18 |
19 | const version = {
20 | version: pkg.version,
21 | desc:
22 | 'https://github.com/geekdada/bob-plugin-deepl-translate/releases/tag/v' +
23 | pkg.version,
24 | sha256: hex,
25 | url: `${githubRelease}/v${pkg.version}/${pkgName}.bobplugin`,
26 | minBobVersion: '0.5.0',
27 | }
28 |
29 | let versions = (plugAppcast && plugAppcast.versions) || []
30 |
31 | if (!Array.isArray(versions)) versions = []
32 |
33 | const index = versions.findIndex((v) => v.version === pkg.version)
34 |
35 | if (index === -1) {
36 | versions.splice(0, 0, version)
37 | } else {
38 | versions.splice(index, 1, version)
39 | }
40 |
41 | const appcastData = {
42 | identifier: 'dev.royli.bob-plugin-deepl-translate',
43 | versions,
44 | }
45 |
46 | fs.outputJSONSync(appcastPath, appcastData, { spaces: 2 })
47 | }
48 |
49 | main()
50 |
--------------------------------------------------------------------------------
/src/api.ts:
--------------------------------------------------------------------------------
1 | import {
2 | RequestCallbackResponse,
3 | RequestObject,
4 | validProvider,
5 | } from '../types/global'
6 |
7 | export class Api {
8 | constructor(private provider: validProvider | null, private token: string) {}
9 |
10 | private get baseUrl(): string {
11 | switch (this.provider) {
12 | case 'deepl-pro':
13 | return 'https://api.deepl.com'
14 |
15 | default:
16 | return 'https://api-free.deepl.com'
17 | }
18 | }
19 |
20 | async request>(
21 | requestObject: Omit,
22 | ): Promise> {
23 | try {
24 | const body: Record = {
25 | ...requestObject.body,
26 | }
27 | const url = `${this.baseUrl}${requestObject.url}`
28 |
29 | return await $http.request({
30 | ...requestObject,
31 | url,
32 | header: {
33 | 'Content-Type': 'application/x-www-form-urlencoded',
34 | Authorization: `DeepL-Auth-Key ${this.token}`,
35 | },
36 | body,
37 | })
38 | } catch (e) {
39 | Object.assign(e, {
40 | _type: 'network',
41 | _message: '接口请求错误 ' + e.message,
42 | })
43 |
44 | throw e
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | // supported languages
2 | export const supportedLanguages: ReadonlyArray<[string, string]> = [
3 | ['auto', 'AUTO'],
4 | ['zh-Hans', 'ZH'],
5 | ['en', 'EN'],
6 | ['de', 'DE'], // German
7 | ['fr', 'FR'], // French
8 | ['it', 'IT'], // Italian
9 | ['ja', 'JA'], // Japanese
10 | ['es', 'ES'], // Spanish
11 | ['nl', 'NL'], // Dutch
12 | ['pl', 'PL'], // Polish
13 | ['pt', 'PT'], // Portuguese (all Portuguese varieties mixed)
14 | ['ru', 'RU'], // Russian
15 | ]
16 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { Api } from './api'
2 | import { supportedLanguages } from './config'
3 | import { ErrorType, TranslateCompletion, TranslateQuery } from './types'
4 | import { langMap, langMapReverse, translateStatusCode } from './utils'
5 |
6 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
7 | export function supportLanguages(): ReadonlyArray {
8 | return supportedLanguages.map(([standardLang]) => standardLang)
9 | }
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
12 | export function translate(
13 | query: TranslateQuery,
14 | completion: (data: TranslateCompletion) => void,
15 | ): void {
16 | const api = new Api($option.api, $option.token);
17 |
18 | ; (async () => {
19 | const targetLanguage = langMap.get(query.detectTo)
20 |
21 | $log.info(`translate to ${targetLanguage}`)
22 |
23 | if (!targetLanguage) {
24 | const err = new Error()
25 | Object.assign(err, {
26 | _type: 'unsupportLanguage',
27 | _message: '不支持该语种',
28 | })
29 | throw err
30 | }
31 |
32 | const formality = isFormalitySupported(targetLanguage)
33 | ? $option.formality
34 | : 'default'
35 | const response = await api.request<{
36 | translations: ReadonlyArray<{
37 | detected_source_language: string
38 | text: string
39 | }>
40 | }>({
41 | method: 'POST',
42 | url: '/v2/translate',
43 | body: {
44 | text: query.text,
45 | target_lang: targetLanguage,
46 | split_sentences: '1',
47 | preserve_formatting: '0',
48 | formality,
49 | },
50 | })
51 |
52 | if (response.error) {
53 | const { statusCode } = response.response
54 |
55 | let reason: ErrorType
56 |
57 | if (statusCode >= 400 && statusCode < 500) {
58 | reason = 'param'
59 | } else {
60 | reason = 'api'
61 | }
62 |
63 | completion({
64 | error: {
65 | type: reason,
66 | message: `接口响应错误 ${translateStatusCode(statusCode)}`,
67 | addtion: JSON.stringify(response),
68 | },
69 | })
70 | } else {
71 | const translations = response.data?.translations
72 |
73 | if (!translations || !translations.length) {
74 | completion({
75 | error: {
76 | type: 'api',
77 | message: '接口未返回翻译结果',
78 | },
79 | })
80 |
81 | return
82 | }
83 |
84 | completion({
85 | result: {
86 | from: langMapReverse.get(translations[0].detected_source_language),
87 | toParagraphs: translations.map((item) => item.text),
88 | },
89 | })
90 | }
91 | })().catch((err) => {
92 | completion({
93 | error: {
94 | type: err._type || 'unknown',
95 | message: err._message || '未知错误',
96 | addtion: err._addtion,
97 | },
98 | })
99 | })
100 | }
101 |
102 | function isFormalitySupported(lang: string): boolean {
103 | const unsupported = ['EN', 'EN-GB', 'EN-US', 'ES', 'JA', 'ZH']
104 |
105 | return !unsupported.includes(lang.toUpperCase())
106 | }
107 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | // https://ripperhe.gitee.io/bob/#/plugin/quickstart/translate
2 | export interface TranslateQuery {
3 | text: string // 需要翻译的文本。
4 | from: string // 用户选中的源语种标准码,可能是 auto
5 | to: string // 用户选中的目标语种标准码,可能是 auto
6 | detectFrom: string // 检测过后的源语种,一定不是 auto,如果插件不具备检测语种的能力,可直接使用该属性
7 | detectTo: string // 检测过后的目标语种,一定不是 auto,如果不想自行推测用户实际需要的目标语种,可直接使用该属性
8 | }
9 |
10 | export interface TranslateCompletion {
11 | result?: {
12 | from?: string // 由翻译接口提供的源语种,可以与查询时的 from 不同。查看 语种列表。
13 | to?: string // 由翻译接口提供的目标语种,可以与查询时的 to 不同。查看 语种列表。
14 | fromParagraphs?: ReadonlyArray // 原文分段拆分过后的 string 数组,可不传。
15 | toParagraphs: ReadonlyArray // 译文分段拆分过后的 string 数组,必传。
16 | toDict?: never // 词典结果,见 to dict object。可不传。
17 | fromTTS?: never // result 原文的语音合成数据,如果没有,可不传。
18 | toTTS?: never // result 译文的语音合成数据,如果没有,可不传。
19 | raw?: any // 如果插件内部调用了某翻译接口,可将接口原始数据传回,方便定位问题,可不传。
20 | }
21 | error?: {
22 | type: ErrorType // 错误类型,可设置为下方错误之一
23 | message: string // 错误描述,用于展示给用户看
24 | addtion?: any // 附加信息,可以是任何可 json 序列化的数据类型,用于 debug
25 | }
26 | }
27 |
28 | export type ErrorType =
29 | | 'unknown' // 未知错误
30 | | 'param' // 参数错误
31 | | 'unsupportLanguage' // 不支持的语种
32 | | 'secretKey' // 缺少秘钥
33 | | 'network' // 网络异常,网络请失败
34 | | 'api' // 服务接口异常
35 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { supportedLanguages } from './config'
2 |
3 | /**
4 | * 往往各大翻译服务商的语种类型都不同,
5 | * 实现插件的过程中很可能需要服务商特有的语种标识符和 Bob 定义的标识符来回转换,
6 | * 建议以下面的方式实现,
7 | * `xxx` 代表服务商特有的语种标识符,请替换为真实的,
8 | * 具体支持的语种数量根据实际情况而定。
9 | *
10 | * Bob 语种标识符转服务商语种标识符(以为 'zh-Hans' 为例): var lang = langMap.get('zh-Hans');
11 | * 服务商语种标识符转 Bob 语种标识符: var standardLang = langMapReverse.get('xxx');
12 | */
13 |
14 | export const langMap = new Map(supportedLanguages)
15 |
16 | export const langMapReverse = new Map(
17 | supportedLanguages.map(([standardLang, lang]) => [lang, standardLang]),
18 | )
19 |
20 | export const translateStatusCode = (code: number): string => {
21 | switch (code) {
22 | case 400:
23 | return 'Bad request. Please check error message and your parameters.'
24 | case 403:
25 | return 'Authorization failed. Please supply a valid token.'
26 | case 404:
27 | return 'The requested resource could not be found.'
28 | case 413:
29 | return 'The request size exceeds the limit.'
30 | case 429:
31 | return 'Too many requests. Please wait and resend your request.'
32 | case 456:
33 | return 'Quota exceeded. The character limit has been reached.'
34 | case 503:
35 | return 'Resource currently unavailable. Try again later.'
36 | default:
37 | return 'Internal error'
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tsconfig.eslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [
4 | "src/**/*.ts",
5 | "types/**/*.ts",
6 | "./src/**/*.js",
7 | "./.eslintrc.js",
8 | "./rollup.config.js",
9 | "./scripts/**/*.js"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | // "outDir": "./build",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "module": "esnext",
8 | "declaration": false,
9 | "inlineSourceMap": false,
10 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
11 | "allowSyntheticDefaultImports": true, // https://zhuanlan.zhihu.com/p/29022311
12 | "resolveJsonModule": true,
13 | "experimentalDecorators": true,
14 |
15 | "strict": true /* Enable all strict type-checking options. */,
16 |
17 | /* Strict Type-Checking Options */
18 | // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
19 | // "strictNullChecks": true /* Enable strict null checks. */,
20 | // "strictFunctionTypes": true /* Enable strict checking of function types. */,
21 | // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
22 | // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
23 | // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
24 |
25 | /* Additional Checks */
26 | "noUnusedLocals": false /* Report errors on unused locals. */,
27 | "noUnusedParameters": true /* Report errors on unused parameters. */,
28 | "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
29 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
30 |
31 | /* Debugging Options */
32 | "traceResolution": false /* Report module resolution log messages. */,
33 | "listEmittedFiles": false /* Print names of generated files part of the compilation. */,
34 | "listFiles": false /* Print names of files part of the compilation. */,
35 | "pretty": true /* Stylize errors and messages using color and context. */,
36 |
37 | /* Experimental Options */
38 | // "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
39 | // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
40 |
41 | "lib": [
42 | "esnext"
43 | ],
44 | "types": [
45 | "node"
46 | ],
47 | "typeRoots": [
48 | "node_modules/@types",
49 | "types"
50 | ]
51 | },
52 | "include": ["types/**/*.ts", "src/**/*.ts", "test/**/*.ts"],
53 | "exclude": ["node_modules/**"],
54 | "compileOnSave": false,
55 | "skipLibCheck": true
56 | }
57 |
--------------------------------------------------------------------------------
/types/global.d.ts:
--------------------------------------------------------------------------------
1 | interface $info {
2 | identifier: string
3 | version: string
4 | category: string
5 | name: string
6 | summary: string
7 | icon: string
8 | author: string
9 | homepage: string
10 | appcast: string
11 | minBobVersion: string
12 | }
13 |
14 | interface $log {
15 | info(obj?: string | Record): void
16 | error(obj?: string | Record): void
17 | }
18 |
19 | interface $http {
20 | request(requestObject: RequestObject): Promise>
21 | }
22 |
23 | export interface RequestObject {
24 | method: 'GET' | 'POST' | 'DELETE' | 'HEAD'
25 | url: string
26 | header?: Record
27 | body?: Record
28 | timeout?: number // 请求超时
29 | }
30 |
31 | export interface RequestCallbackResponse> {
32 | data?: T
33 | response: {
34 | url: string // url
35 | MIMEType: string // MIME 类型
36 | expectedContentLength: number // 长度
37 | textEncodingName: string // 编码
38 | suggestedFilename: string // 建议的文件名
39 | statusCode: number // HTTP 状态码
40 | headers: Record // HTTP header
41 | }
42 | error?: {
43 | domain: string // domain
44 | code: number // code
45 | userInfo: any // userInfo
46 | localizedDescription: string // 描述
47 | localizedFailureReason: string // 原因
48 | localizedRecoverySuggestion: string // 建议
49 | }
50 | }
51 |
52 | interface $option {
53 | token: string
54 | formality: 'default' | 'more' | 'less'
55 | api: validProvider
56 | }
57 |
58 | declare global {
59 | const $info: $info
60 | const $log: $log
61 | const $http: $http
62 | const $option: $option
63 | }
64 |
65 | type validProvider = 'deepl-pro' | 'deepl-free'
66 |
--------------------------------------------------------------------------------