Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
yxc0915 authored Aug 16, 2024
1 parent 130efe9 commit 49cff9a
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 0 deletions.
56 changes: 56 additions & 0 deletions core/api_caller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import aiohttp
from aiohttp_socks import ProxyConnector, ProxyType
import asyncio
from yarl import URL
from core.config_loader import load_config

def get_proxy_connector(proxy_url):
if not proxy_url:
return None

url = URL(proxy_url)
if url.scheme in ['http', 'https']:
return ProxyConnector.from_url(proxy_url)
elif url.scheme == 'socks5':
return ProxyConnector(
proxy_type=ProxyType.SOCKS5,
host=url.host,
port=url.port
)
else:
raise ValueError(f"Unsupported proxy scheme: {url.scheme}")

async def call_ai_api_async(api_name, model_name, prompt, max_retries=3, retry_delay=5):
config = load_config()
api_config = config['apis'][api_name]
proxy_url = config.get('proxy', {}).get('url')

headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_config['api_key']}"
}

data = {
"model": model_name,
"messages": [
{"role": "system", "content": "你是一位专业的我的世界插件配置文件翻译专家,你正在翻译配置文件中带#的注释部分内容,你只需要输出翻译后的内容,不要附上自己的猜想。如果遇到带有#的RGB颜色代码,请不要翻译,直接返回原内容。注意纯大写的文本如 LOWEST, LOW, NORMAL, HIGH, HIGHEST,请不要翻译,它们可能是供参考的配置选项。"},
{"role": "user", "content": prompt}
]
}

connector = get_proxy_connector(proxy_url)

async with aiohttp.ClientSession(connector=connector) as session:
for attempt in range(max_retries):
try:
async with session.post(api_config['base_url'], headers=headers, json=data, timeout=30) as response:
response.raise_for_status()
result = await response.json()
return result['choices'][0]['message']['content'].strip()
except aiohttp.ClientError as e:
if attempt < max_retries - 1:
print(f"API 调用失败 (尝试 {attempt + 1}/{max_retries}): {str(e)}. 正在重试...")
await asyncio.sleep(retry_delay)
else:
print(f"API 调用失败 (尝试 {attempt + 1}/{max_retries}): {str(e)}. 已达到最大重试次数。")
return None
12 changes: 12 additions & 0 deletions core/comment_extractor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
def extract_comments(file_content):
comments = []
for i, line in enumerate(file_content.split('\n'), 1):
if '#' in line:
before_comment, comment = line.split('#', 1)
comment = comment.strip()
if comment:
comments.append({
"original_text": comment,
"location": {"line": i, "before_comment": before_comment}
})
return comments
6 changes: 6 additions & 0 deletions core/config_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import yaml

def load_config():
with open('config.yml', 'r', encoding='utf-8') as file:
config = yaml.safe_load(file)
return config
38 changes: 38 additions & 0 deletions core/file_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import json

def save_json(data, filename):
"""
将数据保存为 JSON 文件
"""
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)

def merge_translations(file_content, translated_comments, chinese_only=False):
"""
将翻译后的注释合并回原始文件内容
:param file_content: 原始文件内容
:param translated_comments: 翻译后的注释列表
:param chinese_only: 是否只保留中文翻译
:return: 合并后的文件内容
"""
lines = file_content.split('\n')
for comment in translated_comments:
line_num = comment["location"]["line"] - 1
before_comment = comment["location"]["before_comment"]
original = comment["original_text"]
translation = comment["translated_text"]

if chinese_only:
lines[line_num] = f"{before_comment}# {translation}"
else:
lines[line_num] = f"{before_comment}# {original} | {translation}"

return '\n'.join(lines)

def load_json(filename):
"""
从 JSON 文件加载数据
"""
with open(filename, 'r', encoding='utf-8') as f:
return json.load(f)
113 changes: 113 additions & 0 deletions core/translator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import asyncio
from core.api_caller import call_ai_api_async
from core.config_loader import load_config
import logging
import sys

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', stream=sys.stdout)
logger = logging.getLogger(__name__)

def format_ai_output(output):
"""
将 AI 输出的多行内容合并为单行,使用 ' | ' 作为分隔符。
"""
return ' | '.join(line.strip() for line in output.split('\n') if line.strip())

async def translate_comment_async(comment, api_name, model_name, target_lang):
prompt = f"请将以下我的世界插件配置文件注释翻译成{target_lang}{comment['original_text']}"
translation = await call_ai_api_async(api_name, model_name, prompt)
formatted_translation = format_ai_output(translation) if translation else "翻译失败"
return {
"id": comment.get("id"),
"original_text": comment["original_text"],
"translated_text": formatted_translation,
"location": comment["location"]
}

async def translate_batch_async(comments, api_name, model_name, target_lang, batch_size):
tasks = []
for comment in comments:
task = asyncio.create_task(translate_comment_async(comment, api_name, model_name, target_lang))
tasks.append(task)

if len(tasks) >= batch_size:
batch_results = await asyncio.gather(*tasks, return_exceptions=True)
for result in batch_results:
if isinstance(result, Exception):
logger.error(f"Translation failed: {str(result)}")
yield None
else:
yield result
tasks = []

if tasks:
batch_results = await asyncio.gather(*tasks, return_exceptions=True)
for result in batch_results:
if isinstance(result, Exception):
logger.error(f"Translation failed: {str(result)}")
yield None
else:
yield result

async def process_translations(comments, api_name, model_name, target_lang, batch_size, progress_bar, status_text):
translated_comments = []
retry_comments = []
total_comments = len(comments)

async for translated_comment in translate_batch_async(comments, api_name, model_name, target_lang, batch_size):
if translated_comment is None:
retry_comments.append(comments[len(translated_comments)])
else:
translated_comments.append(translated_comment)

progress = len(translated_comments) / total_comments
if progress_bar:
progress_bar.progress(progress)
if status_text:
status_text.text(f"已翻译 {len(translated_comments)}/{total_comments} 条注释 (进度: {progress:.2%})")
logger.info(f"Translated {len(translated_comments)}/{total_comments} comments (Progress: {progress:.2%})")

return translated_comments, retry_comments

def translate_comments(comments, api_name, model_name, target_lang, progress_bar=None, status_text=None, batch_size=10, max_retries=3):
config = load_config()
all_translated_comments = []
remaining_comments = comments
total_comments = len(comments)

for attempt in range(max_retries):
if not remaining_comments:
break

logger.info(f"Translation attempt {attempt + 1}/{max_retries}")
if status_text:
status_text.text(f"翻译尝试 {attempt + 1}/{max_retries}")

async def run_translation():
nonlocal all_translated_comments, remaining_comments
translated, to_retry = await process_translations(remaining_comments, api_name, model_name, target_lang, batch_size, progress_bar, status_text)
all_translated_comments.extend(translated)
remaining_comments = to_retry

asyncio.run(run_translation())

if remaining_comments:
logger.info(f"{len(remaining_comments)} comments failed to translate. Retrying...")
if status_text:
status_text.text(f"重新连接中... 剩余 {len(remaining_comments)} 条注释待翻译")
asyncio.run(asyncio.sleep(5)) # 等待5秒后重试

if remaining_comments:
logger.warning(f"Failed to translate {len(remaining_comments)} comments after {max_retries} attempts")
if status_text:
status_text.text(f"警告:{len(remaining_comments)} 条注释翻译失败")

# 确保进度条显示100%完成
if progress_bar:
progress_bar.progress(1.0)
if status_text:
status_text.text(f"翻译完成: {len(all_translated_comments)}/{total_comments} 条注释已翻译")

logger.info(f"Translation completed. {len(all_translated_comments)}/{total_comments} comments translated.")

return all_translated_comments

0 comments on commit 49cff9a

Please sign in to comment.