File size: 6,311 Bytes
96c8569
 
20f1d29
96c8569
efcb14c
0535e28
20f1d29
 
 
 
7e19594
0599754
20f1d29
 
 
 
 
96c8569
 
 
 
 
20f1d29
 
 
96c8569
 
20f1d29
 
7e19594
0535e28
20f1d29
 
 
 
 
 
 
 
 
 
7e19594
0535e28
20f1d29
 
 
0599754
7e19594
20f1d29
 
 
 
 
 
 
 
96c8569
 
7e19594
96c8569
 
20f1d29
 
 
 
7e19594
96c8569
20f1d29
0535e28
500dfe6
20f1d29
 
7e19594
20f1d29
 
efcb14c
20f1d29
 
 
 
7e19594
0535e28
7e19594
96c8569
7e19594
96c8569
 
20f1d29
 
 
 
0535e28
96c8569
20f1d29
96c8569
 
20f1d29
7e19594
 
 
20f1d29
 
 
 
96c8569
 
 
20f1d29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
efcb14c
20f1d29
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import hashlib, hmac, json, os, random, time
from datetime import datetime
from typing import List, Sequence, Optional, Dict, Any

import requests

TRANSLATOR_ACCESS_KEY = os.environ.get("TRANSLATOR_ACCESS_KEY")

TENCENT_SECRET_ID = os.environ.get("TENCENT_SECRET_ID")
TENCENT_SECRET_KEY = os.environ.get("TENCENT_SECRET_KEY")
TENCENT_TRANSLATE_URL = os.environ.get("TENCENT_TRANSLATE_URL", "https://tmt.tencentcloudapi.com")

BAIDU_TRANSLATE_URL = os.environ.get("BAIDU_TRANSLATE_URL", "https://fanyi-api.baidu.com/api/trans/vip/translate")
try:
    BAIDU_CREDENTIALS_DEFAULT = json.loads(os.environ.get("BAIDU_CREDENTIALS_JSON", "[]"))
except json.JSONDecodeError:
    BAIDU_CREDENTIALS_DEFAULT = []

def _sign(key: bytes, msg: str) -> bytes:
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()

def _tc3_signature(secret_key: str, date: str, service: str, string_to_sign: str) -> str:
    secret_date = _sign(("TC3" + secret_key).encode(), date)
    secret_service = _sign(secret_date, service)
    secret_signing = _sign(secret_service, "tc3_request")
    return hmac.new(secret_signing, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()

def _translate_with_tencent(texts: Sequence[str], src: str, tgt: str, secret_id: str, secret_key: str) -> Optional[List[str]]:
    if not (secret_id and secret_key): return None

    service, host, action, version, region = "tmt", "tmt.tencentcloudapi.com", "TextTranslate", "2018-03-21", "ap-beijing"
    ts = int(time.time())
    date = datetime.utcfromtimestamp(ts).strftime("%Y-%m-%d")
    
    payload = {"SourceText": "\n".join(texts), "Source": src, "Target": tgt, "ProjectId": 0}
    payload_str = json.dumps(payload, ensure_ascii=False)
    
    canonical_request = "POST\n/\n\n" + \
                        f"content-type:application/json; charset=utf-8\nhost:{host}\nx-tc-action:{action.lower()}\n\n" + \
                        "content-type;host;x-tc-action\n" + \
                        hashlib.sha256(payload_str.encode()).hexdigest()
    
    credential_scope = f"{date}/{service}/tc3_request"
    string_to_sign = "TC3-HMAC-SHA256\n" + str(ts) + "\n" + credential_scope + "\n" + \
                     hashlib.sha256(canonical_request.encode()).hexdigest()
    
    signature = _tc3_signature(secret_key, date, service, string_to_sign)
    
    authorization = f"TC3-HMAC-SHA256 Credential={secret_id}/{credential_scope}, " + \
                    f"SignedHeaders=content-type;host;x-tc-action, Signature={signature}"
    
    headers = {
        "Authorization": authorization, "Content-Type": "application/json; charset=utf-8",
        "Host": host, "X-TC-Action": action, "X-TC-Timestamp": str(ts),
        "X-TC-Version": version, "X-TC-Region": region,
    }

    try:
        resp = requests.post(TENCENT_TRANSLATE_URL, headers=headers, data=payload_str, timeout=8)
        resp.raise_for_status()
        data = resp.json()
        if "Response" in data and "TargetText" in data["Response"]:
            return data["Response"]["TargetText"].split("\n")
        else:
            print(f"[translator] Tencent API abnormal response: {data}")
            return None
    except Exception as e:
        print(f"[translator] Tencent API error β†’ {e}")
        return None

def _translate_with_baidu(texts: Sequence[str], src: str, tgt: str, baidu_credentials: List[Dict[str, str]]) -> Optional[List[str]]:
    if not baidu_credentials: return None

    cred = random.choice(baidu_credentials)
    app_id, secret_key = cred.get("app_id"), cred.get("secret_key")

    if not (app_id and secret_key): return None
    
    salt = random.randint(32768, 65536)
    query = "\n".join(texts)
    sign = hashlib.md5((app_id + query + str(salt) + secret_key).encode()).hexdigest()
    params = {"q": query, "from": src, "to": tgt, "appid": app_id, "salt": salt, "sign": sign}

    try:
        resp = requests.get(BAIDU_TRANSLATE_URL, params=params, timeout=8)
        resp.raise_for_status()
        data = resp.json()
        if "trans_result" in data:
            return [item["dst"] for item in data["trans_result"]]
        else:
            print(f"[translator] Baidu API abnormal response: {data}")
            return None
    except Exception as e:
        print(f"[translator] Baidu API error β†’ {e}")
        return None


def translate_texts(texts: Sequence[str],
                    src_lang: str = "auto",
                    tgt_lang: str = "zh",
                    system_key_input: Optional[str] = None,
                    tencent_id: Optional[str] = None,
                    tencent_key: Optional[str] = None,
                    baidu_creds_json_str: Optional[str] = None) -> List[str]:
    if not texts:
        return []

    use_tencent_id, use_tencent_key = None, None
    use_baidu_creds = []

    if tencent_id and tencent_key:
        use_tencent_id, use_tencent_key = tencent_id, tencent_key
        print("[translator] Using custom Tencent API key.")
    if baidu_creds_json_str:
        try:
            creds = json.loads(baidu_creds_json_str)
            if isinstance(creds, list) and all(isinstance(d, dict) for d in creds):
                use_baidu_creds = creds
                print("[translator] Using custom Baidu API key(s).")
            else:
                 print("[translator] Warning: Custom Baidu credentials format is incorrect.")
        except json.JSONDecodeError:
            print("[translator] Warning: Failed to parse custom Baidu credentials JSON.")

    elif TRANSLATOR_ACCESS_KEY and system_key_input == TRANSLATOR_ACCESS_KEY:
        print("[translator] System access key validated. Using system-configured API keys.")
        use_tencent_id, use_tencent_key = TENCENT_SECRET_ID, TENCENT_SECRET_KEY
        use_baidu_creds = BAIDU_CREDENTIALS_DEFAULT
    
    else:
        print("[translator] Translation disabled: No valid API keys or system key provided.")
        return list(texts)

    translated_texts = None
    if use_tencent_id and use_tencent_key:
        translated_texts = _translate_with_tencent(texts, src_lang, tgt_lang, use_tencent_id, use_tencent_key)
    
    if translated_texts is None and use_baidu_creds:
        translated_texts = _translate_with_baidu(texts, src_lang, tgt_lang, use_baidu_creds)

    return translated_texts or list(texts)