Files

195 lines
7.6 KiB
Python

"""
翻译处理模块:自动识别外文文章,通过AI并发生成中文翻译
"""
import re
import json
from concurrent.futures import ThreadPoolExecutor, as_completed
from .utils import is_chinese, call_ai
from .config import AI_CONCURRENCY
from .logger import get_logger
def translate_title(article, api_key, base_url, model, index=0, total=0):
"""仅翻译外文文章的标题,返回更新后的文章"""
logger = get_logger()
try:
title = article['title']
prompt = f"""请将以下军事相关的英文标题翻译成专业中文,仅返回翻译结果,不要其他内容:
{title}"""
result = call_ai([
{'role': 'system', 'content': '你是专业的军事科技翻译专家,翻译准确、专业、简洁,只返回翻译结果。'},
{'role': 'user', 'content': prompt}
], api_key, base_url, model, temperature=0.2, max_tokens=200, purpose="标题翻译")
article['translated_title'] = result.strip()
if index > 0 and total > 0:
logger.info("标题翻译完成 %d/%d: %s", index, total, article['translated_title'][:30])
return article
except Exception as e:
article['translated_title'] = article['title']
if index > 0 and total > 0:
logger.warning("标题翻译失败 %d/%d: %s", index, total, e)
return article
def translate_content(article, api_key, base_url, model, index=0, total=0):
"""翻译外文文章的正文前500词,返回更新后的文章"""
logger = get_logger()
try:
content = article['content'][:500] if article['content'] else ''
prompt = f"""请将以下军事相关的英文正文翻译成专业中文,仅返回翻译结果:
{content}"""
result = call_ai([
{'role': 'system', 'content': '你是专业的军事科技翻译专家,翻译准确、专业、简洁,只返回翻译结果。'},
{'role': 'user', 'content': prompt}
], api_key, base_url, model, temperature=0.2, max_tokens=800, purpose="正文翻译")
article['translated_content'] = result.strip()[:300]
if index > 0 and total > 0:
logger.info("正文翻译完成 %d/%d: %s", index, total, article['translated_title'][:30])
return article
except Exception as e:
article['translated_content'] = article['content'][:300] if article['content'] else ''
if index > 0 and total > 0:
logger.warning("正文翻译失败 %d/%d: %s", index, total, e)
return article
def _translate_single_article(article, api_key, base_url, model, index=0, total=0):
"""翻译单篇外文文章的标题和正文前500词,返回处理后的文章(兼容旧接口)"""
article = translate_title(article, api_key, base_url, model, index, total)
article = translate_content(article, api_key, base_url, model, index, total)
return article
def batch_translate_titles(articles, api_key, base_url, model, max_concurrent=None):
"""批量翻译所有外文文章的标题,中文文章直接填充translated_title
所有文章处理后都包含translated_title字段,无需后续判断
"""
logger = get_logger()
processed = []
chinese_articles = []
foreign_articles = []
for a in articles:
if is_chinese(a['title']):
a['translated_title'] = a['title']
chinese_articles.append(a)
else:
foreign_articles.append(a)
processed.extend(chinese_articles)
logger.info("中文文章:%d 篇,待翻译标题的外文:%d 篇", len(chinese_articles), len(foreign_articles))
if not foreign_articles:
return processed
if max_concurrent is None:
max_concurrent = AI_CONCURRENCY
logger.info("开始并发翻译标题,并发数:%d", max_concurrent)
translated_results = []
with ThreadPoolExecutor(max_workers=max_concurrent) as executor:
future_to_index = {
executor.submit(translate_title, article, api_key, base_url, model, i+1, len(foreign_articles)): i
for i, article in enumerate(foreign_articles)
}
for future in as_completed(future_to_index):
translated_article = future.result()
translated_results.append(translated_article)
processed.extend(translated_results)
logger.info("标题翻译完成,共处理 %d 篇文章", len(processed))
return processed
def batch_translate_contents(articles, api_key, base_url, model, max_concurrent=None):
"""批量翻译指定文章的正文,仅翻译需要的文章
调用前需要确保文章已经翻译了标题,并且经过筛选确实需要翻译正文
"""
logger = get_logger()
if not articles:
return []
# 分离不需要翻译正文的中文和需要翻译的外文
chinese_articles = []
foreign_articles = []
for a in articles:
if is_chinese(a['title']):
a['translated_content'] = a['content'][:300] if a['content'] else ''
chinese_articles.append(a)
else:
foreign_articles.append(a)
logger.info("无需翻译正文的中文文章:%d 篇,待翻译正文的外文:%d 篇", len(chinese_articles), len(foreign_articles))
if not foreign_articles:
return chinese_articles
if max_concurrent is None:
max_concurrent = AI_CONCURRENCY
logger.info("开始并发翻译正文,并发数:%d", max_concurrent)
translated_results = []
with ThreadPoolExecutor(max_workers=max_concurrent) as executor:
future_to_index = {
executor.submit(translate_content, article, api_key, base_url, model, i+1, len(foreign_articles)): i
for i, article in enumerate(foreign_articles)
}
for future in as_completed(future_to_index):
translated_article = future.result()
translated_results.append(translated_article)
all_articles = chinese_articles + translated_results
logger.info("正文翻译完成,共处理 %d 篇文章", len(all_articles))
return all_articles
def batch_translate_articles(articles, api_key, base_url, model, max_concurrent=None):
"""批量翻译外文文章:中英分离,外文并发翻译(兼容旧接口)
默认并发数使用配置中的AI_CONCURRENCY
"""
logger = get_logger()
chinese_articles = []
foreign_articles = []
for a in articles:
if is_chinese(a['title']) and is_chinese(a['content'][:100]):
a['translated_title'] = a['title']
a['translated_content'] = a['content'][:300] if a['content'] else ''
chinese_articles.append(a)
else:
foreign_articles.append(a)
logger.info("中文文章:%d 篇,待翻译外文:%d 篇", len(chinese_articles), len(foreign_articles))
if not foreign_articles:
return articles
if max_concurrent is None:
max_concurrent = AI_CONCURRENCY
logger.info("开始并发翻译,并发数:%d", max_concurrent)
translated_results = []
with ThreadPoolExecutor(max_workers=max_concurrent) as executor:
future_to_index = {
executor.submit(_translate_single_article, article, api_key, base_url, model, i+1, len(foreign_articles)): i
for i, article in enumerate(foreign_articles)
}
for future in as_completed(future_to_index):
translated_article = future.result()
translated_results.append(translated_article)
logger.info("所有翻译任务完成,共翻译 %d 篇外文", len(translated_results))
return chinese_articles + translated_results