feat: translate output filename to target language
Add _translate_filename_stem() which calls the configured LLM to translate the filename stem before building the export map. Result: 磨粉.pdf translated to English → grinding.pdf (instead of 磨粉_translated.pdf). Falls back to original stem on any API error with zero impact on translation flow.
This commit is contained in:
@@ -7,6 +7,7 @@ import binascii
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -626,6 +627,47 @@ class TranslateServiceRequest(BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _translate_filename_stem(
|
||||||
|
stem: str, to_lang: str, base_url: str | None, api_key: str, model_id: str | None
|
||||||
|
) -> str:
|
||||||
|
"""Translate a filename stem to target language for cleaner download filenames."""
|
||||||
|
try:
|
||||||
|
clean_base = (base_url or "").strip().rstrip("/")
|
||||||
|
clean_model = (model_id or "").strip()
|
||||||
|
if not clean_base or not clean_model:
|
||||||
|
return stem
|
||||||
|
resp = await httpx_client.post(
|
||||||
|
f"{clean_base}/chat/completions",
|
||||||
|
headers={
|
||||||
|
"Authorization": f"Bearer {(api_key or '').strip()}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
json={
|
||||||
|
"model": clean_model,
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": (
|
||||||
|
f"Translate the document filename below into {to_lang}. "
|
||||||
|
"Return ONLY the translated name, no file extension, no explanation, no quotes.\n"
|
||||||
|
f"Filename: {stem}"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max_tokens": 60,
|
||||||
|
"temperature": 0,
|
||||||
|
},
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
translated = resp.json()["choices"][0]["message"]["content"].strip().strip("\"'")
|
||||||
|
sanitized = re.sub(r"[^\w\s\-]", "", translated).strip()
|
||||||
|
sanitized = re.sub(r"\s+", "_", sanitized)[:50]
|
||||||
|
return sanitized if sanitized else stem
|
||||||
|
except Exception:
|
||||||
|
return stem
|
||||||
|
|
||||||
|
|
||||||
# --- Background Task Logic ---
|
# --- Background Task Logic ---
|
||||||
async def _perform_translation(
|
async def _perform_translation(
|
||||||
task_id: str,
|
task_id: str,
|
||||||
@@ -1131,6 +1173,11 @@ async def _perform_translation(
|
|||||||
task_state["temp_dir"] = temp_dir
|
task_state["temp_dir"] = temp_dir
|
||||||
downloadable_files = {}
|
downloadable_files = {}
|
||||||
filename_stem = task_state["original_filename_stem"]
|
filename_stem = task_state["original_filename_stem"]
|
||||||
|
translated_stem = await _translate_filename_stem(
|
||||||
|
filename_stem, payload.to_lang,
|
||||||
|
getattr(payload, "base_url", None), getattr(payload, "api_key", ""),
|
||||||
|
getattr(payload, "model_id", None),
|
||||||
|
)
|
||||||
|
|
||||||
# 检查CDN可用性
|
# 检查CDN可用性
|
||||||
is_cdn_available = True
|
is_cdn_available = True
|
||||||
@@ -1149,66 +1196,66 @@ async def _perform_translation(
|
|||||||
if isinstance(workflow, MDFormatsExportable):
|
if isinstance(workflow, MDFormatsExportable):
|
||||||
export_map["markdown"] = (
|
export_map["markdown"] = (
|
||||||
workflow.export_to_markdown,
|
workflow.export_to_markdown,
|
||||||
f"{filename_stem}_translated.md",
|
f"{translated_stem}.md",
|
||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
export_map["markdown_zip"] = (
|
export_map["markdown_zip"] = (
|
||||||
workflow.export_to_markdown_zip,
|
workflow.export_to_markdown_zip,
|
||||||
f"{filename_stem}_translated.zip",
|
f"{translated_stem}.zip",
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, TXTExportable):
|
if isinstance(workflow, TXTExportable):
|
||||||
export_map["txt"] = (
|
export_map["txt"] = (
|
||||||
workflow.export_to_txt,
|
workflow.export_to_txt,
|
||||||
f"{filename_stem}_translated.txt",
|
f"{translated_stem}.txt",
|
||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, JsonExportable):
|
if isinstance(workflow, JsonExportable):
|
||||||
export_map["json"] = (
|
export_map["json"] = (
|
||||||
workflow.export_to_json,
|
workflow.export_to_json,
|
||||||
f"{filename_stem}_translated.json",
|
f"{translated_stem}.json",
|
||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, XlsxExportable):
|
if isinstance(workflow, XlsxExportable):
|
||||||
export_map["xlsx"] = (
|
export_map["xlsx"] = (
|
||||||
workflow.export_to_xlsx,
|
workflow.export_to_xlsx,
|
||||||
f"{filename_stem}_translated.xlsx",
|
f"{translated_stem}.xlsx",
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, CsvExportable):
|
if isinstance(workflow, CsvExportable):
|
||||||
export_map["csv"] = (
|
export_map["csv"] = (
|
||||||
workflow.export_to_csv,
|
workflow.export_to_csv,
|
||||||
f"{filename_stem}_translated.csv",
|
f"{translated_stem}.csv",
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, DocxExportable):
|
if isinstance(workflow, DocxExportable):
|
||||||
export_map["docx"] = (
|
export_map["docx"] = (
|
||||||
workflow.export_to_docx,
|
workflow.export_to_docx,
|
||||||
f"{filename_stem}_translated.docx",
|
f"{translated_stem}.docx",
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, SrtExportable):
|
if isinstance(workflow, SrtExportable):
|
||||||
export_map["srt"] = (
|
export_map["srt"] = (
|
||||||
workflow.export_to_srt,
|
workflow.export_to_srt,
|
||||||
f"{filename_stem}_translated.srt",
|
f"{translated_stem}.srt",
|
||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, EpubExportable):
|
if isinstance(workflow, EpubExportable):
|
||||||
export_map["epub"] = (
|
export_map["epub"] = (
|
||||||
workflow.export_to_epub,
|
workflow.export_to_epub,
|
||||||
f"{filename_stem}_translated.epub",
|
f"{translated_stem}.epub",
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, AssExportable):
|
if isinstance(workflow, AssExportable):
|
||||||
export_map["ass"] = (
|
export_map["ass"] = (
|
||||||
workflow.export_to_ass,
|
workflow.export_to_ass,
|
||||||
f"{filename_stem}_translated.ass",
|
f"{translated_stem}.ass",
|
||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
if isinstance(workflow, PPTXExportable):
|
if isinstance(workflow, PPTXExportable):
|
||||||
export_map["pptx"] = (
|
export_map["pptx"] = (
|
||||||
workflow.export_to_pptx,
|
workflow.export_to_pptx,
|
||||||
f"{filename_stem}_translated.pptx",
|
f"{translated_stem}.pptx",
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1235,7 +1282,7 @@ async def _perform_translation(
|
|||||||
html_config = PPTX2HTMLExporterConfig(cdn=is_cdn_available)
|
html_config = PPTX2HTMLExporterConfig(cdn=is_cdn_available)
|
||||||
export_map["html"] = (
|
export_map["html"] = (
|
||||||
lambda: workflow.export_to_html(html_config),
|
lambda: workflow.export_to_html(html_config),
|
||||||
f"{filename_stem}_translated.html",
|
f"{translated_stem}.html",
|
||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user