""" 翻译处理模块:自动识别外文文章,通过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