Remove Microsoft Translator integration and update docs

Eliminated Microsoft Translator support from the codebase, including its service implementation, API endpoint, and related tests. Updated both English and Chinese README files to reflect the removal and to clarify usage of DeepL and Google endpoints, providing improved examples and API documentation.
This commit is contained in:
xixu-me committed 2025-08-09 18:41:49 +08:00
1 parent e30b019cd2
commit 1076a5817f
7 files changed
+330 -552

No files matched your search

+162 -14
View File
@@ -132,7 +132,33 @@ graph TB
## 📦 Quick Start
### cURL Example
### cURL Examples
#### DeepL Translation (Recommended)
```bash
curl -X POST https://dplx.xi-xu.me/deepl \
-H "Content-Type: application/json" \
-d '{
"text": "Hello, world!",
"source_lang": "EN",
"target_lang": "ZH"
}'
```
#### Google Translate
```bash
curl -X POST https://dplx.xi-xu.me/google \
-H "Content-Type: application/json" \
-d '{
"text": "Hello, world!",
"source_lang": "EN",
"target_lang": "ZH"
}'
```
#### Legacy Endpoint (DeepL)
```bash
curl -X POST https://dplx.xi-xu.me/translate \
@@ -144,11 +170,13 @@ curl -X POST https://dplx.xi-xu.me/translate \
}'
```
### JavaScript Example
### JavaScript Examples
#### Using DeepL Translation
```javascript
async function translate(text, sourceLang = 'auto', targetLang = 'zh') {
const response = await fetch('https://dplx.xi-xu.me/translate', {
async function translateWithDeepL(text, sourceLang = 'auto', targetLang = 'zh') {
const response = await fetch('https://dplx.xi-xu.me/deepl', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -165,19 +193,47 @@ async function translate(text, sourceLang = 'auto', targetLang = 'zh') {
}
// Usage example
translate('Hello, world!', 'en', 'zh')
translateWithDeepL('Hello, world!', 'en', 'zh')
.then(result => console.log(result))
.catch(error => console.error(error));
```
### Python Example
#### Using Google Translate
```javascript
async function translateWithGoogle(text, sourceLang = 'auto', targetLang = 'zh') {
const response = await fetch('https://dplx.xi-xu.me/google', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
text: text,
source_lang: sourceLang,
target_lang: targetLang
})
});
const result = await response.json();
return result.data;
}
// Usage example
translateWithGoogle('Hello, world!', 'en', 'zh')
.then(result => console.log(result))
.catch(error => console.error(error));
```
### Python Examples
#### Using DeepL Translation
```python
import requests
import json
def translate(text, source_lang='auto', target_lang='zh'):
url = 'https://dplx.xi-xu.me/translate'
def translate_with_deepl(text, source_lang='auto', target_lang='zh'):
url = 'https://dplx.xi-xu.me/deepl'
data = {
'text': text,
'source_lang': source_lang,
@@ -194,7 +250,37 @@ def translate(text, source_lang='auto', target_lang='zh'):
# Usage example
try:
result = translate('Hello, world!', 'en', 'zh')
result = translate_with_deepl('Hello, world!', 'en', 'zh')
print(result)
except Exception as e:
print(f"Error: {e}")
```
#### Using Google Translate
```python
import requests
import json
def translate_with_google(text, source_lang='auto', target_lang='zh'):
url = 'https://dplx.xi-xu.me/google'
data = {
'text': text,
'source_lang': source_lang,
'target_lang': target_lang
}
response = requests.post(url, json=data)
result = response.json()
if result['code'] == 200:
return result['data']
else:
raise Exception(f"Translation failed: {result.get('message', 'Unknown error')}")
# Usage example
try:
result = translate_with_google('Hello, world!', 'en', 'zh')
print(result)
except Exception as e:
print(f"Error: {e}")
@@ -220,14 +306,14 @@ A modern, free web-based translation app powered by the DeepLX API. Features inc
1. [Download and install Pot for your platform](https://github.com/pot-app/pot-desktop/releases/latest)
2. Open Pot settings and navigate to Service Settings
3. Configure the DeepL service type as DeepLX and set the custom URL to `https://dplx.xi-xu.me/translate`
3. Configure the DeepL service type as DeepLX and set the custom URL to `https://dplx.xi-xu.me/deepl`
### [Zotero](https://www.zotero.org/) (Open-source reference management app)
1. [Download and install Zotero for your platform](https://www.zotero.org/download/)
2. Download and install the [Translate for Zotero](https://github.com/windingwind/zotero-pdf-translate) plugin
3. Open Zotero settings and navigate to the Services section under Translation
4. Configure the translation service as DeepLX (API) and set the endpoint to `https://dplx.xi-xu.me/translate` after clicking the config button
4. Configure the translation service as DeepLX (API) and set the endpoint to `https://dplx.xi-xu.me/deepl` after clicking the config button
### [PDFMathTranslate (pdf2zh)](https://github.com/Byaidu/PDFMathTranslate) (Open-source PDF document translation tool)
@@ -237,14 +323,14 @@ Refer to [Advanced Options](https://github.com/Byaidu/PDFMathTranslate?tab=readm
1. [Install Immersive Translate](https://immersivetranslate.com/download/)
2. Go to developer settings and enable beta testing features
3. Go to translation services and add a custom translation service DeepLX, configure the API URL to `https://dplx.xi-xu.me/translate`
3. Go to translation services and add a custom translation service DeepLX, configure the API URL to `https://dplx.xi-xu.me/deepl`
4. Configure the maximum requests per second and maximum text length per request to appropriate values (e.g., `80` and `5000`) to ensure stability and performance
### [Bob](https://bobtranslate.com/) (Closed-source macOS app)
1. [Download and install Bob from the Mac App Store](https://apps.apple.com/app/id1630034110)
2. Download and install the [bob-plugin-deeplx](https://github.com/missuo/bob-plugin-deeplx) plugin
3. Configure the plugin to use `https://dplx.xi-xu.me/translate`
3. Configure the plugin to use `https://dplx.xi-xu.me/deepl`
## 🚀 Self-deployment
@@ -329,7 +415,15 @@ For optimal performance and stability, it's recommended to deploy as many [XDPL]
## 📖 API Reference
### `/translate`
### Available Endpoints
| Endpoint | Provider | Description | Status |
|----------|----------|-------------|---------|
| `/deepl` | DeepL | Primary DeepL translation endpoint | **Recommended** |
| `/google` | Google Translate | Google Translate endpoint | Active |
| `/translate` | DeepL | Legacy endpoint (uses DeepL) | Legacy |
### `/deepl` (Recommended)
**Request Method**: `POST`
@@ -355,6 +449,60 @@ For optimal performance and stability, it's recommended to deploy as many [XDPL]
}
```
### `/google`
**Request Method**: `POST`
**Request Headers**: `Content-Type: application/json`
**Request Parameters**:
| Parameter | Type | Description | Required |
| - | - | - | - |
| `text` | string | Text to translate | Yes |
| `source_lang` | string | Source language code | No, default `AUTO` |
| `target_lang` | string | Target language code | No, default `EN` |
**Response**:
```json
{
"code": 200,
"data": "Translation result",
"id": "Random identifier",
"source_lang": "Detected source language code",
"target_lang": "Target language code"
}
```
### `/translate` (Legacy)
**Request Method**: `POST`
**Request Headers**: `Content-Type: application/json`
**Note**: This is a legacy endpoint that uses DeepL. For new integrations, please use `/deepl` instead.
**Request Parameters**:
| Parameter | Type | Description | Required |
| - | - | - | - |
| `text` | string | Text to translate | Yes |
| `source_lang` | string | Source language code | No, default `AUTO` |
| `target_lang` | string | Target language code | No, default `EN` |
**Response**:
```json
{
"code": 200,
"data": "Translation result",
"id": "Random identifier",
"source_lang": "Detected source language code",
"target_lang": "Target language code"
}
```
**Supported Language Codes**:
- `AUTO` - Auto-detect (source language only)
+159 -11
View File
@@ -134,6 +134,32 @@ graph TB
### cURL 示例
#### DeepL 翻译(推荐)
```bash
curl -X POST https://dplx.xi-xu.me/deepl \
-H "Content-Type: application/json" \
-d '{
"text": "Hello, world!",
"source_lang": "EN",
"target_lang": "ZH"
}'
```
#### Google 翻译
```bash
curl -X POST https://dplx.xi-xu.me/google \
-H "Content-Type: application/json" \
-d '{
"text": "Hello, world!",
"source_lang": "EN",
"target_lang": "ZH"
}'
```
#### 传统端点(DeepL)
```bash
curl -X POST https://dplx.xi-xu.me/translate \
-H "Content-Type: application/json" \
@@ -146,9 +172,11 @@ curl -X POST https://dplx.xi-xu.me/translate \
### JavaScript 示例
#### 使用 DeepL 翻译
```javascript
async function translate(text, sourceLang = 'auto', targetLang = 'zh') {
const response = await fetch('https://dplx.xi-xu.me/translate', {
async function translateWithDeepL(text, sourceLang = 'auto', targetLang = 'zh') {
const response = await fetch('https://dplx.xi-xu.me/deepl', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -165,19 +193,47 @@ async function translate(text, sourceLang = 'auto', targetLang = 'zh') {
}
// 使用示例
translate('Hello, world!', 'en', 'zh')
translateWithDeepL('Hello, world!', 'en', 'zh')
.then(result => console.log(result))
.catch(error => console.error(error));
```
#### 使用 Google 翻译
```javascript
async function translateWithGoogle(text, sourceLang = 'auto', targetLang = 'zh') {
const response = await fetch('https://dplx.xi-xu.me/google', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
text: text,
source_lang: sourceLang,
target_lang: targetLang
})
});
const result = await response.json();
return result.data;
}
// 使用示例
translateWithGoogle('Hello, world!', 'en', 'zh')
.then(result => console.log(result))
.catch(error => console.error(error));
```
### Python 示例
#### 使用 DeepL 翻译
```python
import requests
import json
def translate(text, source_lang='auto', target_lang='zh'):
url = 'https://dplx.xi-xu.me/translate'
def translate_with_deepl(text, source_lang='auto', target_lang='zh'):
url = 'https://dplx.xi-xu.me/deepl'
data = {
'text': text,
'source_lang': source_lang,
@@ -194,7 +250,37 @@ def translate(text, source_lang='auto', target_lang='zh'):
# 使用示例
try:
result = translate('Hello, world!', 'en', 'zh')
result = translate_with_deepl('Hello, world!', 'en', 'zh')
print(result)
except Exception as e:
print(f"错误: {e}")
```
#### 使用 Google 翻译
```python
import requests
import json
def translate_with_google(text, source_lang='auto', target_lang='zh'):
url = 'https://dplx.xi-xu.me/google'
data = {
'text': text,
'source_lang': source_lang,
'target_lang': target_lang
}
response = requests.post(url, json=data)
result = response.json()
if result['code'] == 200:
return result['data']
else:
raise Exception(f"翻译失败: {result.get('message', '未知错误')}")
# 使用示例
try:
result = translate_with_google('Hello, world!', 'en', 'zh')
print(result)
except Exception as e:
print(f"错误: {e}")
@@ -220,14 +306,14 @@ except Exception as e:
1. [下载并安装适用于您平台的 Pot](https://github.com/pot-app/pot-desktop/releases/latest)
2. 打开 Pot 设置并导航到服务设置
3. 将 DeepL 服务类型配置为 DeepLX,并将自定义 URL 配置为 `https://dplx.xi-xu.me/translate`
3. 将 DeepL 服务类型配置为 DeepLX,并将自定义 URL 配置为 `https://dplx.xi-xu.me/deepl`
### [Zotero](https://www.zotero.org/)(开源文献管理应用)
1. [下载并安装适用于您平台的 Zotero](https://www.zotero.org/download/)
2. 下载并安装 [Translate for Zotero](https://github.com/windingwind/zotero-pdf-translate) 插件
3. 打开 Zotero 设置并导航到翻译中的服务部分
4. 将翻译服务配置为 DeepLX(API),并点击配置按钮后将接口配置为 `https://dplx.xi-xu.me/translate`
4. 将翻译服务配置为 DeepLX(API),并点击配置按钮后将接口配置为 `https://dplx.xi-xu.me/deepl`
### [PDFMathTranslate(pdf2zh)](https://github.com/Byaidu/PDFMathTranslate)(开源 PDF 文档翻译工具)
@@ -237,14 +323,14 @@ except Exception as e:
1. [安装沉浸式翻译](https://immersivetranslate.com/zh-Hans/download/)
2. 进入开发者设置并开启 beta 测试特性
3. 进入翻译服务添加自定义翻译服务 DeepLX,将 API URL 配置为 `https://dplx.xi-xu.me/translate`
3. 进入翻译服务添加自定义翻译服务 DeepLX,将 API URL 配置为 `https://dplx.xi-xu.me/deepl`
4. 将每秒最大请求数和每次请求最大文本长度配置为合适的值(例如 `80` 和 `5000`),以确保稳定性和性能
### [Bob](https://bobtranslate.com/)(闭源 macOS 应用)
1. [从 Mac App Store 下载并安装 Bob](https://apps.apple.com/cn/app/id1630034110)
2. 下载并安装 [bob-plugin-deeplx](https://github.com/missuo/bob-plugin-deeplx) 插件
3. 配置插件使用 `https://dplx.xi-xu.me/translate`
3. 配置插件使用 `https://dplx.xi-xu.me/deepl`
## 🚀 自部署
@@ -329,7 +415,15 @@ npx wrangler deploy
## 📖 API 参考
### `/translate`
### 可用端点
| 端点 | 服务提供商 | 描述 | 状态 |
|----------|----------|-------------|---------|
| `/deepl` | DeepL | 主要 DeepL 翻译端点 | **推荐** |
| `/google` | Google 翻译 | Google 翻译端点 | 活跃 |
| `/translate` | DeepL | 传统端点(使用 DeepL) | 传统 |
### `/deepl`(推荐)
**请求方法**:`POST`
@@ -355,6 +449,60 @@ npx wrangler deploy
}
```
### `/google`
**请求方法**:`POST`
**请求标头**:`Content-Type: application/json`
**请求参数**:
| 参数 | 类型 | 说明 | 是否必要 |
| - | - | - | - |
| `text` | string | 要翻译的文本 | 是 |
| `source_lang` | string | 源语言代码 | 否,默认值 `AUTO` |
| `target_lang` | string | 目标语言代码 | 否,默认值 `EN` |
**响应**:
```json
{
"code": 200,
"data": "翻译结果",
"id": "随机标识符",
"source_lang": "检测到的源语言代码",
"target_lang": "目标语言代码"
}
```
### `/translate`(传统)
**请求方法**:`POST`
**请求标头**:`Content-Type: application/json`
**注意**:这是一个使用 DeepL 的传统端点。对于新集成,请使用 `/deepl`。
**请求参数**:
| 参数 | 类型 | 说明 | 是否必要 |
| - | - | - | - |
| `text` | string | 要翻译的文本 | 是 |
| `source_lang` | string | 源语言代码 | 否,默认值 `AUTO` |
| `target_lang` | string | 目标语言代码 | 否,默认值 `EN` |
**响应**:
```json
{
"code": 200,
"data": "翻译结果",
"id": "随机标识符",
"source_lang": "检测到的源语言代码",
"target_lang": "目标语言代码"
}
```
**支持的语言代码**:
- `AUTO` - 自动检测(仅作为源语言)
+3 -21
View File
@@ -20,7 +20,6 @@ import {
validateLanguageCode,
} from "./lib/security";
import { translateWithGoogle } from "./lib/services/googleTranslate";
import { translateWithMicrosoft } from "./lib/services/microsoftTranslator";
import { createStandardResponse } from "./lib/types";
/**
@@ -67,15 +66,12 @@ export default worker;
/**
* Common translation handler function
* Processes translation requests for DeepL, Google Translate, and Microsoft Translator
* Processes translation requests for both DeepL and Google Translate
* @param c - Hono context
* @param provider - Translation provider ('deepl', 'google', or 'microsoft')
* @param provider - Translation provider ('deepl' or 'google')
* @returns Translation response
*/
async function handleTranslation(
c: any,
provider: "deepl" | "google" | "microsoft"
) {
async function handleTranslation(c: any, provider: "deepl" | "google") {
const env = c.env;
const clientIP = getSecureClientIP(c.req.raw) || "unknown";
@@ -163,11 +159,6 @@ async function handleTranslation(
env,
clientIP,
});
} else if (provider === "microsoft") {
result = await translateWithMicrosoft(validatedParams, {
env,
clientIP,
});
} else {
// Use DeepL as default
result = await query(validatedParams, {
@@ -215,7 +206,6 @@ app
.get("/translate", (c) => c.text("Please use POST method :)"))
.get("/deepl", (c) => c.text("Please use POST method :)"))
.get("/google", (c) => c.text("Please use POST method :)"))
.get("/microsoft", (c) => c.text("Please use POST method :)"))
/**
* Debug endpoint for request format validation and troubleshooting
@@ -341,14 +331,6 @@ app
return handleTranslation(c, "google");
})
/**
* Microsoft Translator endpoint
* POST /microsoft - Uses Microsoft Translator service
*/
.post("/microsoft", async (c) => {
return handleTranslation(c, "microsoft");
})
/**
* Catch-all route for undefined paths
* Redirects all other requests to the GitHub repository
-196
View File
@@ -1,196 +0,0 @@
/**
* Microsoft Translator integration service
* Uses Microsoft Edge's translate authentication endpoint
*/
import { createErrorResponse } from "../errorHandler";
import {
Config,
createStandardResponse,
RequestParams,
ResponseParams,
} from "../types";
/**
* Language code mapping for Microsoft Translator
* Maps DeepLX language codes to Microsoft Translator language codes
*/
const LANGUAGE_MAP: Record<string, string> = {
auto: "",
zh: "zh-Hans",
"zh-cn": "zh-Hans",
"zh-tw": "zh-Hant",
"zh-hant": "zh-Hant",
en: "en",
ja: "ja",
ko: "ko",
fr: "fr",
es: "es",
ru: "ru",
de: "de",
it: "it",
tr: "tr",
"pt-pt": "pt-pt",
"pt-br": "pt",
pt: "pt",
vi: "vi",
id: "id",
th: "th",
ms: "ms",
ar: "ar",
hi: "hi",
"mn-cy": "mn-Cyrl",
"mn-mo": "mn-Mong",
km: "km",
"nb-no": "nb",
nb: "nb",
fa: "fa",
sv: "sv",
pl: "pl",
nl: "nl",
uk: "uk",
he: "he",
};
/**
* Normalize language code for Microsoft Translator API
*/
function normalizeLanguageCode(lang: string): string {
const normalized = lang.toLowerCase();
return LANGUAGE_MAP[normalized] || normalized;
}
/**
* Get authentication token from Microsoft Edge translate auth endpoint
*/
async function getAuthToken(): Promise<string> {
const tokenUrl = "https://edge.microsoft.com/translate/auth";
const response = await fetch(tokenUrl, {
method: "GET",
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
},
});
if (!response.ok) {
throw new Error(
`Failed to get auth token: ${response.status} ${response.statusText}`
);
}
const token = await response.text();
if (!token || token.trim() === "") {
throw new Error("Empty auth token received");
}
return token.trim();
}
/**
* Translate text using Microsoft Translator API
* @param params - Translation parameters (text, source_lang, target_lang)
* @param config - Configuration options
* @returns Translation response in DeepLX format
*/
export async function translateWithMicrosoft(
params: RequestParams,
config?: Config & { env?: any; clientIP?: string }
): Promise<ResponseParams> {
try {
const { text, source_lang, target_lang } = params;
// Get authentication token
const authToken = await getAuthToken();
// Normalize language codes
const fromLang = normalizeLanguageCode(source_lang);
const toLang = normalizeLanguageCode(target_lang);
// Construct the API URL with query parameters
const apiUrl = new URL(
"https://api-edge.cognitive.microsofttranslator.com/translate"
);
apiUrl.searchParams.append("from", fromLang);
apiUrl.searchParams.append("to", toLang);
apiUrl.searchParams.append("api-version", "3.0");
apiUrl.searchParams.append("includeSentenceLength", "true");
// Prepare request body
const requestBody = [{ Text: text }];
// Make the translation request
const response = await fetch(apiUrl.toString(), {
method: "POST",
headers: {
accept: "*/*",
"accept-language":
"zh-TW,zh;q=0.9,ja;q=0.8,zh-CN;q=0.7,en-US;q=0.6,en;q=0.5",
authorization: `Bearer ${authToken}`,
"cache-control": "no-cache",
"content-type": "application/json",
pragma: "no-cache",
"sec-ch-ua":
'"Microsoft Edge";v="120", "Chromium";v="120", "Not-A.Brand";v="24"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "cross-site",
Referer: "https://translator.microsoft.com/",
"Referrer-Policy": "strict-origin-when-cross-origin",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(
`Microsoft Translator API responded with status ${response.status}: ${response.statusText}`
);
}
const responseData = await response.json();
// Parse the response
if (!Array.isArray(responseData) || responseData.length === 0) {
throw new Error("Invalid response format from Microsoft Translator");
}
const firstResult = responseData[0];
if (!firstResult.translations || !Array.isArray(firstResult.translations)) {
throw new Error("No translations found in response");
}
const translation = firstResult.translations[0];
if (!translation || !translation.text) {
throw new Error("Empty translation result");
}
const translatedText = translation.text.trim();
// Detect source language if auto-detection was used
const detectedSourceLang =
firstResult.detectedLanguage?.language || fromLang;
// Format the response to match the DeepLX API
return createStandardResponse(
200,
translatedText,
Math.floor(Math.random() * 10000000000),
detectedSourceLang.toUpperCase(),
toLang.toUpperCase()
);
} catch (error) {
console.error("Error in Microsoft Translator:", error);
const errorResponse = createErrorResponse(error, {
endpoint: "/microsoft",
clientIP: config?.clientIP || "unknown",
});
return errorResponse.response;
}
}
-71
View File
@@ -45,77 +45,6 @@ describe("Translation Integration Tests", () => {
expect(result.target_lang).toBe("ZH");
});
it("should complete Microsoft Translator workflow", async () => {
// Mock Microsoft Translator auth token response
global.fetch = jest
.fn()
.mockResolvedValueOnce({
ok: true,
text: () => Promise.resolve("mock-auth-token"),
})
.mockResolvedValueOnce({
ok: true,
json: () =>
Promise.resolve([
{
translations: [
{
text: "你好世界",
to: "zh-Hans",
},
],
detectedLanguage: {
language: "en",
score: 1.0,
},
},
]),
});
const { translateWithMicrosoft } = await import(
"../../src/lib/services/microsoftTranslator"
);
const result = await translateWithMicrosoft(
{
text: "Hello world",
source_lang: "en",
target_lang: "zh",
},
{ env: mockEnv, clientIP: "127.0.0.1" }
);
expect(result.code).toBe(200);
expect(result.data).toBe("你好世界");
expect(result.source_lang).toBe("EN");
expect(result.target_lang).toBe("ZH-HANS");
// Verify both auth and translate API calls were made
expect(global.fetch).toHaveBeenCalledTimes(2);
expect(global.fetch).toHaveBeenNthCalledWith(
1,
"https://edge.microsoft.com/translate/auth",
expect.objectContaining({
method: "GET",
headers: expect.objectContaining({
"User-Agent": expect.stringContaining("Chrome"),
}),
})
);
expect(global.fetch).toHaveBeenNthCalledWith(
2,
expect.stringContaining("api-edge.cognitive.microsofttranslator.com"),
expect.objectContaining({
method: "POST",
headers: expect.objectContaining({
authorization: "Bearer mock-auth-token",
"content-type": "application/json",
}),
body: JSON.stringify([{ Text: "Hello world" }]),
})
);
});
it("should handle rate limiting in translation flow", async () => {
// Mock rate limit exceeded
(mockEnv.RATE_LIMIT_KV.get as jest.Mock).mockResolvedValue(
@@ -1,239 +0,0 @@
/**
* Unit tests for Microsoft Translator service
*/
import { beforeEach, describe, expect, it } from "@jest/globals";
import { translateWithMicrosoft } from "../../../src/lib/services/microsoftTranslator";
// Mock global fetch
const mockFetch = jest.fn();
global.fetch = mockFetch;
describe("Microsoft Translator Service", () => {
beforeEach(() => {
jest.clearAllMocks();
});
it("should successfully translate text", async () => {
// Mock successful auth token response
mockFetch
.mockResolvedValueOnce({
ok: true,
text: () => Promise.resolve("mock-auth-token"),
})
.mockResolvedValueOnce({
ok: true,
json: () =>
Promise.resolve([
{
translations: [
{
text: "你好世界",
to: "zh-Hans",
},
],
detectedLanguage: {
language: "en",
score: 1.0,
},
},
]),
});
const result = await translateWithMicrosoft({
text: "Hello world",
source_lang: "en",
target_lang: "zh",
});
expect(result.code).toBe(200);
expect(result.data).toBe("你好世界");
expect(result.source_lang).toBe("EN");
expect(result.target_lang).toBe("ZH-HANS");
// Verify correct API calls
expect(mockFetch).toHaveBeenCalledTimes(2);
expect(mockFetch).toHaveBeenNthCalledWith(
1,
"https://edge.microsoft.com/translate/auth",
expect.objectContaining({
method: "GET",
headers: expect.objectContaining({
"User-Agent": expect.stringContaining("Chrome"),
}),
})
);
expect(mockFetch).toHaveBeenNthCalledWith(
2,
expect.stringContaining("api-edge.cognitive.microsofttranslator.com"),
expect.objectContaining({
method: "POST",
headers: expect.objectContaining({
authorization: "Bearer mock-auth-token",
"content-type": "application/json",
}),
body: JSON.stringify([{ Text: "Hello world" }]),
})
);
});
it("should handle auto language detection", async () => {
// Mock successful auth token response
mockFetch
.mockResolvedValueOnce({
ok: true,
text: () => Promise.resolve("mock-auth-token"),
})
.mockResolvedValueOnce({
ok: true,
json: () =>
Promise.resolve([
{
translations: [
{
text: "Hello world",
to: "en",
},
],
detectedLanguage: {
language: "zh-Hans",
score: 0.95,
},
},
]),
});
const result = await translateWithMicrosoft({
text: "你好世界",
source_lang: "auto",
target_lang: "en",
});
expect(result.code).toBe(200);
expect(result.data).toBe("Hello world");
expect(result.source_lang).toBe("ZH-HANS");
expect(result.target_lang).toBe("EN");
});
it("should handle auth token failure", async () => {
// Mock failed auth token response
mockFetch.mockResolvedValueOnce({
ok: false,
status: 403,
statusText: "Forbidden",
});
const result = await translateWithMicrosoft({
text: "Hello world",
source_lang: "en",
target_lang: "zh",
});
expect(result.code).toBeGreaterThanOrEqual(400);
expect(result.data).toBeNull();
expect(mockFetch).toHaveBeenCalledTimes(1);
});
it("should handle translation API failure", async () => {
// Mock successful auth but failed translation
mockFetch
.mockResolvedValueOnce({
ok: true,
text: () => Promise.resolve("mock-auth-token"),
})
.mockResolvedValueOnce({
ok: false,
status: 500,
statusText: "Internal Server Error",
});
const result = await translateWithMicrosoft({
text: "Hello world",
source_lang: "en",
target_lang: "zh",
});
expect(result.code).toBeGreaterThanOrEqual(400);
expect(result.data).toBeNull();
expect(mockFetch).toHaveBeenCalledTimes(2);
});
it("should handle empty auth token", async () => {
// Mock empty auth token response
mockFetch.mockResolvedValueOnce({
ok: true,
text: () => Promise.resolve(""),
});
const result = await translateWithMicrosoft({
text: "Hello world",
source_lang: "en",
target_lang: "zh",
});
expect(result.code).toBeGreaterThanOrEqual(400);
expect(result.data).toBeNull();
expect(mockFetch).toHaveBeenCalledTimes(1);
});
it("should handle invalid translation response", async () => {
// Mock successful auth but invalid translation response
mockFetch
.mockResolvedValueOnce({
ok: true,
text: () => Promise.resolve("mock-auth-token"),
})
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve([{}]), // Empty translation object
});
const result = await translateWithMicrosoft({
text: "Hello world",
source_lang: "en",
target_lang: "zh",
});
expect(result.code).toBeGreaterThanOrEqual(400);
expect(result.data).toBeNull();
});
it("should normalize language codes correctly", async () => {
// Mock successful response
mockFetch
.mockResolvedValueOnce({
ok: true,
text: () => Promise.resolve("mock-auth-token"),
})
.mockResolvedValueOnce({
ok: true,
json: () =>
Promise.resolve([
{
translations: [
{
text: "你好世界",
to: "zh-Hans",
},
],
detectedLanguage: {
language: "en",
score: 1.0,
},
},
]),
});
await translateWithMicrosoft({
text: "Hello world",
source_lang: "EN", // Should be normalized to 'en'
target_lang: "ZH-CN", // Should be normalized to 'zh-Hans'
});
// Check that the URL contains correctly normalized language codes
const secondCall = mockFetch.mock.calls[1];
const url = secondCall[0];
expect(url).toContain("from=en");
expect(url).toContain("to=zh-hans");
});
});
+6
View File
@@ -186,6 +186,12 @@ export function createTestEnvironment() {
const env: Env = {
CACHE_KV: cacheKV as any,
RATE_LIMIT_KV: rateLimitKV as any,
{ status: 200 }
)
),
}),
} as any,
ANALYTICS: {
writeDataPoint: jest.fn(),
} as any,