pptx支持

This commit is contained in:
xunbu committed 2025-11-29 21:51:09 +08:00
1 parent 0ff1c87870
commit 63e6101064
4 files changed
+171 -63

No files matched your search

+159 -31
View File
@@ -65,6 +65,9 @@ from docutranslate.workflow.base import Workflow
from docutranslate.workflow.docx_workflow import DocxWorkflow, DocxWorkflowConfig
from docutranslate.workflow.epub_workflow import EpubWorkflow, EpubWorkflowConfig
from docutranslate.workflow.html_workflow import HtmlWorkflow, HtmlWorkflowConfig
# --- 新增的 Import ---
from docutranslate.workflow.pptx_workflow import PPTXWorkflow, PPTXWorkflowConfig
# ----------------------
from docutranslate.workflow.interfaces import DocxExportable, EpubExportable
from docutranslate.workflow.interfaces import (
HTMLExportable,
@@ -75,6 +78,7 @@ from docutranslate.workflow.interfaces import (
SrtExportable,
CsvExportable,
AssExportable,
PPTXExportable, # Added PPTXExportable
)
from docutranslate.workflow.json_workflow import JsonWorkflow, JsonWorkflowConfig
from docutranslate.workflow.md_based_workflow import (
@@ -88,9 +92,7 @@ from docutranslate.workflow.xlsx_workflow import XlsxWorkflow, XlsxWorkflowConfi
if DOCLING_EXIST or TYPE_CHECKING:
from docutranslate.converter.x2md.converter_docling import ConverterDoclingConfig
from docutranslate.converter.x2md.converter_mineru import ConverterMineruConfig
# --- 新增的 Import ---
from docutranslate.converter.x2md.converter_mineru_deploy import ConverterMineruDeployConfig
# ----------------------
from docutranslate.exporter.md.md2html_exporter import MD2HTMLExporterConfig
from docutranslate.exporter.txt.txt2html_exporter import TXT2HTMLExporterConfig
from docutranslate.translator.ai_translator.md_translator import MDTranslatorConfig
@@ -108,8 +110,8 @@ from docutranslate.exporter.epub.epub2html_exporter import Epub2HTMLExporterConf
from docutranslate.translator.ai_translator.html_translator import HtmlTranslatorConfig
from docutranslate.translator.ai_translator.ass_translator import AssTranslatorConfig
from docutranslate.exporter.ass.ass2html_exporter import Ass2HTMLExporterConfig
# ------------------------------------
from docutranslate.translator.ai_translator.pptx_translator import PPTXTranslatorConfig
from docutranslate.exporter.pptx.pptx2html_exporter import PPTX2HTMLExporterConfig
from docutranslate.logger import global_logger
from docutranslate.translator import default_params
@@ -133,6 +135,7 @@ WORKFLOW_DICT: Dict[str, Type[Workflow]] = {
"epub": EpubWorkflow,
"html": HtmlWorkflow,
"ass": AssWorkflow,
"pptx": PPTXWorkflow, # Added PPTXWorkflow
}
# --- 媒体类型映射 ---
@@ -148,6 +151,7 @@ MEDIA_TYPES = {
"srt": "text/plain; charset=utf-8",
"epub": "application/epub+zip",
"ass": "text/plain; charset=utf-8",
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", # Added PPTX MIME
}
@@ -609,6 +613,25 @@ class AssWorkflowParams(BaseWorkflowParams):
# --- ASS WORKFLOW PARAMS END ---
# --- PPTX WORKFLOW PARAMS START ---
class PPTXWorkflowParams(BaseWorkflowParams):
workflow_type: Literal["pptx"] = Field(
..., description="指定使用PPTX的翻译工作流。"
)
insert_mode: Literal["replace", "append", "prepend"] = Field(
"replace",
description="翻译文本的插入模式。'replace':替换原文,'append':附加到原文后,'prepend':附加到原文前。",
)
separator: str = Field(
"\n",
description="当 insert_mode 为 'append' 或 'prepend' 时,用于分隔原文和译文的分隔符。",
)
# target_cjk_font removed as per request
# --- PPTX WORKFLOW PARAMS END ---
# 3. 使用可辨识联合类型(Discriminated Union)将它们组合起来
TranslatePayload = Annotated[
Union[
@@ -621,6 +644,7 @@ TranslatePayload = Annotated[
EpubWorkflowParams,
HtmlWorkflowParams,
AssWorkflowParams,
PPTXWorkflowParams,
],
Field(discriminator="workflow_type"),
]
@@ -639,6 +663,7 @@ class TranslateServiceRequest(BaseModel):
"my_book.epub",
"index.html",
"dialogue.ass",
"presentation.pptx",
],
)
file_content: str = Field(
@@ -864,6 +889,26 @@ class TranslateServiceRequest(BaseModel):
"retry": default_params["retry"],
},
},
{
"file_name": "presentation.pptx",
"file_content": "UEsDBBQAAAAIA... (base64-encoded pptx)",
"payload": {
"workflow_type": "pptx",
"skip_translate": False,
"base_url": "https://api.openai.com/v1",
"api_key": "sk-your-api-key-here",
"model_id": "gpt-4o",
"to_lang": "中文",
"insert_mode": "replace",
"separator": "\n",
"chunk_size": default_params["chunk_size"],
"concurrent": default_params["concurrent"],
"temperature": default_params["temperature"],
"timeout": default_params["timeout"],
"thinking": "default",
"retry": default_params["retry"],
},
},
]
}
)
@@ -1283,6 +1328,46 @@ async def _perform_translation(
workflow = AssWorkflow(config=workflow_config)
# --- ASS WORKFLOW LOGIC END ---
# --- PPTX WORKFLOW LOGIC START ---
elif isinstance(payload, PPTXWorkflowParams):
task_logger.info("构建 PPTXWorkflow 配置。")
translator_args = payload.model_dump(
include={
"skip_translate",
"base_url",
"api_key",
"model_id",
"to_lang",
"custom_prompt",
"temperature",
"thinking",
"chunk_size",
"concurrent",
"insert_mode",
"separator",
"glossary_dict",
"timeout",
"retry",
"system_proxy_enable",
"force_json",
},
exclude_none=True,
)
translator_args["glossary_generate_enable"] = (
payload.glossary_generate_enable
)
translator_args["glossary_agent_config"] = build_glossary_agent_config()
translator_config = PPTXTranslatorConfig(**translator_args)
html_exporter_config = PPTX2HTMLExporterConfig(cdn=True)
workflow_config = PPTXWorkflowConfig(
translator_config=translator_config,
html_exporter_config=html_exporter_config,
logger=task_logger,
)
workflow = PPTXWorkflow(config=workflow_config)
# --- PPTX WORKFLOW LOGIC END ---
else:
raise TypeError(f"工作流类型 '{payload.workflow_type}' 的处理逻辑未实现。")
@@ -1313,30 +1398,7 @@ async def _perform_translation(
# 定义导出函数映射
export_map = {}
# 根据 workflow 的类型填充导出映射
if isinstance(workflow, HTMLExportable):
html_config = None
if isinstance(workflow, MarkdownBasedWorkflow):
html_config = MD2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, TXTWorkflow):
html_config = TXT2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, JsonWorkflow):
html_config = Json2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, XlsxWorkflow):
html_config = Xlsx2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, DocxWorkflow):
html_config = Docx2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, SrtWorkflow):
html_config = Srt2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, EpubWorkflow):
html_config = Epub2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, AssWorkflow):
html_config = Ass2HTMLExporterConfig(cdn=is_cdn_available)
export_map["html"] = (
lambda: workflow.export_to_html(html_config),
f"{filename_stem}_translated.html",
True,
)
if isinstance(workflow, MDFormatsExportable):
export_map["markdown"] = (
workflow.export_to_markdown,
@@ -1396,6 +1458,39 @@ async def _perform_translation(
f"{filename_stem}_translated.ass",
True,
)
if isinstance(workflow, PPTXExportable):
export_map["pptx"] = (
workflow.export_to_pptx,
f"{filename_stem}_translated.pptx",
False,
)
# 根据 workflow 的类型填充导出映射
if isinstance(workflow, HTMLExportable):
html_config = None
if isinstance(workflow, MarkdownBasedWorkflow):
html_config = MD2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, TXTWorkflow):
html_config = TXT2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, JsonWorkflow):
html_config = Json2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, XlsxWorkflow):
html_config = Xlsx2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, DocxWorkflow):
html_config = Docx2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, SrtWorkflow):
html_config = Srt2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, EpubWorkflow):
html_config = Epub2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, AssWorkflow):
html_config = Ass2HTMLExporterConfig(cdn=is_cdn_available)
elif isinstance(workflow, PPTXWorkflow):
html_config = PPTX2HTMLExporterConfig(cdn=is_cdn_available)
export_map["html"] = (
lambda: workflow.export_to_html(html_config),
f"{filename_stem}_translated.html",
True,
)
# 循环生成文件
for file_type, (export_func, filename, is_string_output) in export_map.items():
@@ -1616,7 +1711,7 @@ def _cancel_translation_logic(task_id: str):
description="""
接收一个包含文件内容(Base64编码)和工作流参数的JSON请求,启动一个后台翻译任务。
- **工作流选择**: 请求体中的 `payload.workflow_type` 字段决定了本次任务的类型(如 `markdown_based`, `txt`, `json`, `xlsx`, `docx`, `srt`, `epub`, `html`, `ass`)。
- **工作流选择**: 请求体中的 `payload.workflow_type` 字段决定了本次任务的类型(如 `markdown_based`, `txt`, `json`, `xlsx`, `docx`, `srt`, `epub`, `html`, `ass`, `pptx`)。
- **动态参数**: 根据所选工作流,API需要不同的参数集。请参考下面的Schema或示例。
- **异步处理**: 此端点会立即返回任务ID,客户端需轮询状态接口获取进度。
""",
@@ -1960,6 +2055,27 @@ async def service_release_task(task_id: str):
},
},
# --- ASS STATUS EXAMPLE END ---
# --- PPTX STATUS EXAMPLE START ---
"completed_pptx": {
"summary": "已完成 (PPTX)",
"value": {
"task_id": "a1b2c3d6",
"is_processing": False,
"status_message": "翻译成功!用时 30.50 秒。",
"error_flag": False,
"download_ready": True,
"original_filename_stem": "presentation",
"original_filename": "presentation.pptx",
"task_start_time": 1678890300.0,
"task_end_time": 1678890330.50,
"downloads": {
"pptx": "/service/download/a1b2c3d6/pptx",
"html": "/service/download/a1b2c3d6/html",
},
"attachment": {},
},
},
# --- PPTX STATUS EXAMPLE END ---
"error": {
"summary": "失败",
"value": {
@@ -2052,6 +2168,7 @@ FileType = Literal[
"srt",
"epub",
"ass",
"pptx",
]
@@ -2077,6 +2194,9 @@ FileType = Literal[
"application/epub+zip": {
"schema": {"type": "string", "format": "binary"}
},
"application/vnd.openxmlformats-officedocument.presentationml.presentation": {
"schema": {"type": "string", "format": "binary"}
},
},
},
404: {
@@ -2092,7 +2212,7 @@ async def service_download_file(
file_type: FileType = FastApiPath(
...,
description="要下载的文件类型。",
examples=["html", "json", "csv", "docx", "srt", "epub", "ass"],
examples=["html", "json", "csv", "docx", "srt", "epub", "ass", "pptx"],
),
):
task_state = tasks_state.get(task_id)
@@ -2198,6 +2318,14 @@ async def service_download_attachment(
"content": "UEsDBBQAAAAIA... (base64-encoded string)",
},
},
"pptx_base64": {
"summary": "PPTX 内容 (Base64)",
"value": {
"file_type": "pptx",
"filename": "my_presentation_translated.pptx",
"content": "UEsDBBQAAAAIA... (base64-encoded string)",
},
},
}
}
},
@@ -2215,7 +2343,7 @@ async def service_content(
file_type: FileType = FastApiPath(
...,
description="要获取内容的文件类型。",
examples=["html", "json", "csv", "docx", "srt", "epub", "ass"],
examples=["html", "json", "csv", "docx", "srt", "epub", "ass", "pptx"],
),
):
task_state = tasks_state.get(task_id)
+8 -10
View File
@@ -13,6 +13,7 @@
"workflowOptionAss": "ASS字幕翻译 (.ass)",
"workflowOptionJson": "JSON翻译 (.json)",
"workflowOptionHtml": "HTML翻译 (.html)",
"workflowOptionPptx": "PPTX演示文稿 (.pptx)",
"autoWorkflowLabel": "自动选择工作流",
"txtSettingsTitleText": "TXT翻译选项",
"insertModeLabel": "插入模式",
@@ -48,6 +49,8 @@
"insertModeHelpAss": "选择如何将翻译后的文本插入。",
"separatorPlaceholderAss": "例如: \\N (换行符)",
"separatorHelpAss": "当插入模式为附加或前置时,用于分隔原文和译文的字符。<code>\\N</code> 是ASS格式的换行符。",
"pptxSettingsTitleText": "PPTX翻译选项",
"insertModeHelpPptx": "选择如何将翻译后的文本插入文本框。",
"jsonSettingsTitleText": "JSON路径配置",
"jsonPathLabel": "需要翻译的JSON路径",
"jsonPathPlaceholder": "每行一个路径, 例如:\n$.name\n$.*",
@@ -55,7 +58,6 @@
"parsingSettingsTitleText": "解析配置",
"parsingEngineLabel": "解析引擎",
"parsingEngineHelp": "如果上传的文件本身是.md格式,此项可不选。",
"getMineruTokenTitle": "获取Mineru Token",
"mineruTokenPlaceholder": "使用Mineru引擎时需要",
"modelVersionLabel": "Mineru 模型版本",
"modelVersionVlm": "VLM",
@@ -80,7 +82,6 @@
"platformCustom": "自定义接口",
"baseUrlLabel": "API 地址 (Base URL)",
"baseUrlPlaceholder": "OpenAi兼容地址",
"getApiKeyTitle": "获取API Key",
"apiKeyPlaceholder": "请输入您的API Key",
"modelIdLabel": "模型ID",
"modelIdPlaceholder": "例如: gpt-4o, glm-4",
@@ -115,8 +116,6 @@
"glossaryGenConfigCustom": "自定义",
"importConfigBtn": "导入配置",
"exportConfigBtn": "导出配置",
"githubInfo": "GitHub主页(欢迎star❤): <br/>\n <a href=\"https://github.com/xunbu/docutranslate\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/xunbu/docutranslate</a>",
"qqGroupInfo": "交流QQ群: 1047781902",
"taskListTitle": "任务列表",
"newTaskBtn": "新建任务",
"noTaskPlaceholder": "当前没有任务,点击“新建任务”开始吧!",
@@ -144,7 +143,7 @@
"closeBtn": "关闭",
"downloadBtn": "下载",
"tutorialModalTitle": "使用教程",
"tutorialModalBody": "<p><i class=\"bi bi-camera-video me-2\"></i>视频教程可以在B站搜索 <a href=\"https://search.bilibili.com/all?keyword=docutranslate\" target=\"_blank\">docutranslate</a> 获取。</p><p>欢迎使用 DocuTranslate!请按照以下步骤完成文档翻译:</p><ol><li><strong><i class=\"bi bi-diagram-3 me-2\"></i>第一步:选择工作流</strong><p class=\"mt-2\">在左侧配置面板的顶部,首先选择最适合您文件类型的处理流程。</p><div class=\"alert alert-info mt-2\" role=\"alert\"><i class=\"bi bi-lightbulb-fill me-2\"></i>提示: 默认已开启“自动选择工作流”。您只需上传文件,系统会自动为您匹配合适的工作流,简化操作。</div><ul><li><b>转Markdown再翻译</b>: 适用于翻译PDF、markdown、图片等文件。这是最通用和强大的模式。</li><li><b>纯文本翻译</b>: 用于翻译 <code>.txt</code> 纯文本文件。</li><li><b>EPUB翻译</b>: 用于翻译 <code>.epub</code> 电子书文件。</li><li><b>DOCX翻译</b>: 用于翻译 <code>.docx</code> Word文档。</li><li><b>XLSX翻译</b>: 用于翻译 <code>.xlsx</code> 或 <code>.csv</code> 电子表格文件。</li><li><b>SRT字幕翻译</b>: 用于翻译 <code>.srt</code> 字幕文件。</li><li><b>ASS字幕翻译</b>: 用于翻译 <code>.ass</code> 特效字幕文件。</li><li><b>JSON翻译</b>: 用于翻译 <code>.json</code> 文件中的特定字段。</li><li><b>HTML翻译</b>: 用于翻译 <code>.html</code> 网页文件。</li></ul></li><li><strong><i class=\"bi bi-gear-fill me-2\"></i>第二步:配置参数</strong><p class=\"mt-2\">选择工作流后,下方会显示相关的配置选项。请依次完成设置(所有配置都会自动保存在您的浏览器中):</p><p class=\"mb-2\"><strong>A. 工作流特定选项</strong> (根据您第一步的选择出现):</p><ul class=\"ms-4\"><li><u>如果选择“转Markdown再翻译”</u>,请配置 <strong>解析配置</strong>:<ul><li><strong>解析引擎</strong>: 选择一个引擎将您的文件(如PDF)转换为适合翻译的Markdown格式。如果您的文件已经是Markdown格式,则无需选择。</li><li><strong>Mineru Token</strong>: 如果您选择 <code>minerU</code> 引擎,需要在此处填入您的Token。</li></ul></li><li><u>如果选择“纯文本/DOCX/XLSX/SRT/ASS/EPUB/HTML”</u>,请配置其 <strong>翻译选项</strong>:<ul><li><strong>插入模式</strong>: 定义翻译结果如何放入文档。您可以选择直接“替换”原文,或是在原文之后“附加”,或是在原文之前“前置”。</li><li><strong>分隔符</strong>: 当选择“附加”或“前置”模式时,此项用于在原文和译文之间插入分隔符(例如ASS格式常用 <code>\\N</code>,EPUB格式常用 <code>&lt;br /&gt;</code> 作为换行分隔符)。</li></ul></li><li><u>如果选择“JSON翻译”</u>,请配置 <strong>JSON路径</strong>:<ul><li><strong>需要翻译的JSON路径</strong>: 每行输入一个 <a href=\"https://goessner.net/articles/JsonPath/\" target=\"_blank\">JSONPath</a> 表达式,将翻译所有与路径匹配的对象中的字符串。例如:<code>$.*</code>(翻译全部字符串),<code>$..description</code>(翻译所有键为<code>description</code>的值)。</li></ul></li></ul><p class=\"mb-2 mt-3\"><strong>B. 通用选项</strong> (适用于所有工作流):</p><ul class=\"ms-4\"><li><strong>翻译模型</strong>:<ul><li><strong>选择平台/API 地址/API Key/模型ID</strong>: 配置您希望使用的AI翻译服务。模型能力指令遵循越强,<strong>出错</strong>、<strong>漏翻</strong>的概率越低。</li><li><strong>跳过翻译</strong>: 勾选此项后,将只执行文档解析和格式转换,不调用AI进行翻译。</li></ul></li><li><strong>翻译配置</strong>:<ul><li><strong>目标语言</strong>: 指定翻译的目标语言。</li><li><strong>自定义Prompt</strong>: 可选,添加额外指令,如“人名保持原文不翻译”。</li><li><strong>思考模式</strong>: 针对部分支持混合推理的模型进行设置,建议选择“禁用(推荐)”。</li><li><strong>分块大小/并发数等</strong>: 高级参数,用于调整性能和API请求行为,通常保持默认即可。</li></ul></li><li><strong>术语表</strong>:<ul><li><strong>上传术语表 (可选)</strong>: 上传CSV文件(需包含'src'和'dst'列)来保证特定术语翻译的统一性和准确性。</li><li><strong>自动生成术语表</strong>: 启用后,程序会先从原文中提取术语并生成一个术语表,然后再进行翻译。</li></ul></li></ul></li><li><strong><i class=\"bi bi-file-earmark-arrow-up-fill me-2\"></i>第三步:上传文件</strong><p class=\"mt-2\">在右侧的任务列表中,点击或拖拽您的文档到文件上传区域。</p></li><li><strong><i class=\"bi bi-play-circle-fill me-2\"></i>第四步:开始翻译</strong><p class=\"mt-2\">文Line truncated
"tutorialModalBody": "<p><i class=\"bi bi-camera-video me-2\"></i>视频教程可以在B站搜索 <a href=\"https://search.bilibili.com/all?keyword=docutranslate\" target=\"_blank\">docutranslate</a> 获取。</p><p>欢迎使用 DocuTranslate!请按照以下步骤完成文档翻译:</p><ol><li><strong><i class=\"bi bi-diagram-3 me-2\"></i>第一步:选择工作流</strong><p class=\"mt-2\">在左侧配置面板的顶部,首先选择最适合您文件类型的处理流程。</p><div class=\"alert alert-info mt-2\" role=\"alert\"><i class=\"bi bi-lightbulb-fill me-2\"></i>提示: 默认已开启“自动选择工作流”。您只需上传文件,系统会自动为您匹配合适的工作流,简化操作。</div><ul><li><b>转Markdown再翻译</b>: 适用于翻译PDF、markdown、图片等文件。这是最通用和强大的模式。</li><li><b>纯文本翻译</b>: 用于翻译 <code>.txt</code> 纯文本文件。</li><li><b>EPUB翻译</b>: 用于翻译 <code>.epub</code> 电子书文件。</li><li><b>DOCX翻译</b>: 用于翻译 <code>.docx</code> Word文档。</li><li><b>XLSX翻译</b>: 用于翻译 <code>.xlsx</code> 或 <code>.csv</code> 电子表格文件。</li><li><b>PPTX翻译</b>: 用于翻译 <code>.pptx</code> 幻灯片文件。</li><li><b>SRT字幕翻译</b>: 用于翻译 <code>.srt</code> 字幕文件。</li><li><b>ASS字幕翻译</b>: 用于翻译 <code>.ass</code> 特效字幕文件。</li><li><b>JSON翻译</b>: 用于翻译 <code>.json</code> 文件中的特定字段。</li><li><b>HTML翻译</b>: 用于翻译 <code>.html</code> 网页文件。</li></ul></li><li><strong><i class=\"bi bi-gear-fill me-2\"></i>第二步:配置参数</strong><p class=\"mt-2\">选择工作流后,下方会显示相关的配置选项。请依次完成设置(所有配置都会自动保存在您的浏览器中):</p><p class=\"mb-2\"><strong>A. 工作流特定选项</strong> (根据您第一步的选择出现):</p><ul class=\"ms-4\"><li><u>如果选择“转Markdown再翻译”</u>,请配置 <strong>解析配置</strong>:<ul><li><strong>解析引擎</strong>: 选择一个引擎将您的文件(如PDF)转换为适合翻译的Markdown格式。如果您的文件已经是Markdown格式,则无需选择。</li><li><strong>Mineru Token</strong>: 如果您选择 <code>minerU</code> 引擎,需要在此处填入您的Token。</li></ul></li><li><u>如果选择“纯文本/DOCX/XLSX/PPTX/SRT/ASS/EPUB/HTML”</u>,请配置其 <strong>翻译选项</strong>:<ul><li><strong>插入模式</strong>: 定义翻译结果如何放入文档。您可以选择直接“替换”原文,或是在原文之后“附加”,或是在原文之前“前置”。</li><li><strong>分隔符</strong>: 当选择“附加”或“前置”模式时,此项用于在原文和译文之间插入分隔符(例如ASS格式常用 <code>\\N</code>,EPUB格式常用 <code>&lt;br /&gt;</code> 作为换行分隔符)。</li></ul></li><li><u>如果选择“JSON翻译”</u>,请配置 <strong>JSON路径</strong>:<ul><li><strong>需要翻译的JSON路径</strong>: 每行输入一个 <a href=\"https://goessner.net/articles/JsonPath/\" target=\"_blank\">JSONPath</a> 表达式,将翻译所有与路径匹配的对象中的字符串。例如:<code>$.*</code>(翻译全部字符串),<code>$..description</code>(翻译所有键为<code>description</code>的值)。</li></ul></li></ul><p class=\"mb-2 mt-3\"><strong>B. 通用选项</strong> (适用于所有工作流):</p><ul class=\"ms-4\"><li><strong>翻译模型</strong>:<ul><li><strong>选择平台/API 地址/API Key/模型ID</strong>: 配置您希望使用的AI翻译服务。模型能力指令遵循越强,<strong>出错</strong>、<strong>漏翻</strong>的概率越低。</li><li><strong>跳过翻译</strong>: 勾选此项后,将只执行文档解析和格式转换,不调用AI进行翻译。</li></ul></li><li><strong>翻译配置</strong>:<ul><li><strong>目标语言</strong>: 指定翻译的目标语言。</li><li><strong>自定义Prompt</strong>: 可选,添加额外指令,如“人名保持原文不翻译”。</li><li><strong>思考模式</strong>: 针对部分支持混合推理的模型进行设置,建议选择“禁用(推荐)”。</li><li><strong>分块大小/并发数等</strong>: 高级参数,用于调整性能和API请求行为,通常保持默认即可。</li></ul></li><li><strong>术语表</strong>:<ul><li><strong>上传术语表 (可选)</strong>: 上传CSV文件(需包含'src'和'dst'列)来保证特定术语翻译的统一性和准确性。</li><li><strong>自动生成术语表</strong>: 启用后,程序会先从原文中提取术语并生成一个术语表,然后再进行翻译。</li></ul></li></ul></li><li><strong><i class=\"bi bi-file-earmark-arrow-up-fill me-2\"></i>第三步:上传文件</strong><p class=\"mt-2\">在右侧的任务列表中,点击或拖拽您的文档到文件上传区域。</p></li><li><strong><i class=\"bi bi-pLine truncated
"tutorialUnderstandBtn": "我明白了",
"contributorsModalTitle": "感谢贡献",
"contributorsPara1": "DocuTranslate是一个开源项目!大家的需求与使用是项目进步的动力。",
@@ -208,6 +207,7 @@
"workflowOptionAss": "ASS Subtitle Translation (.ass)",
"workflowOptionJson": "JSON Translation (.json)",
"workflowOptionHtml": "HTML Translation (.html)",
"workflowOptionPptx": "PPTX Presentation (.pptx)",
"autoWorkflowLabel": "Auto-select Workflow",
"txtSettingsTitleText": "TXT Translation Options",
"insertModeLabel": "Insert Mode",
@@ -243,6 +243,8 @@
"insertModeHelpAss": "Choose how to insert the translated text.",
"separatorPlaceholderAss": "e.g., \\N (newline character)",
"separatorHelpAss": "Character used to separate original and translated text in append or prepend mode. <code>\\N</code> is the newline character for the ASS format.",
"pptxSettingsTitleText": "PPTX Translation Options",
"insertModeHelpPptx": "Choose how to insert the translated text into text boxes.",
"jsonSettingsTitleText": "JSON Path Configuration",
"jsonPathLabel": "JSON Paths to Translate",
"jsonPathPlaceholder": "One path per line, e.g.:\n$.name\n$.*",
@@ -250,7 +252,6 @@
"parsingSettingsTitleText": "Parsing Configuration",
"parsingEngineLabel": "Parsing Engine",
"parsingEngineHelp": "If the uploaded file is already in .md format, this can be skipped.",
"getMineruTokenTitle": "Get Mineru Token",
"mineruTokenPlaceholder": "Required when using the Mineru engine",
"modelVersionLabel": "Mineru Model Version",
"modelVersionVlm": "VLM",
@@ -275,7 +276,6 @@
"platformCustom": "Custom API",
"baseUrlLabel": "API Address (Base URL)",
"baseUrlPlaceholder": "OpenAI-compatible address",
"getApiKeyTitle": "Get API Key",
"apiKeyPlaceholder": "Please enter your API Key",
"modelIdLabel": "Model ID",
"modelIdPlaceholder": "e.g., gpt-4o, glm-4",
@@ -310,8 +310,6 @@
"glossaryGenConfigCustom": "Custom",
"importConfigBtn": "Import Config",
"exportConfigBtn": "Export Config",
"githubInfo": "GitHub Page (stars❤ welcome): <br/>\n <a href=\"https://github.com/xunbu/docutranslate\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/xunbu/docutranslate</a>",
"qqGroupInfo": "Discussion QQ Group: 1047781902",
"taskListTitle": "Task List",
"newTaskBtn": "New Task",
"noTaskPlaceholder": "No tasks yet. Click \"New Task\" to get started!",
@@ -339,7 +337,7 @@
"closeBtn": "Close",
"downloadBtn": "Download",
"tutorialModalTitle": "Tutorial",
"tutorialModalBody": "<p><i class=\"bi bi-camera-video me-2\"></i>Video tutorials are available on Bilibili by searching for <a href=\"https://search.bilibili.com/all?keyword=docutranslate\" target=\"_blank\">docutranslate</a>.</p><p>Welcome to DocuTranslate! Please follow these steps to translate your documents:</p><ol><li><strong><i class=\"bi bi-diagram-3 me-2\"></i>Step 1: Select Workflow</strong><p class=\"mt-2\">At the top of the left-side configuration panel, first choose the processing flow that best suits your file type.</p><div class=\"alert alert-info mt-2\" role=\"alert\"><i class=\"bi bi-lightbulb-fill me-2\"></i>Tip: \"Auto-select Workflow\" is enabled by default. Simply upload your file, and the system will automatically match it with the appropriate workflow to simplify the process.</div><ul><li><b>Convert to Markdown then Translate</b>: Suitable for translating PDF, Markdown, images, etc. This is the most versatile and powerful mode.</li><li><b>Plain Text Translation</b>: For translating <code>.txt</code> plain text files.</li><li><b>EPUB Translation</b>: For translating <code>.epub</code> e-book files.</li><li><b>DOCX Translation</b>: For translating <code>.docx</code> Word documents.</li><li><b>XLSX Translation</b>: For translating <code>.xlsx</code> or <code>.csv</code> spreadsheet files.</li><li><b>SRT Subtitle Translation</b>: For translating <code>.srt</code> subtitle files.</li><li><b>ASS Subtitle Translation</b>: For translating <code>.ass</code> advanced subtitle files.</li><li><b>JSON Translation</b>: For translating specific fields within <code>.json</code> files.</li><li><b>HTML Translation</b>: For translating <code>.html</code> web page files.</li></ul></li><li><strong><i class=\"bi bi-gear-fill me-2\"></i>Step 2: Configure Parameters</strong><p class=\"mt-2\">After selecting a workflow, the relevant configuration options will appear below. Please complete the settings in order (all configurations are automatically saved in your browser):</p><p class=\"mb-2\"><strong>A. Workflow-Specific Options</strong> (Appears based on your choice in Step 1):</p><ul class=\"ms-4\"><li><u>If \"Convert to Markdown then Translate\" is selected</u>, configure <strong>Parsing Configuration</strong>:<ul><li><strong>Parsing Engine</strong>: Choose an engine to convert your file (like a PDF) into a translation-friendly Markdown format. No selection is needed if your file is already in Markdown format.</li><li><strong>Mineru Token</strong>: If you choose the <code>minerU</code> engine, you need to enter your token here.</li></ul></li><li><u>If \"Plain Text/DOCX/XLSX/SRT/ASS/EPUB/HTML\" is selected</u>, configure its <strong>Translation Options</strong>:<ul><li><strong>Insert Mode</strong>: Defines how the translation result is placed in the document. You can choose to \"Replace\" the original, \"Append\" it after the original, or \"Prepend\" it before the original.</li><li><strong>Separator</strong>: When \"Append\" or \"Prepend\" mode is selected, this is used to insert a separator between the original and translated text (e.g., <code>\\\\N</code> is common for ASS format, <code>&lt;br /&gt;</code> for EPUB format as a line break).</li></ul></li><li><u>If \"JSON Translation\" is selected</u>, configure <strong>JSON Paths</strong>:<ul><li><strong>JSON Paths to Translate</strong>: Enter one <a href=\"https://goessner.net/articles/JsonPath/\" target=\"_blank\">JSONPath</a> expression per line to translate all strings within the matched objects. For example: <code>$.*</code> (translate all strings), <code>$..description</code> (translate all values with the key <code>description</code>).</li></ul></li></ul><p class=\"mb-2 mt-3\"><strong>B. General Options</strong> (Applicable to all workflows):</p><ul class=\"ms-4\"><li><strong>Translation Model</strong>:<ul><li><strong>Select Platform/API Address/API Key/Model ID</strong>: Configure the AI translation service you wish to use. The better the model follows instructions, the lower the probability of <strong>errors</strong> and <strong>missed translations</strong>.</li><li><strong>Skip Translation</strong>: If checked, only document parsing and format conversion will be performed, without calling the AI for translation.</li></ul></li><li><strong>Translation Configuration</strong>:<ul><li><strong>Target Language</strong>: Specify the target language for the translation.</li><li><strong>Custom Prompt</strong>: Optional, add extra instructions, like \"Do not translate personal names.\"</li><li><strong>Thinking Mode</strong>: A setting for some models that support mixed inference; \"Disable (Recommended)\" is the suggested choice.</li><li><strong>Chunk Size/Concurrency, etc.</strong>: Advanced parameters for adjusting performance and API request behavior; usually, the defaults are fine.</li></ul></li><li><strong>Glossary</strong>:<ul><li><strong>Upload Glossary (Optional)</strong>: Upload a CSV file (must contain 'src' and 'dst' columns) to ensure consisLine truncated
"tutorialModalBody": "<p><i class=\"bi bi-camera-video me-2\"></i>Video tutorials are available on Bilibili by searching for <a href=\"https://search.bilibili.com/all?keyword=docutranslate\" target=\"_blank\">docutranslate</a>.</p><p>Welcome to DocuTranslate! Please follow these steps to translate your documents:</p><ol><li><strong><i class=\"bi bi-diagram-3 me-2\"></i>Step 1: Select Workflow</strong><p class=\"mt-2\">At the top of the left-side configuration panel, first choose the processing flow that best suits your file type.</p><div class=\"alert alert-info mt-2\" role=\"alert\"><i class=\"bi bi-lightbulb-fill me-2\"></i>Tip: \"Auto-select Workflow\" is enabled by default. Simply upload your file, and the system will automatically match it with the appropriate workflow to simplify the process.</div><ul><li><b>Convert to Markdown then Translate</b>: Suitable for translating PDF, Markdown, images, etc. This is the most versatile and powerful mode.</li><li><b>Plain Text Translation</b>: For translating <code>.txt</code> plain text files.</li><li><b>EPUB Translation</b>: For translating <code>.epub</code> e-book files.</li><li><b>DOCX Translation</b>: For translating <code>.docx</code> Word documents.</li><li><b>XLSX Translation</b>: For translating <code>.xlsx</code> or <code>.csv</code> spreadsheet files.</li><li><b>PPTX Translation</b>: For translating <code>.pptx</code> slide files.</li><li><b>SRT Subtitle Translation</b>: For translating <code>.srt</code> subtitle files.</li><li><b>ASS Subtitle Translation</b>: For translating <code>.ass</code> advanced subtitle files.</li><li><b>JSON Translation</b>: For translating specific fields within <code>.json</code> files.</li><li><b>HTML Translation</b>: For translating <code>.html</code> web page files.</li></ul></li><li><strong><i class=\"bi bi-gear-fill me-2\"></i>Step 2: Configure Parameters</strong><p class=\"mt-2\">After selecting a workflow, the relevant configuration options will appear below. Please complete the settings in order (all configurations are automatically saved in your browser):</p><p class=\"mb-2\"><strong>A. Workflow-Specific Options</strong> (Appears based on your choice in Step 1):</p><ul class=\"ms-4\"><li><u>If \"Convert to Markdown then Translate\" is selected</u>, configure <strong>Parsing Configuration</strong>:<ul><li><strong>Parsing Engine</strong>: Choose an engine to convert your file (like a PDF) into a translation-friendly Markdown format. No selection is needed if your file is already in Markdown format.</li><li><strong>Mineru Token</strong>: If you choose the <code>minerU</code> engine, you need to enter your token here.</li></ul></li><li><u>If \"Plain Text/DOCX/XLSX/PPTX/SRT/ASS/EPUB/HTML\" is selected</u>, configure its <strong>Translation Options</strong>:<ul><li><strong>Insert Mode</strong>: Defines how the translation result is placed in the document. You can choose to \"Replace\" the original, \"Append\" it after the original, or \"Prepend\" it before the original.</li><li><strong>Separator</strong>: When \"Append\" or \"Prepend\" mode is selected, this is used to insert a separator between the original and translated text (e.g., <code>\\\\N</code> is common for ASS format, <code>&lt;br /&gt;</code> for EPUB format as a line break).</li></ul></li><li><u>If \"JSON Translation\" is selected</u>, configure <strong>JSON Paths</strong>:<ul><li><strong>JSON Paths to Translate</strong>: Enter one <a href=\"https://goessner.net/articles/JsonPath/\" target=\"_blank\">JSONPath</a> expression per line to translate all strings within the matched objects. For example: <code>$.*</code> (translate all strings), <code>$..description</code> (translate all values with the key <code>description</code>).</li></ul></li></ul><p class=\"mb-2 mt-3\"><strong>B. General Options</strong> (Applicable to all workflows):</p><ul class=\"ms-4\"><li><strong>Translation Model</strong>:<ul><li><strong>Select Platform/API Address/API Key/Model ID</strong>: Configure the AI translation service you wish to use. The better the model follows instructions, the lower the probability of <strong>errors</strong> and <strong>missed translations</strong>.</li><li><strong>Skip Translation</strong>: If checked, only document parsing and format conversion will be performed, without calling the AI for translation.</li></ul></li><li><strong>Translation Configuration</strong>:<ul><li><strong>Target Language</strong>: Specify the target language for the translation.</li><li><strong>Custom Prompt</strong>: Optional, add extra instructions, like \"Do not translate personal names.\"</li><li><strong>Thinking Mode</strong>: A setting for some models that support mixed inference; \"Disable (Recommended)\" is the suggested choice.</li><li><strong>Chunk Size/Concurrency, etc.</strong>: Advanced parameters for adjusting performance and API request behavior; usually, the defaults are fine.</li></ul></li><li><strong>Glossary</strong>:<ul><li><strong>Upload Glossary (OptionaLine truncated
"tutorialUnderstandBtn": "I Understand",
"contributorsModalTitle": "Thanks for Contributing",
"contributorsPara1": "DocuTranslate is an open-source project! The community's needs and usage are the driving force behind its progress.",
+1 -1
View File
@@ -1 +1 @@
<!DOCTYPE html> <html lang="zh-CN" data-bs-theme="auto"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DocuTranslate - 交互式文档翻译</title> <link rel="icon" href="/static/favicon.ico" type="image/x-icon"> <!-- Bootstrap CSS --> <link href="/static/bootstrap.css" rel="stylesheet" crossorigin="anonymous"> <!-- Bootstrap Icons --> <link rel="stylesheet" href="/static/bootstrap-icons.css"> <style> /* 复用原版 CSS */ body { background-color: var(--bs-body-bg); } button:focus { outline: none !important; box-shadow: none !important; } .main-container { display: flex; flex-direction: column; height: 100vh; padding-top: 1rem; padding-bottom: 1rem; } .settings-panel { height: calc(100vh - 2rem); overflow-y: auto; padding-right: 15px; } .task-area { height: calc(100vh - 2rem); overflow-y: auto; } .task-card { transition: all 0.3s ease-in-out; } .log-area { height: 150px; background-color: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: .375rem; font-family: monospace; font-size: 0.8rem; padding: 10px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; } .log-area-wrapper { position: relative; } .copy-log-btn { position: absolute; bottom: 5px; right: 5px; z-index: 10; opacity: 0.5; transition: all 0.2s ease-in-out; padding: 0.1rem 0.4rem; line-height: 1; } .copy-log-btn:hover { opacity: 1; } .file-drop-area { border: 2px dashed var(--bs-secondary-bg); border-radius: .375rem; padding: 2rem; text-align: center; cursor: pointer; transition: all 0.2s ease-in-out; background-color: var(--bs-body-bg); } .file-drop-area.drag-over { border-color: var(--bs-primary); background-color: var(--bs-secondary-bg); } .file-drop-area.file-selected { border-style: solid; border-color: var(--bs-success); background-color: var(--bs-success-bg-subtle); } .file-drop-area.input-error { border-color: var(--bs-danger); } #previewOffcanvas { --bs-offcanvas-width: 95vw; max-width: 1600px; } .preview-split-container { display: flex; flex-direction: row; height: 90%; } .preview-pane-wrapper { display: flex; flex-direction: column; overflow: hidden; width: 100%; height: 100%; } .preview-pane-wrapper .preview-pane { flex-grow: 1; border: 1px solid var(--bs-border-color); border-radius: .375rem; overflow: auto; position: relative; } .gutter { background-color: var(--bs-tertiary-bg); background-repeat: no-repeat; background-position: 50%; } .gutter.gutter-horizontal { cursor: col-resize; border-left: 1px solid var(--bs-border-color); border-right: 1px solid var(--bs-border-color); } .gutter.gutter-vertical { cursor: row-resize; border-top: 1px solid var(--bs-border-color); border-bottom: 1px solid var(--bs-border-color); } .preview-pane iframe { width: 100%; height: 100%; border: none; } .preview-pane pre { margin: 0; padding: 1rem; background-color: var(--bs-body-bg); white-space: pre; } .bottom-left-controls { position: fixed; bottom: 1rem; left: 1rem; z-index: 1050; display: flex; gap: 0.5rem; } .step-number { margin-right: 0.25rem; } #translatedPreviewContainer .preview-pane { overflow: hidden; } #translatedPreviewContainer .preview-pane iframe { display: block; width: 100%; height: 100%; } @media (max-width: 991.98px) { .main-container { height: auto; padding-bottom: 6rem; } .settings-panel, .task-area { Line truncated
<!DOCTYPE html> <html lang="zh-CN" data-bs-theme="auto"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DocuTranslate - 交互式文档翻译</title> <link rel="icon" href="/static/favicon.ico" type="image/x-icon"> <!-- Bootstrap CSS --> <link href="/static/bootstrap.css" rel="stylesheet" crossorigin="anonymous"> <!-- Bootstrap Icons --> <link rel="stylesheet" href="/static/bootstrap-icons.css"> <style> /* 复用原版 CSS */ body { background-color: var(--bs-body-bg); } button:focus { outline: none !important; box-shadow: none !important; } .main-container { display: flex; flex-direction: column; height: 100vh; padding-top: 1rem; padding-bottom: 1rem; } .settings-panel { height: calc(100vh - 2rem); overflow-y: auto; padding-right: 15px; } .task-area { height: calc(100vh - 2rem); overflow-y: auto; } .task-card { transition: all 0.3s ease-in-out; } .log-area { height: 150px; background-color: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: .375rem; font-family: monospace; font-size: 0.8rem; padding: 10px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; } .log-area-wrapper { position: relative; } .copy-log-btn { position: absolute; bottom: 5px; right: 5px; z-index: 10; opacity: 0.5; transition: all 0.2s ease-in-out; padding: 0.1rem 0.4rem; line-height: 1; } .copy-log-btn:hover { opacity: 1; } .file-drop-area { border: 2px dashed var(--bs-secondary-bg); border-radius: .375rem; padding: 2rem; text-align: center; cursor: pointer; transition: all 0.2s ease-in-out; background-color: var(--bs-body-bg); } .file-drop-area.drag-over { border-color: var(--bs-primary); background-color: var(--bs-secondary-bg); } .file-drop-area.file-selected { border-style: solid; border-color: var(--bs-success); background-color: var(--bs-success-bg-subtle); } .file-drop-area.input-error { border-color: var(--bs-danger); } #previewOffcanvas { --bs-offcanvas-width: 95vw; max-width: 1600px; } .preview-split-container { display: flex; flex-direction: row; height: 90%; } .preview-pane-wrapper { display: flex; flex-direction: column; overflow: hidden; width: 100%; height: 100%; } .preview-pane-wrapper .preview-pane { flex-grow: 1; border: 1px solid var(--bs-border-color); border-radius: .375rem; overflow: auto; position: relative; } .gutter { background-color: var(--bs-tertiary-bg); background-repeat: no-repeat; background-position: 50%; } .gutter.gutter-horizontal { cursor: col-resize; border-left: 1px solid var(--bs-border-color); border-right: 1px solid var(--bs-border-color); } .gutter.gutter-vertical { cursor: row-resize; border-top: 1px solid var(--bs-border-color); border-bottom: 1px solid var(--bs-border-color); } .preview-pane iframe { width: 100%; height: 100%; border: none; } .preview-pane pre { margin: 0; padding: 1rem; background-color: var(--bs-body-bg); white-space: pre; } .bottom-left-controls { position: fixed; bottom: 1rem; left: 1rem; z-index: 1050; display: flex; gap: 0.5rem; } .step-number { margin-right: 0.25rem; } #translatedPreviewContainer .preview-pane { overflow: hidden; } #translatedPreviewContainer .preview-pane iframe { display: block; width: 100%; height: 100%; } @media (max-width: 991.98px) { .main-container { height: auto; padding-bottom: 6rem; } .settings-panel, .task-area { Line truncated
@@ -22,9 +22,6 @@ from docutranslate.translator.ai_translator.base import AiTranslatorConfig, AiTr
class PPTXTranslatorConfig(AiTranslatorConfig):
insert_mode: Literal["replace", "append", "prepend"] = "replace"
separator: str = "\n"
# 指定翻译后的中文字体(东亚字体),防止乱码或回退到宋体
# 推荐使用 "Microsoft YaHei" (微软雅黑) 或 "DengXian" (等线)
target_cjk_font: str = "Microsoft YaHei"
# ---------------- 主类 ----------------
@@ -35,7 +32,7 @@ class PPTXTranslator(AiTranslator):
改进特性:
1. 深度遍历:支持母版、版式、备注页、以及隐藏在 AlternateContent (兼容性块) 中的文本。
2. 公式保护:智能检测文本间的公式,防止翻译后文字错位。
3. 字体美化:中西文字体分离,中文使用微软雅黑,英文保持原样。
3. 样式保留:翻译后完全保留原有的中英文字体设置,不做强制覆盖。
4. 布局自适应:防止翻译后文本溢出。
"""
@@ -54,7 +51,6 @@ class PPTXTranslator(AiTranslator):
self.translate_agent = SegmentsTranslateAgent(agent_config)
self.insert_mode = config.insert_mode
self.separator = config.separator
self.target_cjk_font = config.target_cjk_font
# ---------------- 辅助函数:样式与字体 ----------------
@@ -111,18 +107,6 @@ class PPTXTranslator(AiTranslator):
return True
def _set_east_asian_font(self, run, font_name: str):
"""设置 Run 的东亚字体 (解决中文乱码/宋体问题)。"""
if not font_name:
return
try:
rPr = run.font._element.get_or_add_rPr()
# 设置 ea (East Asian) 字体,不影响 latin (西文) 字体
ea = rPr.get_or_add_ea()
ea.set(qn('a:typeface'), font_name)
except Exception:
pass
# ---------------- 核心遍历逻辑 ----------------
def _process_text_frame(self, text_frame: TextFrame, elements: List[Dict[str, Any]], texts: List[str]):
@@ -283,12 +267,10 @@ class PPTXTranslator(AiTranslator):
primary_run = runs[0]
try:
# 1. 写入文本
# 1. 写入文本 (python-pptx 会自动保留原有的 rPr 属性,即保留默认字体)
primary_run.text = text_to_set
# 2. 设置东亚字体 (保留西文字体设置)
if self.target_cjk_font:
self._set_east_asian_font(primary_run, self.target_cjk_font)
# 2. (已移除字体强制设置逻辑,以保留 PPT 原样)
# 3. 处理溢出
text_frame = element_info.get("text_frame")