Create deepl.go

This commit is contained in:
xxnuo
2025-11-17 00:58:18 +08:00
parent 348f188c4b
commit cf81cacad2

249
internal/handlers/deepl.go Normal file
View File

@@ -0,0 +1,249 @@
package handlers
import (
"context"
"fmt"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/xxnuo/MTranServer/internal/services"
)
// deeplLangToBCP47 DeepL 语言代码转 BCP47
var deeplLangToBCP47 = map[string]string{
// Source languages
"AR": "ar",
"BG": "bg",
"CS": "cs",
"DA": "da",
"DE": "de",
"EL": "el",
"EN": "en",
"ES": "es",
"ET": "et",
"FI": "fi",
"FR": "fr",
"HU": "hu",
"ID": "id",
"IT": "it",
"JA": "ja",
"KO": "ko",
"LT": "lt",
"LV": "lv",
"NB": "no",
"NL": "nl",
"PL": "pl",
"PT": "pt",
"RO": "ro",
"RU": "ru",
"SK": "sk",
"SL": "sl",
"SV": "sv",
"TR": "tr",
"UK": "uk",
"ZH": "zh-Hans",
// Target languages with variants
"EN-GB": "en-GB",
"EN-US": "en-US",
"PT-BR": "pt-BR",
"PT-PT": "pt-PT",
"ZH-CN": "zh-Hans",
"ZH-TW": "zh-Hant",
}
// bcp47ToDeeplLang BCP47 转 DeepL 语言代码
var bcp47ToDeeplLang = map[string]string{
"ar": "AR",
"bg": "BG",
"cs": "CS",
"da": "DA",
"de": "DE",
"el": "EL",
"en": "EN",
"en-GB": "EN-GB",
"en-US": "EN-US",
"es": "ES",
"et": "ET",
"fi": "FI",
"fr": "FR",
"hu": "HU",
"id": "ID",
"it": "IT",
"ja": "JA",
"ko": "KO",
"lt": "LT",
"lv": "LV",
"no": "NB",
"nl": "NL",
"pl": "PL",
"pt": "PT",
"pt-BR": "PT-BR",
"pt-PT": "PT-PT",
"ro": "RO",
"ru": "RU",
"sk": "SK",
"sl": "SL",
"sv": "SV",
"tr": "TR",
"uk": "UK",
"zh-Hans": "ZH",
"zh-CN": "ZH-CN",
"zh-Hant": "ZH-TW",
"zh-TW": "ZH-TW",
}
// convertDeeplLangToBCP47 将 DeepL 语言代码转换为 BCP47
func convertDeeplLangToBCP47(deeplLang string) string {
// 转换为大写进行匹配
upperLang := strings.ToUpper(deeplLang)
if bcp47, ok := deeplLangToBCP47[upperLang]; ok {
return bcp47
}
// 如果不在映射表中,返回小写版本
return strings.ToLower(deeplLang)
}
// convertBCP47ToDeeplLang 将 BCP47 语言代码转换为 DeepL
func convertBCP47ToDeeplLang(bcp47Lang string) string {
if deeplLang, ok := bcp47ToDeeplLang[bcp47Lang]; ok {
return deeplLang
}
// 如果不在映射表中,返回大写版本
return strings.ToUpper(bcp47Lang)
}
// DeeplTranslateRequest DeepL 翻译请求
type DeeplTranslateRequest struct {
Text []string `json:"text" binding:"required" example:"Hello, world!"`
SourceLang string `json:"source_lang,omitempty" example:"EN"`
TargetLang string `json:"target_lang" binding:"required" example:"DE"`
SplitSentences string `json:"split_sentences,omitempty" example:"1"`
PreserveFormatting string `json:"preserve_formatting,omitempty" example:"0"`
Formality string `json:"formality,omitempty" example:"default"`
GlossaryID string `json:"glossary_id,omitempty"`
TagHandling string `json:"tag_handling,omitempty" example:"xml"`
OutlineDetection string `json:"outline_detection,omitempty" example:"1"`
NonSplittingTags []string `json:"non_splitting_tags,omitempty"`
SplittingTags []string `json:"splitting_tags,omitempty"`
IgnoreTags []string `json:"ignore_tags,omitempty"`
ModelType string `json:"model_type,omitempty" example:"quality_optimized"`
Context string `json:"context,omitempty"`
EnableBetaLanguages bool `json:"enable_beta_languages,omitempty"`
}
// DeeplTranslation 翻译结果
type DeeplTranslation struct {
DetectedSourceLanguage string `json:"detected_source_language" example:"EN"`
Text string `json:"text" example:"Hallo, Welt!"`
}
// DeeplTranslateResponse DeepL 翻译响应
type DeeplTranslateResponse struct {
Translations []DeeplTranslation `json:"translations"`
}
// HandleDeeplTranslate DeepL 翻译兼容接口
// @Summary DeepL 翻译兼容接口
// @Description 兼容 DeepL API v2 的翻译接口
// @Tags 插件
// @Accept json
// @Produce json
// @Param token query string false "API Token"
// @Param request body DeeplTranslateRequest true "DeepL 翻译请求"
// @Success 200 {object} DeeplTranslateResponse
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /deepl [post]
func HandleDeeplTranslate(apiToken string) gin.HandlerFunc {
return func(c *gin.Context) {
// 检查 token - 兼容 DeepL API 认证方式
if apiToken != "" {
// 支持 DeepL 标准认证: Authorization: DeepL-Auth-Key [key]
authHeader := c.GetHeader("Authorization")
token := ""
if strings.HasPrefix(authHeader, "DeepL-Auth-Key ") {
token = strings.TrimPrefix(authHeader, "DeepL-Auth-Key ")
} else if authHeader != "" {
// 也支持标准 Bearer token
token = strings.TrimPrefix(authHeader, "Bearer ")
} else {
// 兼容 query 参数方式
token = c.Query("token")
}
if token != apiToken {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "Unauthorized",
})
return
}
}
var req DeeplTranslateRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
// 转换语言代码DeepL -> BCP47
sourceLang := "auto"
if req.SourceLang != "" {
sourceLang = convertDeeplLangToBCP47(req.SourceLang)
}
targetLang := convertDeeplLangToBCP47(req.TargetLang)
// 获取或创建翻译引擎
m, err := services.GetOrCreateEngine(sourceLang, targetLang)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get engine: %v", err),
})
return
}
// 批量翻译
translations := make([]DeeplTranslation, len(req.Text))
ctx, cancel := context.WithTimeout(c.Request.Context(), 60*time.Second)
defer cancel()
// 确定是否需要 HTML 处理
isHTML := req.TagHandling == "html" || req.TagHandling == "xml"
for i, text := range req.Text {
var result string
if isHTML {
result, err = m.TranslateHTML(ctx, text)
} else {
result, err = m.Translate(ctx, text)
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Translation failed at index %d: %v", i, err),
})
return
}
// 返回 DeepL 格式的语言代码
detectedLang := req.SourceLang
if detectedLang == "" {
detectedLang = convertBCP47ToDeeplLang(sourceLang)
}
translations[i] = DeeplTranslation{
DetectedSourceLanguage: detectedLang,
Text: result,
}
}
c.JSON(http.StatusOK, DeeplTranslateResponse{
Translations: translations,
})
}
}