diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 40a56d6..7dfa115 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,7 +5,10 @@ - + + + + @@ -15,8 +18,8 @@ - + @@ -36,6 +39,7 @@ "DefaultHtmlFileTemplate": "HTML File", "JavaScript 调试.output.html (1).executor": "Run", "JavaScript 调试.output.html.executor": "Run", + "JavaScript 调试.regex.md_中文.html.executor": "Run", "JavaScript 调试.regex_中文.html.executor": "Run", "JavaScript 调试.test2.html.executor": "Run", "JavaScript 调试.test2_英文.html.executor": "Run", @@ -51,6 +55,7 @@ "Python.agent.executor": "Debug", "Python.agent_utils.executor": "Run", "Python.app.executor": "Run", + "Python.app2.executor": "Run", "Python.convert.executor": "Run", "Python.markdown_splitter.executor": "Debug", "Python.markdown_utils.executor": "Run", @@ -65,7 +70,7 @@ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager": "true", "RunOnceActivity.git.unshallow": "true", "git-widget-placeholder": "main", - "last_opened_file_path": "C:/Users/jxgm/Desktop/FileTranslate/tests/files", + "last_opened_file_path": "C:/Users/jxgm/Desktop/FileTranslate/dist/app", "node.js.detected.package.eslint": "true", "node.js.detected.package.tslint": "true", "node.js.selected.package.eslint": "(autodetect)", @@ -77,8 +82,8 @@ }]]> - + @@ -91,7 +96,7 @@ - + @@ -254,6 +259,9 @@ + + + @@ -326,6 +334,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -372,29 +403,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -418,29 +426,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -563,11 +548,11 @@ - - - - + + + + @@ -641,13 +626,14 @@ - + - + + diff --git a/docutranslate/agents/markdown_agent.py b/docutranslate/agents/markdown_agent.py index 9f30554..7846f4d 100644 --- a/docutranslate/agents/markdown_agent.py +++ b/docutranslate/agents/markdown_agent.py @@ -51,7 +51,8 @@ You are a professional, authentic machine translation engine. If translation is unnecessary (e.g. proper nouns, codes, etc.), return the original text. NO explanations. NO notes. 不要修改标题的级别(如一级标题不要修改为二级标题) -文献名和文献作者名不翻译 +文章的作者名不要翻译 +引用的参考文献和其作者不要翻译 形如的占位符不要改变 code、latex和HTML只翻译说明文字,其余保持原文 公式必须表示为合法的latex公式,且被$正确包裹 @@ -68,7 +69,7 @@ hello, what's your name? c_0+1=2 输出: $c_0+1=2$ -## 参考文献要保持原文 +## 引用的参考文献要保持原文 输入: [2] M. Castro, B. Liskov, et al. Practical byzantine fault tolerance. In OSDI, volume 99, pages 173–186, 1999. diff --git a/docutranslate/app.py b/docutranslate/app.py index bb463db..93795ef 100644 --- a/docutranslate/app.py +++ b/docutranslate/app.py @@ -1,76 +1,78 @@ import asyncio import io import logging +import time from pathlib import Path -from typing import AsyncGenerator +from typing import AsyncGenerator, List, Dict, Any +import traceback import uvicorn -from fastapi import FastAPI, File, Form, UploadFile, Request, HTTPException -from fastapi.responses import HTMLResponse, StreamingResponse, JSONResponse +from fastapi import FastAPI, File, Form, UploadFile, Request, HTTPException, BackgroundTasks +from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse from fastapi.templating import Jinja2Templates -# 导入文档翻译相关模块 from docutranslate import FileTranslater from docutranslate.logger import translater_logger -# 设置FastAPI运行标识 + app = FastAPI() # --- 全局配置 --- -SHUTDOWN_SENTINEL = object() # 哨兵对象,用于标识关闭 -log_queue = asyncio.Queue() # 日志队列 -current_state = { +log_queue = asyncio.Queue() +current_state: Dict[str, Any] = { + "is_processing": False, + "status_message": "空闲", + "error_flag": False, + "download_ready": False, "markdown_content": None, "html_content": None, "original_filename_stem": None, - "is_processing": False + "task_start_time": 0, + "task_end_time": 0, } templates = Jinja2Templates(directory=".") +MAX_LOG_HISTORY = 200 +log_history: List[str] = [] # --- 日志处理器 --- -class AsyncQueueHandler(logging.Handler): - def __init__(self, queue: asyncio.Queue): +class QueueAndHistoryHandler(logging.Handler): + def __init__(self, queue: asyncio.Queue, history: List[str], max_history: int): super().__init__() self.queue = queue + self.history = history + self.max_history = max_history def emit(self, record: logging.LogRecord): log_entry = self.format(record) + self.history.append(log_entry) + if len(self.history) > self.max_history: + del self.history[:len(self.history) - self.max_history] try: - # 尝试使用主事件循环安全地添加日志到队列 main_loop = getattr(app.state, "main_event_loop", None) if main_loop and main_loop.is_running(): main_loop.call_soon_threadsafe(self.queue.put_nowait, log_entry) else: - # 备用方案 self.queue.put_nowait(log_entry) except Exception as e: print(f"Error putting log to queue: {e}") - self.handleError(record) # --- 应用生命周期事件 --- @app.on_event("startup") async def startup_event(): app.state.main_event_loop = asyncio.get_running_loop() - # 配置日志处理器 - queue_handler = AsyncQueueHandler(log_queue) + queue_handler = QueueAndHistoryHandler(log_queue, log_history, MAX_LOG_HISTORY) queue_handler.setLevel(logging.INFO) queue_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) - - # 避免重复添加handler - if not any(isinstance(h, AsyncQueueHandler) for h in translater_logger.handlers): + if not any(isinstance(h, QueueAndHistoryHandler) for h in translater_logger.handlers): translater_logger.addHandler(queue_handler) - translater_logger.info("应用启动完成,日志队列处理器已配置。") + translater_logger.propagate = False + translater_logger.setLevel(logging.INFO) + translater_logger.info("应用启动完成,日志队列/历史处理器已配置。") -@app.on_event("shutdown") -async def shutdown_event(): - translater_logger.info("应用正在关闭,通知日志流停止。") - await log_queue.put(SHUTDOWN_SENTINEL) - await asyncio.sleep(0.1) # 给处理器留出时间处理哨兵 - - -# --- HTML模板 --- +# --- HTML模板 (JS part needs modification) --- +# language=HTML HTML_TEMPLATE = """ @@ -81,503 +83,486 @@ HTML_TEMPLATE = """ - - - - - - - DocuTranslate - - - - - API 配置 - - - API 地址 - - - - API 密钥 - - - + + + + DocuTranslate (Polling) + + + + API 配置 + - 模型 ID - - - - - - 文档选择 - - - - - - 目标语言 - - 中文 (Chinese) - 英文 (English) - 日语 (Japanese) - 韩语 (Korean) - 法语 (French) - 德语 (German) - 西班牙语 (Spanish) - 意大利语 (Italian) - 葡萄牙语 (Portuguese) - 俄语 (Russian) - 阿拉伯语 (Arabic) - 印地语 (Hindi) + AI 平台 + + 自定义接口 + OpenAI + 智谱AI + DeepSeek + 阿里云百炼 + DMXAPI + OpenRouter + Moonshot AI (Kimi) - - 高级选项 - - 公式识别 - 代码识别 - 修正文本(耗时) - + + API 地址 (Base URL) + - - 开始翻译 - - - - - - 翻译结果 - 下载 Markdown - 下载 HTML - 下载 PDF - 预览 + + API 密钥 + + + + 模型 ID + + + + + 文档选择 + + + + + 目标语言 + + 中文 (Chinese) + 英文 (English) + 日语 (Japanese) + 韩语 (Korean) + 法语 (French) + 德语 (German) + 西班牙语 (Spanish) + 意大利语 (Italian) + 葡萄牙语 (Portuguese) + 俄语 (Russian) + 阿拉伯语 (Arabic) + 印地语 (Hindi) + + + + 高级选项 + + 公式识别 + 代码识别 + 修正文本(耗时) + - - 运行日志 - - - - - - - × - HTML 预览 - - - 打印/保存为PDF - 关闭 - + 开始翻译 + + + + + 翻译结果 + 下载 Markdown + 下载 HTML + 下载 PDF + 预览 + 运行日志 + + + + + × + HTML 预览 + + + 打印/保存为PDF + 关闭 + + + + - - + + }); +