Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

合并:完成兼容性调整,合并至主分支 #4

Merged
merged 21 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 47 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
# DeepLX Serverless

DeepLX 免费翻译API**函数部署版**,与[原项目DeepLX](https://github.com/OwO-Network/DeepLX)的区别在于**利用了云函数的请求IP不固定的特性,极大程度上避免了`429`请求太频繁报错**
DeepLX 免费翻译API**函数部署版**,与原项目[DeepLX](https://github.com/OwO-Network/DeepLX)的区别在于**利用了无服务器函数(也叫边缘函数)的请求IP不固定的特性,极大程度上避免了`429`请求太频繁报错**

## Usage | 用法
**如果本项目对你有用的话,不妨点个`Star`❤️**
**Click `Star` if you like!! thanks❤️**

## Major Changes | 重大改变
> *vvvvery big changes🤣

如果您在这之前不使用本项目,此部分可以跳过。
1. 新增了docker部署支持
2. 请求参数变化: `alternative_number` -> `alt_count`

## Let's Go | 开始使用
### Prerequisites | 你需要准备什么

- 一双灵活的小手
- 一个聪明的脑袋瓜
- 安装nodejs
- 支持 `Nodejs ≥16.13` 或 `Serverless Function` 的服务器

### Deploy | 部署

使用任意支持云函数部署的服务器,比如可以使用vercel进行部署,又或者其他能够使用nodejs的服务器。(几乎大多数服务器提供商都提供函数计算服务器)
使用任意支持无服务器函数部署的服务器,比如可以使用 `Vercel` 或者 `Netlify` 进行部署,又或者其他能够使用nodejs的服务器。(大多数服务器提供商都提供函数计算服务器)

如果你拥有[Vercel](https://vercel.com)账号的话那就很简单了,因为你只需要点击下方按钮即可一键部署到Vercel:

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/guobao2333/DeepLX-Serverless)

如果你拥有[vercel](https://vercel.com)账号的话那就很简单了,因为你只需要点击下方按钮即可一键部署到Vercel:
<!-- 你也可以在[Netlify](https://netlify.com)上一键部署本项目:
(但目前正在测试中,多平台兼容好难啊……)

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fguobao2333%2FDeepLX-Serverless%2Ftree%2Fmain)
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/guobao2333/DeepLX-Serverless) -->

项目当前没有任何需要填写的变量值,但在后续可能会添加。
因维护者我有多个仓库需要维护,短时间内将无法对多平台部署方案进行兼容,您可以选择**自托管**方案。
✨项目当前没有任何需要填写的变量值,你只需要直接部署就可以用了,但在后续可能会添加。

如果部署完成了,就可以开始使用啦!🎉
#### Docker

直接运行容器:
Expand All @@ -33,71 +49,55 @@ docker build -t deeplx-api .
docker run -d -p 9000:9000 deeplx-api
```

#### 自托管
#### Self hosting | 自托管

尽管本项目是专为 serverless 适配的方案,但是也能使用自己的服务器进行部署:
尽管本项目是专为 serverless 适配的方案,但是也能使用自己的服务器进行部署:
```bash
git clone https://github.com/guobao/DeepLX-Serverless
git clone https://github.com/guobao2333/DeepLX-Serverless
cd DeepLX-Serverless
npm i
npm run start
```

直接复制到命令行运行:
📋直接复制到命令行运行:
```bash
git clone https://github.com/guobao/DeepLX-Serverless && cd DeepLX-Serverless && npm i && npm run start
git clone https://github.com/guobao2333/DeepLX-Serverless && cd DeepLX-Serverless && npm i && npm run start
```

你可以运行`npm run test`用来测试翻译接口。
🚧你可以运行`npm run test`用来测试翻译接口。
⚠️注意!测试命令仅返回翻译内容,获取所有结果需要使用`POST`!

### How To Use | 如何使用

使用post通过 `域名` + `/translate` + `json请求体` 这样的形式获取json响应。
使用post通过 `域名地址` + `/translate` + `json请求体` 这样的形式获取json响应。

请求示例:
```bash
curl --location --request POST 'https://YOUR-DOMAIN/translate' \
--header 'Content-Type: application/json' \
--data-raw '{
"text": "Hello, World!",
"source_lang": "en",
"target_lang": "zh",
"alternative_number": 3
}'
```
详细API文档,请查看本项目的wiki:
[English](https://github.com/guobao2333/DeepLX-Serverless/wiki/English-document) | [简体中文](https://github.com/guobao2333/DeepLX-Serverless/wiki/%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)

直接复制到命令行运行本地测试:
📋你可以直接复制到命令行运行**本地测试:**
```bash
curl --location 'http://localhost:9000/translate' --header 'Content-Type: application/json' --data '{"text": "你好,世界!", "source_lang": "zh", "target_lang": "en", "alternative_number": 3}'
```

**请修改`YOUR-DOMAIN`为你部署服务的域名!!**

-----------------

响应示例:
```json
{
"code": 200,
"message": "success",
"data": "Hello, world.",
"source_lang": "en",
"target_lang": "zh",
"alternatives": ["Hello, World.", "Hello, world!", "Hi, world."]
}
curl --location --request POST 'http://localhost:9000/translate' --header 'Content-Type: application/json' --data '{"text": "你好,世界!", "source_lang": "zh", "target_lang": "en"}'
```

部署完成后,建议搭配浏览器插件「沉浸式翻译」一同使用。
部署完成后,建议搭配浏览器插件「沉浸式翻译」一同使用。

## 沉浸式翻译设置

1. 在浏览器上安装最新的 [沉浸式翻译](https://github.com/immersive-translate/immersive-translate/releases)。
2. 点击左下角的 "开发者设置"。启用测试版实验功能。
3. 翻译服务选中 `DeepLX(beta)`
3. 设置 URL 为刚才获取的访问路径(需带translate)。
4. 设置 URL 为刚才获取的访问路径(需带translate)。

![沉浸式翻译](https://github.com/LegendLeo/deeplx-serverless/assets/25115173/d3affe2b-9e99-4d5c-bc8c-cd67e70d0368)

## Contribute | 贡献

> [!IMPORTANT]
> **在您做出贡献之前,请先切换到`dev`分支!!**
> 因为解决分支冲突真的很麻烦!

本人因时间(和各种各样的)原因,故无法及时对您的贡献进行测试,所以您需要**自行测试**。

## Star History

<a href="https://star-history.com/#guobao2333/DeepLX-Serverless&Date">
Expand All @@ -108,7 +108,8 @@ curl --location 'http://localhost:9000/translate' --header 'Content-Type: applic
</picture>
</a>

## 鸣谢
## Acknowledgments | 鸣谢

1. [OwO-Network/DeepLX](https://github.com/OwO-Network/DeepLX)
2. [LegendLeo/deeplx-serverless](https://github.com/LegendLeo/deeplx-serverless)
3. [bropines/Deeplx-vercel](https://github.com/bropines/Deeplx-vercel)
67 changes: 9 additions & 58 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,16 @@
const express = require('express');
const bodyParser = require('body-parser');
const { translate } = require('../translate');
import express from 'express';
import fetch from 'node-fetch';
import { post, get } from '../server.js';

const app = express();
const PORT = 9000;
const allowAlternative = true;

app.use(bodyParser.json());

app.post('/translate', async (req, res) => {
const startTime = Date.now(); // 记录开始时间

// 检查请求方法和请求体
if (req.method !== 'POST' || !req.body || !req.body.text) {
const duration = Date.now() - startTime;
console.log(`[LOG] ${new Date().toISOString()} | 404 | ${duration}ms | POST "translate"`);
return res.status(404).json({
"code": 404,
"message": "Path not found"
});
}

if (!allowAlternative) alternative_number = 0;
const { text, source_lang, target_lang, alternative_number } = req.body;

try {
const result = await translate(text, source_lang, target_lang, alternative_number);
const duration = Date.now() - startTime; // 计算处理时间
console.log(`[LOG] ${new Date().toISOString()} | 200 | ${duration}ms | POST "translate"`);
// 转发请求
const response = await post(req, res);

const responseData = {
code: 200,
data: result.text, // 取第一个翻译结果
id: Math.floor(Math.random() * 10000000000), // 生成一个随机 ID
method: 'Free',
source_lang,
target_lang,
alternatives: result.alternatives
};

res.json(responseData);
} catch (error) {
const duration = Date.now() - startTime;
console.error(`[ERROR] ${new Date().toISOString()} | 500 | ${duration}ms | POST "translate" | ${error.message}`);
res.status(500).json({
code: 500,
message: 'Translation failed',
error: error.message
});
}
});

//
app.get('/', (req, res) => {
res.json({
code: 200,
message: "Welcome to the DeepL Free API. Please POST to '/translate'. Visit 'https://github.com/OwO-Network/DeepLX' and 'https://github.com/guobao/DeepLX-Serverless' for more information."
});
// 返回响应
const data = await response.json();
res.status(response.status).json(data);
});

// 启动本地服务器
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
app.get('/', async (req, res) => await get(req, res));
2 changes: 2 additions & 0 deletions netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
command = "npm install"
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
"name": "deeplx-serverless",
"version": "1.0.0",
"description": "DeepLX Free API for serverless",
"type": "module",
"main": "translate.js",
"engines": {
"node": ">=16.13"
},
"scripts": {
"start": "node api/index.js",
"start": "node server.js",
"test": "node test.js"
},
"keywords": [
Expand All @@ -19,6 +23,7 @@
"body-parser": "^1.20.2",
"express": "^4.18.2",
"lodash": "^4.17.21",
"node-fetch": "^3.3.2",
"random-int": "^3.0.0"
}
}
70 changes: 70 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import express from 'express';
import bodyParser from 'body-parser';
import { translate } from './translate.js';

const app = express();
const PORT = 9000;
const allowAlternative = true;

app.use(bodyParser.json());
// 为了方便兼容多平台才这样写
app.post('/translate', async (req, res) => await post(req, res));
app.get('/', async (req, res) => await get(req, res));

async function post(req, res) {
const startTime = Date.now(); // 记录开始时间

// 检查请求方法和请求体
if (req.method !== 'POST' || !req.body || !req.body.text) {
const duration = Date.now() - startTime;
console.log(`[LOG] ${new Date().toISOString()} | 404 | ${duration}ms | POST "translate"`);
return res.status(404).json({
"code": 404,
"message": "Path not found"
});
}

// 判断是否备选翻译
if (!allowAlternative) alt_count = 0;
const { text, source_lang, target_lang, alt_count } = req.body;

try {
const result = await translate(text, source_lang, target_lang, alt_count);
const duration = Date.now() - startTime; // 计算处理时间
console.log(`[LOG] ${new Date().toISOString()} | 200 | ${duration}ms | POST "translate"`);

const responseData = {
code: 200,
data: result.text, // 取第一个翻译结果
id: Math.floor(Math.random() * 10000000000), // 生成一个随机 ID
method: 'Free',
source_lang,
target_lang,
alternatives: result.alternatives
};

res.json(responseData);
} catch (error) {
const duration = Date.now() - startTime;
console.error(`[ERROR] ${new Date().toISOString()} | 500 | ${duration}ms | POST "translate" | ${error.message}`);
res.status(500).json({
code: 500,
message: 'Translation failed',
error: error.message
});
}
};

async function get(req, res) {
res.json({
code: 200,
message: "Welcome to the DeepL Free API. Please POST to '/translate'. Visit 'https://github.com/OwO-Network/DeepLX' and 'https://github.com/guobao2333/DeepLX-Serverless' for more information."
});
};

// 启动本地服务器
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

export { post, get };
3 changes: 1 addition & 2 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const { translate } = require('./translate');
import { translate } from './translate.js';

(async () => {
// Example Call
await translate('各位评委好,我是练习时长两年半的个人练习生,蔡徐坤', 'ZH', 'EN', 3, true);
console.log('\n');
await translate('鸡你太美,baby,鸡你实在是太美,baby', 'ZH', 'EN', 0, true)
})()
13 changes: 7 additions & 6 deletions translate.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const axios = require('axios').default;
const { random } = require('lodash');
import axios from 'axios';
import lodash from 'lodash';

const { random } = lodash;
const DEEPL_BASE_URL = 'https://www2.deepl.com/jsonrpc';
const headers = {
'Content-Type': 'application/json',
Expand Down Expand Up @@ -37,20 +38,20 @@ async function translate(
text,
sourceLang = 'AUTO',
targetLang = 'ZH',
alternativeNumber = 0,
alternativeCount = 0,
printResult = false,
) {
const iCount = getICount(text);
const id = getRandomNumber();

alternativeNumber = Math.max(Math.min(3, alternativeNumber), 0);
alternativeCount = Math.max(Math.min(3, alternativeCount), 0);

const postData = {
jsonrpc: '2.0',
method: 'LMT_handle_texts',
id: id,
params: {
texts: [{ text: text, requestAlternatives: alternativeNumber }],
texts: [{ text: text, requestAlternatives: alternativeCount }],
splitting: 'newlines',
lang: {
source_lang_user_selected: sourceLang.toUpperCase(),
Expand Down Expand Up @@ -97,4 +98,4 @@ async function translate(
}
}

exports.translate = translate;
export { translate };