Files
voidraft/internal/common/translator/google_translator.go
2025-10-01 18:15:22 +08:00

280 lines
7.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package translator 提供文本翻译功能
package translator
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"golang.org/x/text/language"
)
// 错误定义
var (
ErrBadNetwork = errors.New("bad network, please check your internet connection")
)
// 常量定义
const (
googleTranslateTKK = "448487.932609646" // 固定TKK值
)
// GoogleTranslator 带token的Google翻译器使用translate.google.com
type GoogleTranslator struct {
httpClient *http.Client // HTTP客户端
Timeout time.Duration // 请求超时时间
languages map[string]LanguageInfo // 支持的语言列表
}
// GoogleTranslatorTokenFree 无token的Google翻译器使用translate.googleapis.com
type GoogleTranslatorTokenFree struct {
httpClient *http.Client // HTTP客户端
Timeout time.Duration // 请求超时时间
languages map[string]LanguageInfo // 支持的语言列表
}
// NewGoogleTranslator 创建一个新的带token的Google翻译器实例
func NewGoogleTranslator() *GoogleTranslator {
return &GoogleTranslator{
httpClient: &http.Client{Timeout: defaultTimeout},
Timeout: defaultTimeout,
languages: initGoogleLanguages(),
}
}
// initGoogleLanguages 初始化Google翻译器支持的语言列表
func initGoogleLanguages() map[string]LanguageInfo {
languages := make(map[string]LanguageInfo)
// 只支持三种语言
languages["en"] = LanguageInfo{Code: "en", Name: "English"}
languages["zh-cn"] = LanguageInfo{Code: "zh-CN", Name: "Chinese (Simplified)"}
languages["zh-tw"] = LanguageInfo{Code: "zh-TW", Name: "Chinese (Traditional)"}
return languages
}
// generateToken 生成翻译token
func generateToken(query string) string {
// 实现TypeScript中的token生成逻辑
tkkSplited := strings.Split(googleTranslateTKK, ".")
tkkIndex, _ := strconv.Atoi(tkkSplited[0])
tkkKey, _ := strconv.Atoi(tkkSplited[1])
// 转换查询字符串为字节数组
bytesArray := transformQuery(query)
// 计算hash
encodingRound := tkkIndex
for _, b := range bytesArray {
encodingRound += int(b)
encodingRound = shiftLeftOrRightThenSumOrXor(encodingRound, "+-a^+6")
}
encodingRound = shiftLeftOrRightThenSumOrXor(encodingRound, "+-3^+b+-f")
encodingRound ^= tkkKey
if encodingRound <= 0 {
encodingRound = (encodingRound & 2147483647) + 2147483648
}
normalizedResult := encodingRound % 1000000
return fmt.Sprintf("%d.%d", normalizedResult, normalizedResult^tkkIndex)
}
// transformQuery 转换查询字符串
func transformQuery(query string) []byte {
var bytesArray []byte
runes := []rune(query)
for i := 0; i < len(runes); i++ {
charCode := int(runes[i])
if charCode < 128 {
bytesArray = append(bytesArray, byte(charCode))
} else if charCode < 2048 {
bytesArray = append(bytesArray, byte((charCode>>6)|192))
bytesArray = append(bytesArray, byte((charCode&63)|128))
} else {
if (charCode&64512) == 55296 && i+1 < len(runes) && (int(runes[i+1])&64512) == 56320 {
charCode = 65536 + ((charCode & 1023) << 10) + (int(runes[i+1]) & 1023)
i++
bytesArray = append(bytesArray, byte((charCode>>18)|240))
bytesArray = append(bytesArray, byte(((charCode>>12)&63)|128))
} else {
bytesArray = append(bytesArray, byte((charCode>>12)|224))
}
bytesArray = append(bytesArray, byte(((charCode>>6)&63)|128))
bytesArray = append(bytesArray, byte((charCode&63)|128))
}
}
return bytesArray
}
// shiftLeftOrRightThenSumOrXor 位运算操作
func shiftLeftOrRightThenSumOrXor(num int, optString string) int {
for i := 0; i < len(optString)-2; i += 3 {
acc := int(optString[i+2])
if acc >= 'a' {
acc = acc - 87
} else {
acc = acc - '0'
}
if optString[i+1] == '+' {
acc = num >> uint(acc)
} else {
acc = num << uint(acc)
}
if optString[i] == '+' {
num = (num + acc) & 4294967295
} else {
num ^= acc
}
}
return num
}
// SetTimeout 设置请求超时时间
func (t *GoogleTranslator) SetTimeout(timeout time.Duration) {
t.Timeout = timeout
t.httpClient.Timeout = timeout
}
// Translate 使用Go语言提供的标准语言标签进行文本翻译
func (t *GoogleTranslator) Translate(text string, from language.Tag, to language.Tag) (string, error) {
return t.translate(text, from.String(), to.String())
}
// TranslateWithParams 使用简单字符串参数进行文本翻译
func (t *GoogleTranslator) TranslateWithParams(text string, params TranslationParams) (string, error) {
if params.Timeout > 0 {
t.SetTimeout(params.Timeout)
}
return t.translate(text, params.From, params.To)
}
// translate 执行实际翻译操作带token版本
func (t *GoogleTranslator) translate(text, from, to string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), t.Timeout)
defer cancel()
// 生成token
token := generateToken(text)
// 构建请求URL
apiURL := "https://translate.google.com/translate_a/single"
params := url.Values{}
params.Set("client", "t")
params.Set("sl", from)
params.Set("tl", to)
params.Set("hl", to)
params.Set("ie", "UTF-8")
params.Set("oe", "UTF-8")
params.Set("otf", "1")
params.Set("ssel", "0")
params.Set("tsel", "0")
params.Set("kc", "7")
params.Set("q", text)
params.Set("tk", token)
// 添加dt参数
dtParams := []string{"at", "bd", "ex", "ld", "md", "qca", "rw", "rm", "ss", "t"}
for _, dt := range dtParams {
params.Add("dt", dt)
}
fullURL := apiURL + "?" + params.Encode()
// 创建请求
req, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
if err != nil {
return "", err
}
// 发送请求
resp, err := t.httpClient.Do(req)
if err != nil {
return "", ErrBadNetwork
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("API error: status code %d", resp.StatusCode)
}
// 读取响应
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
// 解析JSON响应
var result []interface{}
if err := json.Unmarshal(body, &result); err != nil {
return "", err
}
if len(result) == 0 {
return "", errors.New("unexpected response format")
}
// 提取翻译文本
translations, ok := result[0].([]interface{})
if !ok {
return "", errors.New("unexpected response format")
}
var translatedText strings.Builder
for _, translation := range translations {
if chunk, ok := translation.([]interface{}); ok && len(chunk) > 0 {
if text, ok := chunk[0].(string); ok {
translatedText.WriteString(text)
}
}
}
return translatedText.String(), nil
}
// GetSupportedLanguages 获取翻译器支持的语言列表
func (t *GoogleTranslator) GetSupportedLanguages() map[string]LanguageInfo {
return t.languages
}
// IsLanguageSupported 检查指定的语言代码是否受支持
func (t *GoogleTranslator) IsLanguageSupported(languageCode string) bool {
_, ok := t.languages[strings.ToLower(languageCode)]
return ok
}
// visitArrayItems 递归访问数组项
func visitArrayItems(arr []interface{}, visitor func(interface{})) {
for _, obj := range arr {
if subArr, ok := obj.([]interface{}); ok {
visitArrayItems(subArr, visitor)
} else {
visitor(obj)
}
}
}
// GetSupportedLanguages 获取翻译器支持的语言列表
func (t *GoogleTranslatorTokenFree) GetSupportedLanguages() map[string]LanguageInfo {
return t.languages
}
// IsLanguageSupported 检查指定的语言代码是否受支持
func (t *GoogleTranslatorTokenFree) IsLanguageSupported(languageCode string) bool {
_, ok := t.languages[strings.ToLower(languageCode)]
return ok
}