翻译模块

This commit is contained in:
zhongluofeng
2026-09-17 17:32:20 +08:00
parent 79c43d5340
commit f6c1cc250e
47 changed files with 10822 additions and 104 deletions
+275
View File
@@ -0,0 +1,275 @@
/**
* 翻译模块类型定义。
*
* 与 Rust 侧 `src-tauri/src/translate/` 的 specta 类型一一对应(字段名 camelCase)。
*
* 为什么手写而非直接引用 `@/lib/bindings`specta 的绑定只在 **debug 构建启动时**
* 重新生成(见 `lib.rs::export_bindings`),新增命令后需先以 `bun run tauri dev`
* 跑一次才会出现在 bindings.ts 里。在那之前若直接写 `commands.translateRun`
* `vue-tsc` 会因属性不存在而报错。因此 P0 先用原生 `invoke` + 本文件的类型,
* 待首次 dev 启动生成绑定后,可平滑替换为 `commands.*`(调用点集中在 translateStore)。
*/
/** 引擎类型:AI(OpenAI 兼容)| 免密钥网络源 | 需签名的云厂商 */
export type EngineKind = 'ai' | 'free' | 'cloud'
/** 错误分类。前端据此决定给出「改 Key」「查网络」还是「稍后重试」的提示 */
export type TranslateErrorKind =
| 'config'
| 'auth'
| 'rateLimit'
| 'network'
| 'timeout'
| 'parse'
| 'unsupported'
| 'empty'
| 'captcha'
| 'unknown'
export interface TranslateError {
kind: TranslateErrorKind
message: string
detail: string | null
}
export interface TokenUsage {
promptTokens: number
completionTokens: number
totalTokens: number
}
export interface TranslateResult {
text: string
detected: string | null
engineId: string
engineName: string
latencyMs: number
usage: TokenUsage | null
}
export interface PromptTemplates {
translate: string
polish: string
explain: string
summarize: string
}
export interface TranslateEngineConfig {
id: string
name: string
kind: EngineKind
preset: string
enabled: boolean
priority: number
baseUrl: string
model: string
temperature: number
maxTokens: number
timeoutMs: number
systemPrompt: string
extraBody: string | null
/** 是否支持图像输入(截图翻译的视觉直译模式只发给勾选了此项的引擎) */
supportsVision: boolean
}
export interface SelectionSettings {
enabled: boolean
/** 打开翻译面板的全局快捷键(默认 Ctrl+2) */
panelShortcut: string
mode: string
restoreClipboard: boolean
maxChars: number
blacklist: string[]
}
export interface PopupSettings {
fontSize: number
opacity: number
positionPreference: string
showOriginal: boolean
}
export interface ScreenshotSettings {
ocrMode: string
ocrLang: string
autoTranslate: boolean
}
export interface HistorySettings {
enabled: boolean
maxItems: number
}
export interface TranslateSettings {
version: number
defaultTarget: string
defaultEngineId: string
autoFallback: boolean
useProxy: boolean
engines: TranslateEngineConfig[]
selection: SelectionSettings
popup: PopupSettings
screenshot: ScreenshotSettings
history: HistorySettings
promptTemplates: PromptTemplates
}
/** 引擎实例的前端视图:配置 + 后端派生的密钥/可用性状态 */
export interface EngineView {
config: TranslateEngineConfig
hasApiKey: boolean
apiKeyMasked: string
ready: boolean
issue: string | null
}
/** 连通性自检结果(失败也是 Ok 返回,细节在 message/detail 里) */
export interface EngineTestResult {
ok: boolean
latencyMs: number
message: string
errorKind: TranslateErrorKind | null
detail: string | null
}
export interface TranslateRunParams {
text: string
from?: string | null
to?: string | null
toLabel?: string | null
fromLabel?: string | null
engineId?: string | null
mode?: string | null
/** 记入历史时的来源:"manual" | "selection" | "clipboard" | "screenshot" */
via?: string | null
/** 是否写入历史,默认 true;对比 / 预览必须传 false */
record?: boolean | null
}
/** 一条翻译历史记录(与 Rust translate::history::HistoryItem 对应) */
export interface HistoryItem {
id: number
ts: number
fromLang: string
toLang: string
engineId: string
engineName: string
sourceText: string
resultText: string
via: string
favorited: boolean
latencyMs: number
}
/** 引擎预设:用于「新增引擎」时给出可直接用的起点 */
export interface EnginePreset {
key: string
label: string
description: string
template: Partial<TranslateEngineConfig>
}
export const ENGINE_PRESETS: EnginePreset[] = [
{
key: 'deepseek',
label: 'DeepSeek',
description: 'OpenAI 兼容 · 默认引擎,翻译场景用非思考模式,低延迟低成本',
template: {
kind: 'ai',
baseUrl: 'https://api.deepseek.com',
model: 'deepseek-flash',
temperature: 0.3,
maxTokens: 4096,
timeoutMs: 30000
}
},
{
key: 'openai',
label: 'OpenAI',
description: 'OpenAI 兼容 · 官方端点',
template: {
kind: 'ai',
baseUrl: 'https://api.openai.com/v1',
model: 'gpt-4o-mini',
temperature: 0.3,
maxTokens: 4096,
timeoutMs: 30000
}
},
{
key: 'ollama',
label: 'Ollama(本地)',
description: '本地推理,数据不出机器;需先在 Ollama 中拉取模型',
template: {
kind: 'ai',
baseUrl: 'http://localhost:11434/v1',
model: 'qwen2.5:7b',
temperature: 0.3,
maxTokens: 4096,
timeoutMs: 120000
}
},
{
key: 'proxy-gateway',
label: '中转 / one-api',
description: '任意 OpenAI 兼容中转服务,自行填写 Base URL 与模型名',
template: {
kind: 'ai',
baseUrl: '',
model: '',
temperature: 0.3,
maxTokens: 4096,
timeoutMs: 30000
}
},
{
key: 'deepl',
label: 'DeepL(云厂商)',
description: '质量口碑最好的传统机翻之一。免费 Key 用 api-free 端点,目标语言仅简体中文',
template: {
kind: 'cloud',
preset: 'deepl',
baseUrl: 'https://api-free.deepl.com/v2',
model: '',
timeoutMs: 20000
}
},
{
key: 'libretranslate',
label: 'LibreTranslate(自部署)',
description: '开源自托管翻译服务。需自行填写服务地址;API Key 可选(部署启用密钥校验时需要)',
template: {
kind: 'free',
preset: 'libretranslate',
baseUrl: '',
model: '',
timeoutMs: 20000
}
},
{
key: 'mymemory',
label: 'MyMemory(免密钥)',
description: '国内可直接访问的免密兜底。需显式指定源语言,单次约 500 字节内有免费额度',
template: {
kind: 'free',
preset: 'mymemory',
baseUrl: '',
model: '',
timeoutMs: 15000
}
}
]
/** 错误分类 → 用户可执行的动作提示 */
export const ERROR_HINTS: Record<TranslateErrorKind, string> = {
config: '检查引擎配置:Base URL、模型名是否填对',
auth: '检查 API Key 是否正确、账户是否有该模型权限',
rateLimit: '上游限流,稍后重试即可',
network: '网络不可达:检查网络连接,若该服务在境外请开启代理模块',
timeout: '请求超时:可调大该引擎的超时时间',
parse: '上游返回格式异常,可能是接口变更',
unsupported: '该引擎类型尚未接入',
empty: '没有可翻译的内容:请先输入文本',
captcha: '被上游风控拦截,稍后重试或更换引擎',
unknown: '未知错误,详见下方原始响应'
}