Skip to content

feat: 添加DeepLX翻译服务支持,包含服务地址配置和令牌可选性检查#76

Merged
Bistutu merged 1 commit intoBistutu:mainfrom
hu3rror:add-deeplx
Jul 12, 2025
Merged

feat: 添加DeepLX翻译服务支持,包含服务地址配置和令牌可选性检查#76
Bistutu merged 1 commit intoBistutu:mainfrom
hu3rror:add-deeplx

Conversation

@hu3rror
Copy link
Copy Markdown
Contributor

@hu3rror hu3rror commented Jul 2, 2025

这 PR 引入了对 DeepLX 翻译服务的支持,能够利用本地或自托管的 DeepLX 实例进行翻译。

主要改动点:

  1. 新增 DeepLX 翻译服务选项:

    • 在翻译服务选择列表中加入了 "DeepLX" 选项,用户可以在设置中选择 DeepLX 作为翻译引擎。
    • (文件: entrypoints/utils/option.ts, components/Main.vue
  2. 可自定义的 DeepLX 服务地址:

    • 在设置页面的“DeepLX URL 配置”部分新增了一个输入框,允许用户配置 DeepLX API 的服务地址。默认地址为 http://localhost:1188/translate
    • 此配置项仅在选择 DeepLX 作为翻译服务时显示。
    • (文件: components/Main.vue, entrypoints/utils/constant.ts, entrypoints/utils/option.ts
  3. 可选的 API 令牌支持:

    • DeepLX 服务现在支持配置可选的 Bearer Token 进行认证,如果你的 DeepLX 实例需要令牌,可以在设置中填写。
    • 令牌配置在 DeepLX 服务中是非强制的,即使不配置令牌也能使用。
    • (文件: entrypoints/service/deeplx.ts, entrypoints/utils/check.ts
  4. DeepLX 翻译逻辑:

    • 参考了 deepl 的内容,新增 deeplx.ts 模块
    • (文件: entrypoints/service/deeplx.ts, entrypoints/service/_service.ts

Summary by Sourcery

Add support for the DeepLX translation service, including selectable service option, configurable API endpoint, and optional authentication token.

New Features:

  • Add DeepLX as a selectable translation engine
  • Provide a customizable DeepLX API URL field in settings with a default endpoint
  • Enable optional Bearer token authentication for DeepLX service

Bug Fixes:

  • Skip mandatory token validation when using DeepLX

Enhancements:

  • Integrate DeepLX into service type definitions and UI display logic

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Jul 2, 2025

Reviewer's Guide

This PR integrates DeepLX as a new translation backend by registering it alongside existing services, exposing a configurable endpoint and optional token in the UI, adjusting validation rules, and adding the actual translation logic module.

ER diagram for DeepLX service configuration options

erDiagram
    CONFIG {
      string service
      string deeplx
      map token
      map proxy
    }
    SERVICES {
      string value
      string label
    }
    CONFIG ||--o{ SERVICES : uses
    CONFIG ||--|{ TOKEN : has
    TOKEN {
      string service
      string token
    }
Loading

Class diagram for DeepLX translation service integration

classDiagram
    class Config {
      +string service
      +string deeplx
      +map token
      +map proxy
      +string from
      +string to
    }
    class servicesType {
      +Set machine
      +Set AI
      +Set useCustomUrl
      +isUseCustomUrl(service)
    }
    class options {
      +array services
      +object defaultOption
    }
    class _service {
      +deeplx
    }
    class deeplx {
      +async deeplx(message)
    }
    Config "1" -- "*" servicesType : uses
    Config "1" -- "*" options : uses
    _service "1" -- "1" deeplx : registers
    deeplx <|-- deeplx : implements
    note for deeplx "Implements DeepLX translation logic with optional token and configurable URL"
Loading

File-Level Changes

Change Details Files
Register DeepLX in service configuration and defaults
  • Added 'deeplx' entry in services list and servicesType sets
  • Included deeplx in useCustomUrl set
  • Defined default deeplx URL in defaultOption
  • Added deeplx URL to urls constant
entrypoints/utils/option.ts
entrypoints/utils/constant.ts
Expose DeepLX endpoint input in the settings UI
  • Inserted a URL input row shown when DeepLX is selected
  • Bound input to config.deeplx with placeholder default
  • Added showDeepLX computed property to Main.vue
components/Main.vue
Make DeepLX authentication token optional
  • Updated checkConfig to bypass missing-token error when service is deeplx
entrypoints/utils/check.ts
Implement DeepLX translation service
  • Created deeplx.ts module with fetch logic, language mapping, header construction and error handling
  • Registered deeplx in the service dispatcher map
entrypoints/service/deeplx.ts
entrypoints/service/_service.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @hu3rror - I've reviewed your changes - here's some feedback:

  • Avoid the no-op if (config.service === services.deeplx) {} in checkConfig by inverting the logic or using a guard clause to make the intent clearer.
  • DRY up the default DeepLX API URL by moving it into a single constant/shared config instead of duplicating it in both option.ts and constant.ts.
  • Leverage the new servicesType.isUseCustomUrl helper in Main.vue to control the DeepLX URL input visibility rather than a dedicated showDeepLX computed property.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Avoid the no-op `if (config.service === services.deeplx) {}` in `checkConfig` by inverting the logic or using a guard clause to make the intent clearer.
- DRY up the default DeepLX API URL by moving it into a single constant/shared config instead of duplicating it in both `option.ts` and `constant.ts`.
- Leverage the new `servicesType.isUseCustomUrl` helper in `Main.vue` to control the DeepLX URL input visibility rather than a dedicated `showDeepLX` computed property.

## Individual Comments

### Comment 1
<location> `entrypoints/service/deeplx.ts:7` </location>
<code_context>
+
+async function deeplx(message: any) {
+    // deeplx 不支持 zh-Hans,需要转换为 zh
+    let targetLang = config.to === 'zh-Hans' ? 'zh' : config.to;
+    let sourceLang = config.from === 'auto' ? 'auto' : config.from;
+    
+    // 判断是否使用代理或自定义URL
</code_context>

<issue_to_address>
Language code conversion for zh-Hans may need to be more robust.

Consider handling other Chinese language variants (e.g., 'zh-CN', 'zh-Hant') to ensure broader compatibility and prevent failures.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    // deeplx 不支持 zh-Hans,需要转换为 zh
    let targetLang = config.to === 'zh-Hans' ? 'zh' : config.to;
    let sourceLang = config.from === 'auto' ? 'auto' : config.from;
=======
    // deeplx 不支持部分中文变体,需要统一转换为 zh
    function normalizeChineseLang(lang: string): string {
        // 常见中文变体
        const zhVariants = [
            'zh-Hans', 'zh-CN', 'zh-SG', // 简体
            'zh-Hant', 'zh-TW', 'zh-HK', 'zh-MO' // 繁体
        ];
        if (zhVariants.includes(lang)) {
            return 'zh';
        }
        return lang;
    }
    let targetLang = normalizeChineseLang(config.to);
    let sourceLang = config.from === 'auto' ? 'auto' : config.from;
>>>>>>> REPLACE

</suggested_fix>

### Comment 2
<location> `entrypoints/service/deeplx.ts:34` </location>
<code_context>
+    });
+
+    if (resp.ok) {
+        let result = await resp.json();
+        // DeepLX 返回格式通常是 { code: 200, data: "translated text" }
+        if (result.code === 200) {
+            return result.data;
+        } else {
</code_context>

<issue_to_address>
No error handling for invalid JSON responses.

Wrap resp.json() in a try-catch block to handle invalid JSON and provide a clearer error message.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    if (resp.ok) {
        let result = await resp.json();
        // DeepLX 返回格式通常是 { code: 200, data: "translated text" }
        if (result.code === 200) {
            return result.data;
        } else {
            throw new Error(`DeepLX 翻译失败: ${result.message || '未知错误'}`);
        }
    } else {
        console.log("DeepLX 翻译失败:", resp);
        throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
    }
}
=======
    if (resp.ok) {
        let result;
        try {
            result = await resp.json();
        } catch (err) {
            throw new Error(`DeepLX 返回了无效的 JSON 响应: ${err instanceof Error ? err.message : String(err)}`);
        }
        // DeepLX 返回格式通常是 { code: 200, data: "translated text" }
        if (result.code === 200) {
            return result.data;
        } else {
            throw new Error(`DeepLX 翻译失败: ${result.message || '未知错误'}`);
        }
    } else {
        console.log("DeepLX 翻译失败:", resp);
        throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
    }
}
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `entrypoints/service/deeplx.ts:42` </location>
<code_context>
+            throw new Error(`DeepLX 翻译失败: ${result.message || '未知错误'}`);
+        }
+    } else {
+        console.log("DeepLX 翻译失败:", resp);
+        throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
+    }
</code_context>

<issue_to_address>
Logging the entire Response object may not be informative.

Instead, log resp.status, resp.statusText, or the response body for clearer debugging information.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
        console.log("DeepLX 翻译失败:", resp);
        throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
=======
        const errorBody = await resp.text();
        console.log("DeepLX 翻译失败:", `status: ${resp.status}, statusText: ${resp.statusText}, body: ${errorBody}`);
        throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${errorBody}`);
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +6 to +8
// deeplx 不支持 zh-Hans,需要转换为 zh
let targetLang = config.to === 'zh-Hans' ? 'zh' : config.to;
let sourceLang = config.from === 'auto' ? 'auto' : config.from;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Language code conversion for zh-Hans may need to be more robust.

Consider handling other Chinese language variants (e.g., 'zh-CN', 'zh-Hant') to ensure broader compatibility and prevent failures.

Suggested change
// deeplx 不支持 zh-Hans,需要转换为 zh
let targetLang = config.to === 'zh-Hans' ? 'zh' : config.to;
let sourceLang = config.from === 'auto' ? 'auto' : config.from;
// deeplx 不支持部分中文变体,需要统一转换为 zh
function normalizeChineseLang(lang: string): string {
// 常见中文变体
const zhVariants = [
'zh-Hans', 'zh-CN', 'zh-SG', // 简体
'zh-Hant', 'zh-TW', 'zh-HK', 'zh-MO' // 繁体
];
if (zhVariants.includes(lang)) {
return 'zh';
}
return lang;
}
let targetLang = normalizeChineseLang(config.to);
let sourceLang = config.from === 'auto' ? 'auto' : config.from;

Comment on lines +33 to +45
if (resp.ok) {
let result = await resp.json();
// DeepLX 返回格式通常是 { code: 200, data: "translated text" }
if (result.code === 200) {
return result.data;
} else {
throw new Error(`DeepLX 翻译失败: ${result.message || '未知错误'}`);
}
} else {
console.log("DeepLX 翻译失败:", resp);
throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): No error handling for invalid JSON responses.

Wrap resp.json() in a try-catch block to handle invalid JSON and provide a clearer error message.

Suggested change
if (resp.ok) {
let result = await resp.json();
// DeepLX 返回格式通常是 { code: 200, data: "translated text" }
if (result.code === 200) {
return result.data;
} else {
throw new Error(`DeepLX 翻译失败: ${result.message || '未知错误'}`);
}
} else {
console.log("DeepLX 翻译失败:", resp);
throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
}
}
if (resp.ok) {
let result;
try {
result = await resp.json();
} catch (err) {
throw new Error(`DeepLX 返回了无效的 JSON 响应: ${err instanceof Error ? err.message : String(err)}`);
}
// DeepLX 返回格式通常是 { code: 200, data: "translated text" }
if (result.code === 200) {
return result.data;
} else {
throw new Error(`DeepLX 翻译失败: ${result.message || '未知错误'}`);
}
} else {
console.log("DeepLX 翻译失败:", resp);
throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
}
}

Comment on lines +42 to +43
console.log("DeepLX 翻译失败:", resp);
throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Logging the entire Response object may not be informative.

Instead, log resp.status, resp.statusText, or the response body for clearer debugging information.

Suggested change
console.log("DeepLX 翻译失败:", resp);
throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${await resp.text()}`);
const errorBody = await resp.text();
console.log("DeepLX 翻译失败:", `status: ${resp.status}, statusText: ${resp.statusText}, body: ${errorBody}`);
throw new Error(`DeepLX 翻译失败: ${resp.status} ${resp.statusText} body: ${errorBody}`);

let sourceLang = config.from === 'auto' ? 'auto' : config.from;

// 判断是否使用代理或自定义URL
let url: string = config.proxy[config.service] ? config.proxy[config.service] : config.deeplx || 'http://localhost:1188/translate';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Avoid unneeded ternary statements (simplify-ternary)

Suggested change
let url: string = config.proxy[config.service] ? config.proxy[config.service] : config.deeplx || 'http://localhost:1188/translate';
let url: string = config.proxy[config.service] || (config.deeplx || 'http://localhost:1188/translate');


ExplanationIt is possible to simplify certain ternary statements into either use of an || or !.
This makes the code easier to read, since there is no conditional logic.

@lete114
Copy link
Copy Markdown

lete114 commented Jul 12, 2025

希望能尽快合并:)

@Bistutu
Copy link
Copy Markdown
Owner

Bistutu commented Jul 12, 2025

忘了看,sorry!!!

look like good!

@Bistutu Bistutu merged commit 7a66abe into Bistutu:main Jul 12, 2025
@hu3rror hu3rror deleted the add-deeplx branch September 27, 2025 01:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants