#!/usr/bin/python3 import base64 import datetime import hashlib import hmac import json import os import sys import time import urllib.parse from typing import Dict import requests # 运行格式方式:python langAi.py youdao 备注:aliyun 阿里云 youdao 有道翻译 # 语言文件路径 /Users/mac/www/app-starlock/lan path = "./lan2/" # 原语言 SOURCE_LANG = 'lan_en' # 过滤不翻译的语言 FILTER_LANG = ['lan_zh', 'lan_en', 'lan_keys'] # 阿里云语言映射 ALIYUN_LANG_MAP = { 'lan_zh': { 'name': '中文', 'code': 'zh' }, 'lan_tw': { 'name': '繁体中文', 'code': 'zh-tw', }, 'lan_en': { 'name': '英文', 'code': 'en', }, 'lan_fr': { 'name': '法文', 'code': 'fr', }, 'lan_ru': { 'name': '俄文', 'code': 'ru', }, 'lan_de': { 'name': '德文', 'code': 'de', }, 'lan_ja': { 'name': '日文', 'code': 'ja', }, 'lan_ko': { 'name': '韩文', 'code': 'ko', }, 'lan_it': { 'name': '意大利文', 'code': 'it', }, 'lan_pt': { 'name': '葡萄牙文', 'code': 'pt', }, 'lan_es': { 'name': '西班牙文', 'code': 'es', }, 'lan_ar': { 'name': '阿拉伯文', 'code': 'ar', }, 'lan_vi': { 'name': '越南文', 'code': 'vi', }, 'lan_ms': { 'name': '马来文', 'code': 'ms', }, 'lan_nl': { 'name': '荷兰文', 'code': 'nl', }, 'lan_ro': { 'name': '罗马尼亚文', 'code': 'ro', }, 'lan_lt': { 'name': '立陶宛文', 'code': 'lt', }, 'lan_sv': { 'name': '瑞典文', 'code': 'sv', }, 'lan_et': { 'name': '爱沙尼亚文', 'code': 'et', }, 'lan_pl': { 'name': '波兰文', 'code': 'pl', }, 'lan_sk': { 'name': '斯洛伐克文', 'code': 'sk', }, 'lan_cs': { 'name': '捷克文', 'code': 'cs', }, 'lan_el': { 'name': '希腊文', 'code': 'el', }, 'lan_he': { 'name': '希伯来文', 'code': 'he', }, 'lan_tr': { 'name': '土耳其文', 'code': 'tr', }, 'lan_hu': { 'name': '匈牙利文', 'code': 'hu', }, 'lan_bg': { 'name': '保加利亚文', 'code': 'bg', }, 'lan_kk': { 'name': '哈萨克文', 'code': 'kk', }, 'lan_bn': { 'name': '孟加拉文', 'code': 'bn', }, 'lan_hr': { 'name': '克罗地亚文', 'code': 'hbs', }, 'lan_th': { 'name': '泰文', 'code': 'th', }, 'lan_id': { 'name': '印尼文', 'code': 'id', }, 'lan_fi': { 'name': '芬兰文', 'code': 'fi', }, 'lan_da': { 'name': '丹麦文', 'code': 'da', }, } # 有道语言映射 YOUDAO_LANG_MAP = { 'lan_zh': { 'name': '中文', 'code': 'zh-CHS', }, 'lan_tw': { 'name': '繁体中文(中国台湾)', 'code': 'zh-CHT', }, 'lan_hk': { 'name': '繁体中文(中国香港)', 'code': 'yue', }, 'lan_en': { 'name': '英文', 'code': 'en' }, 'lan_fr': { 'name': '法文', 'code': 'fr' }, 'lan_ru': { 'name': '俄文', 'code': 'ru' }, 'lan_de': { 'name': '德文', 'code': 'de' }, 'lan_ja': { 'name': '日文', 'code': 'ja' }, 'lan_ko': { 'name': '韩文', 'code': 'ko' }, 'lan_it': { 'name': '意大利文', 'code': 'it' }, 'lan_pt': { 'name': '葡萄牙文', 'code': 'pt' }, 'lan_es': { 'name': '西班牙文', 'code': 'es' }, 'lan_ar': { 'name': '阿拉伯文', 'code': 'ar' }, 'lan_vi': { 'name': '越南文', 'code': 'vi' }, 'lan_ms': { 'name': '马来文', 'code': 'ms' }, 'lan_nl': { 'name': '荷兰文', 'code': 'nl' }, 'lan_ro': { 'name': '罗马尼亚文', 'code': 'ro' }, 'lan_lt': { 'name': '立陶宛文', 'code': 'lt' }, 'lan_sv': { 'name': '瑞典文', 'code': 'sv' }, 'lan_et': { 'name': '爱沙尼亚文', 'code': 'et' }, 'lan_pl': { 'name': '波兰文', 'code': 'pl' }, 'lan_sk': { 'name': '斯洛伐克文', 'code': 'sk' }, 'lan_cs': { 'name': '捷克文', 'code': 'cs' }, 'lan_el': { 'name': '希腊文', 'code': 'el' }, 'lan_he': { 'name': '希伯来文', 'code': 'he' }, 'lan_tr': { 'name': '土耳其文', 'code': 'tr' }, 'lan_hu': { 'name': '匈牙利文', 'code': 'hu' }, 'lan_bg': { 'name': '保加利亚文', 'code': 'bg' }, 'lan_kk': { 'name': '哈萨克文', 'code': 'kk' }, 'lan_bn': { 'name': '孟加拉文', 'code': 'bn' }, 'lan_hr': { 'name': '克罗地亚文', 'code': 'hr' }, 'lan_th': { 'name': '泰文', 'code': 'th' }, 'lan_id': { 'name': '印尼文', 'code': 'id' }, 'lan_fi': { 'name': '芬兰文', 'code': 'fi' }, 'lan_da': { 'name': '丹麦文', 'code': 'da' }, 'lan_uk': { 'name': '乌克兰文', 'code': 'uk' }, 'lan_sr_cyrl': { 'name': '塞尔维亚语(西里尔文)', 'code': 'sr-Cyrl' }, 'lan_hy': { 'name': '亚美尼亚语', 'code': 'hy' }, 'lan_ka': { 'name': '格鲁吉亚语', 'code': 'ka' }, } # =============================== 以下为固定代码,非必要请勿修改 =============================== # 阿里云配置 ALIYUN_CONFIG = { 'domain': 'mt.cn-hangzhou.aliyuncs.com/api/translate/web/ecommerce', 'accessKeyId': 'LTAI5tFke4yGaY94ondXkmHn', 'accessKeySecret': '08zPw8BoRqfqlKDTMX65k7gfeER33x', } # 有道翻译配置 YOUDAO_CONFIG = { 'domain': 'https://openapi.youdao.com/api', 'appKey': '3e4923bad470f8d2', 'appSecret': '5thWwZ7D0RIzT0aquCF6NKmaiakAyhqr' } CHANNELS = ['aliyun', 'youdao'] if len(sys.argv) < 2: print('Usage: python langAi.py aliLang') sys.exit(1) # 检测传入参数是否合法 if sys.argv[1] not in CHANNELS: print('channel must be one of {}'.format(CHANNELS)) sys.exit(1) # 主函数 def main(channel): en_path = path + SOURCE_LANG + ".json" # 判断文件是否存在 if not os.path.exists(en_path): print('文件不存在:', en_path) sys.exit(1) # 读取文件内容 en_us_content = read_file(en_path) # 把json转数组 en_us_data = json.loads(en_us_content) # 语言列表 如果channel为aliyun赋值ALIYUN_LANG_MAP,否则赋值YOUDAO_LANG_MAP lang_map = ALIYUN_LANG_MAP if channel == 'aliyun' else YOUDAO_LANG_MAP # 遍历ALIYUN_LANG_MAP for key in lang_map: # 过滤掉不翻译的语言 if key in FILTER_LANG: continue lang_path = path + key + ".json" # 验证文件是否存在,如果不存在即创建 if not os.path.exists(lang_path): create_file(lang_path) data_content = read_file(lang_path) if data_content.strip(): # Check if the file is not empty datas = json.loads(data_content) else: datas = {} total = len(en_us_data) # 总数 current = 0 # 当前数 for en_us_key in en_us_data: current += 1 print('语言:%s-%s 正在翻译处理第%d/%d批数据' % (key,lang_map[key]['code'], current, total)) # 过滤已存在的key if en_us_key in datas: continue # 异常处理 try: datas[en_us_key] = translation(channel, lang_map[SOURCE_LANG]['code'], lang_map[key]['code'], en_us_data[en_us_key]) except Exception as e: print('翻译异常:', e.args, 'key:', en_us_key, 'content:', en_us_data[en_us_key]) continue # 写入文件 with open(lang_path, 'w', encoding='utf-8') as file: json.dump(datas, file, ensure_ascii=False, indent=4) # 翻译 def translation(channel, source_language, target_language, text): translation_text = '' if channel == 'aliyun': translation_text = aliyun_translation(source_language, target_language, text) elif channel == 'youdao': translation_text = youdao_translation(source_language, target_language, text) # 延迟2秒 time.sleep(2) return translation_text # 有道翻译 def youdao_translation(source_language, target_language, text): post_body = { 'q': text, 'from': source_language, 'to': target_language, 'appKey': YOUDAO_CONFIG['appKey'], 'salt': hashlib.md5(os.urandom(32)).hexdigest(), # 随机字符串,可使用UUID进行生产 'signType': 'v3', 'curtime': str(int(datetime.datetime.now().timestamp())), } # 签名生成方法如下: # signType=v3; # sign=sha256(应用ID+input+salt+curtime+应用密钥); # 其中,input的计算方式为:input=q前10个字符 + q长度 + q后10个字符(当q长度大于20)或 input=q字符串(当q长度小于等于20); input_text = post_body['q'] if len(input_text) > 20: input_text = input_text[:10] + str(len(input_text)) + input_text[-10:] post_body['input'] = input_text sign = hashlib.sha256((YOUDAO_CONFIG['appKey'] + input_text + post_body['salt'] + post_body['curtime'] + YOUDAO_CONFIG['appSecret']).encode('utf-8')).hexdigest() post_body['sign'] = sign req = fetch_content(YOUDAO_CONFIG['domain'], 'POST', post_body) req_json = json.loads(req) try: return req_json['translation'][0] except KeyError as e: print(f"[翻译异常][KeyError] 目标语言: {target_language}, key: {text}, API返回: {req_json}, 请求参数: {post_body}") raise except Exception as e: print(f"[翻译异常][OtherError] 目标语言: {target_language}, key: {text}, API返回: {req_json}, 请求参数: {post_body}, 异常: {e}") raise # 阿里云翻译 def aliyun_translation(source_language, target_language, text): headers = { 'Version': '2018-10-12', } post_body = { "FormatType": "text", "SourceLanguage": source_language, "TargetLanguage": target_language, "SourceText": text, "Ccene": "general", 'Action': 'TranslateGeneral', } req = aliyun_request(ALIYUN_CONFIG['domain'], {**post_body, **headers}) if req['Code'] == '200': return req['Data']['Translated'] else: raise Exception(req) def aliyun_request(domain: str, params: Dict, security: bool = True, method: str = 'POST') -> Dict: # Add common parameters api_params = { "SignatureMethod": "HMAC-SHA1", "SignatureNonce": hashlib.md5(os.urandom(32)).hexdigest(), "SignatureVersion": "1.0", "AccessKeyId": ALIYUN_CONFIG['accessKeyId'], "Timestamp": datetime.datetime.utcnow().isoformat("T"), "Format": "JSON", } api_params.update(params) # Sort parameters by key sorted_params = sorted(api_params.items(), key=lambda x: x[0]) sorted_query_string_tmp = "" for key, value in sorted(sorted_params): sorted_query_string_tmp += "&" + aliyun_encode(key) + "=" + aliyun_encode(value) string_to_sign = "{}&%2F&{}".format(method, aliyun_encode(sorted_query_string_tmp[1:])) # Calculate the signature signature = base64.b64encode( hmac.new((ALIYUN_CONFIG['accessKeySecret'] + "&").encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha1).digest()).decode( 'utf-8') # Add the signature to the parameters api_params['Signature'] = signature # Send the request url = ("https" if security else "http") + "://" + domain + "/" if method == 'POST': response = requests.post(url, data=api_params) else: response = requests.get(url, params=api_params) return response.json() # 阿里云编码 def aliyun_encode(value): res = urllib.parse.quote_plus(value) res = res.replace("+", "%20") res = res.replace("*", "%2A") res = res.replace("%7E", "~") return res def fetch_content(url, method, body): headers = {"x-sdk-client": "python/2.0.0"} if method == 'POST': response = requests.post(url, data=body, headers=headers) else: response = requests.get(url, params=body, headers=headers) if response.status_code != 200: raise Exception(f"Request failed with status {response.status_code}") return response.text # 创建文件 def create_file(file_path): with open(file_path, 'w') as file: pass # 读取文件内容 def read_file(file_path): with open(file_path, 'r', encoding='utf-8') as file: content = file.read() return content # translation_text = youdao_translation('en', 'de', 'Your application <:name> has been approved.') # print(translation_text) main(sys.argv[1])