348 lines
8.9 KiB
Python
348 lines
8.9 KiB
Python
|
|
#!/usr/bin/python3
|
|||
|
|
import datetime
|
|||
|
|
import json
|
|||
|
|
import os
|
|||
|
|
import re
|
|||
|
|
import sys
|
|||
|
|
import time
|
|||
|
|
from typing import Dict
|
|||
|
|
|
|||
|
|
import requests
|
|||
|
|
|
|||
|
|
# 运行格式方式一:python langAzure.py
|
|||
|
|
# 运行格式方式二:python3 langAzure.py
|
|||
|
|
|
|||
|
|
# 语言文件路径
|
|||
|
|
path = "./resources/lang/"
|
|||
|
|
# 原语言
|
|||
|
|
SOURCE_LANG = 'en_US'
|
|||
|
|
|
|||
|
|
# 过滤不翻译的语言
|
|||
|
|
FILTER_LANG = ['zh_CN', 'en_US']
|
|||
|
|
|
|||
|
|
# Azure翻译语言映射,基于: https://docs.azure.cn/zh-cn/ai-services/translator/text-translation/reference/v3/translate
|
|||
|
|
# 语言代码:https://api.translator.azure.cn/languages?api-version=3.0&scope=translation
|
|||
|
|
AZURE_LANG_MAP = {
|
|||
|
|
# 'zh_CN': {
|
|||
|
|
# 'name': '简体中文',
|
|||
|
|
# 'code': 'zh-Hans'
|
|||
|
|
# },
|
|||
|
|
# 'zh_TW': {
|
|||
|
|
# 'name': '繁体中文',
|
|||
|
|
# 'code': 'zh-Hant',
|
|||
|
|
# },
|
|||
|
|
# 'zh_HK': {
|
|||
|
|
# 'name': '粤语(繁体)',
|
|||
|
|
# 'code': 'yue',
|
|||
|
|
# },
|
|||
|
|
# 'en_US': {
|
|||
|
|
# 'name': '英文',
|
|||
|
|
# 'code': 'en',
|
|||
|
|
# },
|
|||
|
|
# 'fr_FR': {
|
|||
|
|
# 'name': '法语',
|
|||
|
|
# 'code': 'fr',
|
|||
|
|
# },
|
|||
|
|
# 'ru_RU': {
|
|||
|
|
# 'name': '俄语',
|
|||
|
|
# 'code': 'ru',
|
|||
|
|
# },
|
|||
|
|
# 'de_DE': {
|
|||
|
|
# 'name': '德语',
|
|||
|
|
# 'code': 'de',
|
|||
|
|
# },
|
|||
|
|
# 'ja_JP': {
|
|||
|
|
# 'name': '日语',
|
|||
|
|
# 'code': 'ja',
|
|||
|
|
# },
|
|||
|
|
# 'ko_KR': {
|
|||
|
|
# 'name': '韩语',
|
|||
|
|
# 'code': 'ko',
|
|||
|
|
# },
|
|||
|
|
# 'it_IT': {
|
|||
|
|
# 'name': '意大利语',
|
|||
|
|
# 'code': 'it',
|
|||
|
|
# },
|
|||
|
|
# 'pt_PT': {
|
|||
|
|
# 'name': '葡萄牙语',
|
|||
|
|
# 'code': 'pt-PT',
|
|||
|
|
# },
|
|||
|
|
# 'es_ES': {
|
|||
|
|
# 'name': '西班牙语',
|
|||
|
|
# 'code': 'es',
|
|||
|
|
# },
|
|||
|
|
# 'ar_SA': {
|
|||
|
|
# 'name': '阿拉伯语',
|
|||
|
|
# 'code': 'ar',
|
|||
|
|
# },
|
|||
|
|
# 'vi_VN': {
|
|||
|
|
# 'name': '越南语',
|
|||
|
|
# 'code': 'vi',
|
|||
|
|
# },
|
|||
|
|
# 'ms_MY': {
|
|||
|
|
# 'name': '马来语',
|
|||
|
|
# 'code': 'ms',
|
|||
|
|
# },
|
|||
|
|
# 'nl_NL': {
|
|||
|
|
# 'name': '荷兰语',
|
|||
|
|
# 'code': 'nl',
|
|||
|
|
# },
|
|||
|
|
# 'ro_RO': {
|
|||
|
|
# 'name': '罗马尼亚语',
|
|||
|
|
# 'code': 'ro',
|
|||
|
|
# },
|
|||
|
|
# 'lt_LT': {
|
|||
|
|
# 'name': '立陶宛语',
|
|||
|
|
# 'code': 'lt',
|
|||
|
|
# },
|
|||
|
|
# 'sv_SE': {
|
|||
|
|
# 'name': '瑞典语',
|
|||
|
|
# 'code': 'sv',
|
|||
|
|
# },
|
|||
|
|
# 'et_EE': {
|
|||
|
|
# 'name': '爱沙尼亚语',
|
|||
|
|
# 'code': 'et',
|
|||
|
|
# },
|
|||
|
|
# 'pl_PL': {
|
|||
|
|
# 'name': '波兰语',
|
|||
|
|
# 'code': 'pl',
|
|||
|
|
# },
|
|||
|
|
# 'sk_SK': {
|
|||
|
|
# 'name': '斯洛伐克语',
|
|||
|
|
# 'code': 'sk',
|
|||
|
|
# },
|
|||
|
|
# 'cs_CZ': {
|
|||
|
|
# 'name': '捷克语',
|
|||
|
|
# 'code': 'cs',
|
|||
|
|
# },
|
|||
|
|
# 'el_GR': {
|
|||
|
|
# 'name': '希腊语',
|
|||
|
|
# 'code': 'el',
|
|||
|
|
# },
|
|||
|
|
# 'he_IL': {
|
|||
|
|
# 'name': '希伯来语',
|
|||
|
|
# 'code': 'he',
|
|||
|
|
# },
|
|||
|
|
# 'tr_TR': {
|
|||
|
|
# 'name': '土耳其语',
|
|||
|
|
# 'code': 'tr',
|
|||
|
|
# },
|
|||
|
|
# 'hu_HU': {
|
|||
|
|
# 'name': '匈牙利语',
|
|||
|
|
# 'code': 'hu',
|
|||
|
|
# },
|
|||
|
|
# 'bg_BG': {
|
|||
|
|
# 'name': '保加利亚语',
|
|||
|
|
# 'code': 'bg',
|
|||
|
|
# },
|
|||
|
|
# 'kk_KZ': {
|
|||
|
|
# 'name': '哈萨克语',
|
|||
|
|
# 'code': 'kk',
|
|||
|
|
# },
|
|||
|
|
# 'bn_BD': {
|
|||
|
|
# 'name': '孟加拉语',
|
|||
|
|
# 'code': 'bn',
|
|||
|
|
# },
|
|||
|
|
# 'hr_HR': {
|
|||
|
|
# 'name': '克罗地亚语',
|
|||
|
|
# 'code': 'hr',
|
|||
|
|
# },
|
|||
|
|
# 'th_TH': {
|
|||
|
|
# 'name': '泰语',
|
|||
|
|
# 'code': 'th',
|
|||
|
|
# },
|
|||
|
|
# 'id_ID': {
|
|||
|
|
# 'name': '印尼语',
|
|||
|
|
# 'code': 'id',
|
|||
|
|
# },
|
|||
|
|
# 'fi_FI': {
|
|||
|
|
# 'name': '芬兰语',
|
|||
|
|
# 'code': 'fi',
|
|||
|
|
# },
|
|||
|
|
# 'da_DK': {
|
|||
|
|
# 'name': '丹麦语',
|
|||
|
|
# 'code': 'da',
|
|||
|
|
# },
|
|||
|
|
# 'uk_UA': {
|
|||
|
|
# 'name': '乌克兰语',
|
|||
|
|
# 'code': 'uk',
|
|||
|
|
# },
|
|||
|
|
# 'sr_RS': {
|
|||
|
|
# 'name': '塞尔维亚语(西里尔)',
|
|||
|
|
# 'code': 'sr-Cyrl',
|
|||
|
|
# },
|
|||
|
|
# 'hi_IN': {
|
|||
|
|
# 'name': '印地语',
|
|||
|
|
# 'code': 'hi',
|
|||
|
|
# },
|
|||
|
|
# 'ur_PK': {
|
|||
|
|
# 'name': '乌尔都语',
|
|||
|
|
# 'code': 'ur',
|
|||
|
|
# },
|
|||
|
|
# 'hy_AM': {
|
|||
|
|
# 'name': '亚美尼亚语',
|
|||
|
|
# 'code': 'hy',
|
|||
|
|
# },
|
|||
|
|
# 'ka_GE': {
|
|||
|
|
# 'name': '格鲁吉亚语',
|
|||
|
|
# 'code': 'ka',
|
|||
|
|
# },
|
|||
|
|
'pt_BR': {
|
|||
|
|
'name': '葡萄牙语 (巴西)',
|
|||
|
|
'code': 'pt',
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# =============================== 以下为固定代码,非必要请勿修改 ===============================
|
|||
|
|
|
|||
|
|
# Azure配置
|
|||
|
|
AZURE_CONFIG = {
|
|||
|
|
'api_url': 'https://api.cognitive.microsofttranslator.com',
|
|||
|
|
'subscription_key': '5UGFXbyyyIlwtvFbl5HUeWeHffW9aKLNIiZlqWsRZuLaFSmXfxfzJQQJ99BGAC3pKaRXJ3w3AAAbACOGpL8p', # 替换为您的订阅密钥
|
|||
|
|
'region': 'eastasia', # 替换为您的区域,例如 'eastasia'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
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)
|
|||
|
|
|
|||
|
|
# 遍历AZURE_LANG_MAP
|
|||
|
|
for key in AZURE_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
|
|||
|
|
|
|||
|
|
# 检查key是否包含中文
|
|||
|
|
has_chinese = bool(re.search(r'[\u4e00-\u9fff]', en_us_key))
|
|||
|
|
text_to_translate = en_us_key if has_chinese else en_us_data[en_us_key]
|
|||
|
|
|
|||
|
|
print('语言:%s 正在翻译处理第%d/%d批数据 是否中文:%s' % (key, current, total, has_chinese))
|
|||
|
|
# 过滤已存在的key
|
|||
|
|
if en_us_key in datas:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 异常处理
|
|||
|
|
try:
|
|||
|
|
if has_chinese:
|
|||
|
|
# 从中文翻译到目标语言
|
|||
|
|
datas[en_us_key] = azure_translation('zh-Hans', AZURE_LANG_MAP[key]['code'], text_to_translate)
|
|||
|
|
else:
|
|||
|
|
# 从英文翻译到目标语言
|
|||
|
|
datas[en_us_key] = azure_translation(AZURE_LANG_MAP[SOURCE_LANG]['code'], AZURE_LANG_MAP[key]['code'], text_to_translate)
|
|||
|
|
|
|||
|
|
# 延迟1秒避免API限制
|
|||
|
|
#time.sleep(1)
|
|||
|
|
|
|||
|
|
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 azure_translation(source_language, target_language, text):
|
|||
|
|
"""
|
|||
|
|
使用Azure翻译API进行文本翻译
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
source_language: 源语言代码
|
|||
|
|
target_language: 目标语言代码
|
|||
|
|
text: 要翻译的文本
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
翻译后的文本
|
|||
|
|
"""
|
|||
|
|
# 构建API端点
|
|||
|
|
endpoint = f"{AZURE_CONFIG['api_url']}/translate"
|
|||
|
|
|
|||
|
|
# 设置参数
|
|||
|
|
params = {
|
|||
|
|
'api-version': '3.0',
|
|||
|
|
'from': source_language,
|
|||
|
|
'to': target_language
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 设置请求头
|
|||
|
|
headers = {
|
|||
|
|
'Ocp-Apim-Subscription-Key': AZURE_CONFIG['subscription_key'],
|
|||
|
|
'Ocp-Apim-Subscription-Region': AZURE_CONFIG['region'],
|
|||
|
|
'Content-type': 'application/json',
|
|||
|
|
'X-ClientTraceId': str(datetime.datetime.now().timestamp())
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 构建请求体
|
|||
|
|
body = [{'text': text}]
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 发送请求
|
|||
|
|
response = requests.post(endpoint, params=params, headers=headers, json=body)
|
|||
|
|
|
|||
|
|
# 检查响应状态
|
|||
|
|
if response.status_code != 200:
|
|||
|
|
raise Exception(f"Azure翻译API请求失败,状态码:{response.status_code},响应:{response.text}")
|
|||
|
|
|
|||
|
|
# 解析响应
|
|||
|
|
result = response.json()
|
|||
|
|
|
|||
|
|
# 提取翻译结果
|
|||
|
|
if result and len(result) > 0 and 'translations' in result[0]:
|
|||
|
|
return result[0]['translations'][0]['text']
|
|||
|
|
else:
|
|||
|
|
raise Exception(f"Azure翻译API响应格式异常:{result}")
|
|||
|
|
|
|||
|
|
except requests.exceptions.RequestException as e:
|
|||
|
|
raise Exception(f"Azure翻译API网络请求异常:{str(e)}")
|
|||
|
|
except json.JSONDecodeError as e:
|
|||
|
|
raise Exception(f"Azure翻译API响应JSON解析异常:{str(e)}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
# 创建文件
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
# 检查配置
|
|||
|
|
if AZURE_CONFIG['subscription_key'] == 'YOUR_SUBSCRIPTION_KEY':
|
|||
|
|
print("错误:请先在脚本中配置您的Azure订阅密钥")
|
|||
|
|
print("请修改AZURE_CONFIG中的subscription_key和region")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
main()
|