BabelDOC 0.3.39__tar.gz → 0.3.41__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/workflows/publish-to-pypi.yml +2 -2
- {babeldoc-0.3.39 → babeldoc-0.3.41}/PKG-INFO +2 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/README.md +1 -0
- babeldoc-0.3.41/babeldoc/__init__.py +1 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/const.py +1 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/add_debug_information.py +1 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/detect_scanned_file.py +1 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/il_translator.py +181 -26
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/il_translator_llm_only.py +97 -27
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/layout_parser.py +1 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/styles_and_formulas.py +1 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/table_parser.py +1 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/translator/translator.py +6 -3
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/utils/style_helper.py +1 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/high_level.py +2 -2
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/main.py +1 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/translation_config.py +2 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/pyproject.toml +2 -2
- {babeldoc-0.3.39 → babeldoc-0.3.41}/uv.lock +1 -1
- babeldoc-0.3.39/babeldoc/__init__.py +0 -1
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.cursorignore +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/PULL_REQUEST_TEMPLATE/pr_form.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/dependabot.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/labels.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/release-drafter.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/workflows/codeql.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/workflows/docs.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/workflows/labeler.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/workflows/lint.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/workflows/pr-lint.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.github/workflows/test.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.gitignore +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/.pre-commit-config.yaml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/LICENSE +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/assets/assets.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/assets/embedding_assets_metadata.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/asynchronize/__init__.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/converter.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/__init__.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/backend/__init__.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/backend/pdf_creater.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/frontend/__init__.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/frontend/il_creater.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/il_version_1.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/il_version_1.rnc +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/il_version_1.rng +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/il_version_1.xsd +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/__init__.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/paragraph_finder.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/remove_descent.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/midend/typesetting.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/translator/__init__.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/translator/cache.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/utils/atomic_integer.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/utils/fontmap.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/utils/layout_helper.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/utils/paragraph_helper.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/utils/priority_thread_pool_executor.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/utils/zstd_helper.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/xml_converter.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/docvision/README.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/docvision/__init__.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/docvision/doclayout.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/docvision/rpc_doclayout.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/docvision/table_detection/rapidocr.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/format/office/__init__.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/pdfinterp.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/progress_monitor.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/result_merger.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/split_manager.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/tools/generate_font_metadata.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/tools/italic_assistance.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/tools/italic_recognize_tool.py +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/CODE_OF_CONDUCT.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/CONTRIBUTING.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/CONTRIBUTOR_REWARD.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/README.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/README.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/deploy.sh +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-banner.png +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-big-logo.png +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-contributor_reward_example.png +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-preview.gif +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-small-logo.png +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/index.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/intro-to-pdf-object.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/requirements.txt +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/docs/supported_languages.md +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/mkdocs.yml +0 -0
- {babeldoc-0.3.39 → babeldoc-0.3.41}/tests/test_translation_config.py +0 -0
|
@@ -61,8 +61,8 @@ jobs:
|
|
|
61
61
|
- name: Bump version for developmental release
|
|
62
62
|
if: "! steps.check-version.outputs.tag"
|
|
63
63
|
run: |
|
|
64
|
-
version=$(bumpver update --patch --tag=final --dry 2>&1 | grep "New Version" | awk '{print $NF}') &&
|
|
65
|
-
bumpver update --set-version $version.dev$(date +%s)
|
|
64
|
+
version=$(uv run bumpver update --patch --tag=final --dry 2>&1 | grep "New Version" | awk '{print $NF}') &&
|
|
65
|
+
uv run bumpver update --set-version $version.dev$(date +%s)
|
|
66
66
|
|
|
67
67
|
- name: Build package
|
|
68
68
|
run: "uv build"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: BabelDOC
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.41
|
|
4
4
|
Summary: Yet Another Document Translator
|
|
5
5
|
Project-URL: Homepage, https://github.com/funstory-ai/BabelDOC
|
|
6
6
|
Project-URL: Issues, https://github.com/funstory-ai/BabelDOC/issues
|
|
@@ -216,6 +216,7 @@ uv run babeldoc --files example.pdf --files example2.pdf --openai --openai-model
|
|
|
216
216
|
- `--min-text-length`: Minimum text length to translate (default: 5)
|
|
217
217
|
- `--openai`: Use OpenAI for translation (default: False)
|
|
218
218
|
- `--custom-system-prompt`: Custom system prompt for translation.
|
|
219
|
+
- `--add-formula-placehold-hint`: Add formula placeholder hint for translation. (Currently not recommended, it may affect translation quality, default: False)
|
|
219
220
|
|
|
220
221
|
> [!TIP]
|
|
221
222
|
>
|
|
@@ -167,6 +167,7 @@ uv run babeldoc --files example.pdf --files example2.pdf --openai --openai-model
|
|
|
167
167
|
- `--min-text-length`: Minimum text length to translate (default: 5)
|
|
168
168
|
- `--openai`: Use OpenAI for translation (default: False)
|
|
169
169
|
- `--custom-system-prompt`: Custom system prompt for translation.
|
|
170
|
+
- `--add-formula-placehold-hint`: Add formula placeholder hint for translation. (Currently not recommended, it may affect translation quality, default: False)
|
|
170
171
|
|
|
171
172
|
> [!TIP]
|
|
172
173
|
>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.41"
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
1
4
|
import json
|
|
2
5
|
import logging
|
|
3
6
|
import re
|
|
@@ -36,18 +39,29 @@ class RichTextPlaceholder:
|
|
|
36
39
|
composition: PdfSameStyleCharacters,
|
|
37
40
|
left_placeholder: str,
|
|
38
41
|
right_placeholder: str,
|
|
42
|
+
left_regex_pattern: str = None,
|
|
43
|
+
right_regex_pattern: str = None,
|
|
39
44
|
):
|
|
40
45
|
self.id = placeholder_id
|
|
41
46
|
self.composition = composition
|
|
42
47
|
self.left_placeholder = left_placeholder
|
|
43
48
|
self.right_placeholder = right_placeholder
|
|
49
|
+
self.left_regex_pattern = left_regex_pattern
|
|
50
|
+
self.right_regex_pattern = right_regex_pattern
|
|
44
51
|
|
|
45
52
|
|
|
46
53
|
class FormulaPlaceholder:
|
|
47
|
-
def __init__(
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
placeholder_id: int,
|
|
57
|
+
formula: PdfFormula,
|
|
58
|
+
placeholder: str,
|
|
59
|
+
regex_pattern: str,
|
|
60
|
+
):
|
|
48
61
|
self.id = placeholder_id
|
|
49
62
|
self.formula = formula
|
|
50
63
|
self.placeholder = placeholder
|
|
64
|
+
self.regex_pattern = regex_pattern
|
|
51
65
|
|
|
52
66
|
|
|
53
67
|
class PbarContext:
|
|
@@ -78,6 +92,10 @@ class DocumentTranslateTracker:
|
|
|
78
92
|
i_str = getattr(para, "input", None)
|
|
79
93
|
o_str = getattr(para, "output", None)
|
|
80
94
|
pdf_unicode = getattr(para, "pdf_unicode", None)
|
|
95
|
+
llm_translate_trackers = getattr(para, "llm_translate_trackers", None)
|
|
96
|
+
llm_translate_trackers_json = []
|
|
97
|
+
for tracker in llm_translate_trackers:
|
|
98
|
+
llm_translate_trackers_json.append(tracker.to_dict())
|
|
81
99
|
if pdf_unicode is None or i_str is None:
|
|
82
100
|
continue
|
|
83
101
|
paragraphs.append(
|
|
@@ -85,6 +103,7 @@ class DocumentTranslateTracker:
|
|
|
85
103
|
"input": i_str,
|
|
86
104
|
"output": o_str,
|
|
87
105
|
"pdf_unicode": pdf_unicode,
|
|
106
|
+
"llm_translate_trackers": llm_translate_trackers_json,
|
|
88
107
|
},
|
|
89
108
|
)
|
|
90
109
|
pages.append({"paragraph": paragraphs})
|
|
@@ -103,7 +122,7 @@ class PageTranslateTracker:
|
|
|
103
122
|
|
|
104
123
|
class ParagraphTranslateTracker:
|
|
105
124
|
def __init__(self):
|
|
106
|
-
|
|
125
|
+
self.llm_translate_trackers = []
|
|
107
126
|
|
|
108
127
|
def set_pdf_unicode(self, unicode: str):
|
|
109
128
|
self.pdf_unicode = unicode
|
|
@@ -114,6 +133,52 @@ class ParagraphTranslateTracker:
|
|
|
114
133
|
def set_output(self, output: str):
|
|
115
134
|
self.output = output
|
|
116
135
|
|
|
136
|
+
def new_llm_translate_tracker(self) -> LLMTranslateTracker:
|
|
137
|
+
tracker = LLMTranslateTracker()
|
|
138
|
+
self.llm_translate_trackers.append(tracker)
|
|
139
|
+
return tracker
|
|
140
|
+
|
|
141
|
+
def last_llm_translate_tracker(self) -> LLMTranslateTracker | None:
|
|
142
|
+
if self.llm_translate_trackers:
|
|
143
|
+
return self.llm_translate_trackers[-1]
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class LLMTranslateTracker:
|
|
148
|
+
def __init__(self):
|
|
149
|
+
self.input = ""
|
|
150
|
+
self.output = ""
|
|
151
|
+
self.has_error = False
|
|
152
|
+
self.error_message = ""
|
|
153
|
+
self.placeholder_full_match = False
|
|
154
|
+
self.fallback_to_translate = False
|
|
155
|
+
|
|
156
|
+
def set_input(self, input_text: str):
|
|
157
|
+
self.input = input_text
|
|
158
|
+
|
|
159
|
+
def set_output(self, output_text: str):
|
|
160
|
+
self.output = output_text
|
|
161
|
+
|
|
162
|
+
def set_error_message(self, error_message: str):
|
|
163
|
+
self.has_error = True
|
|
164
|
+
self.error_message = error_message
|
|
165
|
+
|
|
166
|
+
def set_placeholder_full_match(self):
|
|
167
|
+
self.placeholder_full_match = True
|
|
168
|
+
|
|
169
|
+
def set_fallback_to_translate(self):
|
|
170
|
+
self.fallback_to_translate = True
|
|
171
|
+
|
|
172
|
+
def to_dict(self):
|
|
173
|
+
return {
|
|
174
|
+
"input": self.input,
|
|
175
|
+
"output": self.output,
|
|
176
|
+
"has_error": self.has_error,
|
|
177
|
+
"error_message": self.error_message,
|
|
178
|
+
"placeholder_full_match": self.placeholder_full_match,
|
|
179
|
+
"fallback_to_translate": self.fallback_to_translate,
|
|
180
|
+
}
|
|
181
|
+
|
|
117
182
|
|
|
118
183
|
class ILTranslator:
|
|
119
184
|
stage_name = "Translate Paragraphs"
|
|
@@ -156,9 +221,11 @@ class ILTranslator:
|
|
|
156
221
|
# Try to find the first title paragraph
|
|
157
222
|
title_paragraph = self.find_title_paragraph(docs)
|
|
158
223
|
self.translation_config.shared_context_cross_split_part.first_paragraph = (
|
|
224
|
+
copy.deepcopy(title_paragraph)
|
|
225
|
+
)
|
|
226
|
+
self.translation_config.shared_context_cross_split_part.recent_title_paragraph = copy.deepcopy(
|
|
159
227
|
title_paragraph
|
|
160
228
|
)
|
|
161
|
-
self.translation_config.shared_context_cross_split_part.recent_title_paragraph = title_paragraph
|
|
162
229
|
if title_paragraph:
|
|
163
230
|
logger.info(f"Found first title paragraph: {title_paragraph.unicode}")
|
|
164
231
|
|
|
@@ -220,7 +287,9 @@ class ILTranslator:
|
|
|
220
287
|
# self.translate_paragraph(paragraph, pbar,tracker.new_paragraph(), page_font_map, page_xobj_font_map)
|
|
221
288
|
paragraph_token_count = self.calc_token_count(paragraph.unicode)
|
|
222
289
|
if paragraph.layout_label == "title":
|
|
223
|
-
self.shared_context_cross_split_part.recent_title_paragraph =
|
|
290
|
+
self.shared_context_cross_split_part.recent_title_paragraph = (
|
|
291
|
+
copy.deepcopy(paragraph)
|
|
292
|
+
)
|
|
224
293
|
executor.submit(
|
|
225
294
|
self.translate_paragraph,
|
|
226
295
|
paragraph,
|
|
@@ -245,6 +314,24 @@ class ILTranslator:
|
|
|
245
314
|
self.placeholders = placeholders
|
|
246
315
|
self.base_style = base_style
|
|
247
316
|
|
|
317
|
+
def get_placeholders_hint(self) -> dict[str, str] | None:
|
|
318
|
+
hint = {}
|
|
319
|
+
for placeholder in self.placeholders:
|
|
320
|
+
if isinstance(placeholder, FormulaPlaceholder):
|
|
321
|
+
cid_count = 0
|
|
322
|
+
for char in placeholder.formula.pdf_character:
|
|
323
|
+
if re.match(r"^\(cid:\d+\)$", char.char_unicode):
|
|
324
|
+
cid_count += 1
|
|
325
|
+
if cid_count > len(placeholder.formula.pdf_character) * 0.8:
|
|
326
|
+
continue
|
|
327
|
+
|
|
328
|
+
hint[placeholder.placeholder] = get_char_unicode_string(
|
|
329
|
+
placeholder.formula.pdf_character
|
|
330
|
+
)
|
|
331
|
+
if hint:
|
|
332
|
+
return hint
|
|
333
|
+
return None
|
|
334
|
+
|
|
248
335
|
def create_formula_placeholder(
|
|
249
336
|
self,
|
|
250
337
|
formula: PdfFormula,
|
|
@@ -252,10 +339,14 @@ class ILTranslator:
|
|
|
252
339
|
paragraph: PdfParagraph,
|
|
253
340
|
):
|
|
254
341
|
placeholder = self.translate_engine.get_formular_placeholder(formula_id)
|
|
342
|
+
if isinstance(placeholder, tuple):
|
|
343
|
+
placeholder, regex_pattern = placeholder
|
|
344
|
+
else:
|
|
345
|
+
regex_pattern = re.escape(placeholder)
|
|
255
346
|
if placeholder in paragraph.unicode:
|
|
256
347
|
return self.create_formula_placeholder(formula, formula_id + 1, paragraph)
|
|
257
348
|
|
|
258
|
-
return FormulaPlaceholder(formula_id, formula, placeholder)
|
|
349
|
+
return FormulaPlaceholder(formula_id, formula, placeholder, regex_pattern)
|
|
259
350
|
|
|
260
351
|
def create_rich_text_placeholder(
|
|
261
352
|
self,
|
|
@@ -269,6 +360,14 @@ class ILTranslator:
|
|
|
269
360
|
right_placeholder = self.translate_engine.get_rich_text_right_placeholder(
|
|
270
361
|
composition_id,
|
|
271
362
|
)
|
|
363
|
+
if isinstance(left_placeholder, tuple):
|
|
364
|
+
left_placeholder, left_placeholder_regex_pattern = left_placeholder
|
|
365
|
+
else:
|
|
366
|
+
left_placeholder_regex_pattern = re.escape(left_placeholder)
|
|
367
|
+
if isinstance(right_placeholder, tuple):
|
|
368
|
+
right_placeholder, right_placeholder_regex_pattern = right_placeholder
|
|
369
|
+
else:
|
|
370
|
+
right_placeholder_regex_pattern = re.escape(right_placeholder)
|
|
272
371
|
if (
|
|
273
372
|
left_placeholder in paragraph.unicode
|
|
274
373
|
or right_placeholder in paragraph.unicode
|
|
@@ -284,6 +383,8 @@ class ILTranslator:
|
|
|
284
383
|
composition,
|
|
285
384
|
left_placeholder,
|
|
286
385
|
right_placeholder,
|
|
386
|
+
left_placeholder_regex_pattern,
|
|
387
|
+
right_placeholder_regex_pattern,
|
|
287
388
|
)
|
|
288
389
|
|
|
289
390
|
def get_translate_input(
|
|
@@ -452,9 +553,8 @@ class ILTranslator:
|
|
|
452
553
|
self,
|
|
453
554
|
input_text: TranslateInput,
|
|
454
555
|
output: str,
|
|
556
|
+
llm_translate_tracker: LLMTranslateTracker | None = None,
|
|
455
557
|
) -> [PdfParagraphComposition]:
|
|
456
|
-
import re
|
|
457
|
-
|
|
458
558
|
result = []
|
|
459
559
|
|
|
460
560
|
# 如果没有占位符,直接返回整个文本
|
|
@@ -463,6 +563,8 @@ class ILTranslator:
|
|
|
463
563
|
comp.pdf_same_style_unicode_characters = PdfSameStyleUnicodeCharacters()
|
|
464
564
|
comp.pdf_same_style_unicode_characters.unicode = output
|
|
465
565
|
comp.pdf_same_style_unicode_characters.pdf_style = input_text.base_style
|
|
566
|
+
if llm_translate_tracker:
|
|
567
|
+
llm_translate_tracker.set_placeholder_full_match()
|
|
466
568
|
return [comp]
|
|
467
569
|
|
|
468
570
|
# 构建正则表达式模式
|
|
@@ -473,18 +575,28 @@ class ILTranslator:
|
|
|
473
575
|
for placeholder in input_text.placeholders:
|
|
474
576
|
if isinstance(placeholder, FormulaPlaceholder):
|
|
475
577
|
# 转义特殊字符
|
|
476
|
-
pattern = re.escape(placeholder.placeholder)
|
|
578
|
+
# pattern = re.escape(placeholder.placeholder)
|
|
579
|
+
pattern = placeholder.regex_pattern
|
|
477
580
|
patterns.append(f"({pattern})")
|
|
478
581
|
placeholder_patterns.append(f"({pattern})")
|
|
479
582
|
placeholder_map[placeholder.placeholder] = placeholder
|
|
480
583
|
else:
|
|
481
|
-
left =
|
|
482
|
-
right =
|
|
584
|
+
left = placeholder.left_regex_pattern
|
|
585
|
+
right = placeholder.right_regex_pattern
|
|
483
586
|
patterns.append(f"({left}.*?{right})")
|
|
484
587
|
placeholder_patterns.append(f"({left})")
|
|
485
588
|
placeholder_patterns.append(f"({right})")
|
|
486
589
|
placeholder_map[placeholder.left_placeholder] = placeholder
|
|
487
|
-
|
|
590
|
+
all_match = True
|
|
591
|
+
for pattern in patterns:
|
|
592
|
+
if not re.search(pattern, output, flags=re.IGNORECASE):
|
|
593
|
+
all_match = False
|
|
594
|
+
break
|
|
595
|
+
if all_match:
|
|
596
|
+
if llm_translate_tracker:
|
|
597
|
+
llm_translate_tracker.set_placeholder_full_match()
|
|
598
|
+
else:
|
|
599
|
+
logger.debug(f"Failed to match all placeholder for {input_text.unicode}")
|
|
488
600
|
# 合并所有模式
|
|
489
601
|
combined_pattern = "|".join(patterns)
|
|
490
602
|
combined_placeholder_pattern = "|".join(placeholder_patterns)
|
|
@@ -534,13 +646,12 @@ class ILTranslator:
|
|
|
534
646
|
p
|
|
535
647
|
for p in input_text.placeholders
|
|
536
648
|
if not isinstance(p, FormulaPlaceholder)
|
|
537
|
-
and
|
|
649
|
+
and re.match(f"^{p.left_regex_pattern}", matched_text)
|
|
538
650
|
)
|
|
539
|
-
text =
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
]
|
|
651
|
+
text = re.match(
|
|
652
|
+
f"^{placeholder.left_regex_pattern}(.+){placeholder.right_regex_pattern}$",
|
|
653
|
+
matched_text,
|
|
654
|
+
).group(1)
|
|
544
655
|
|
|
545
656
|
if isinstance(
|
|
546
657
|
placeholder.composition,
|
|
@@ -626,10 +737,12 @@ class ILTranslator:
|
|
|
626
737
|
"""Post-translation processing: update paragraph with translated text."""
|
|
627
738
|
tracker.set_output(translated_text)
|
|
628
739
|
if translated_text == translate_input.unicode:
|
|
740
|
+
if llm_translate_tracker := tracker.last_llm_translate_tracker():
|
|
741
|
+
llm_translate_tracker.set_placeholder_full_match()
|
|
629
742
|
return False
|
|
630
743
|
paragraph.unicode = translated_text
|
|
631
744
|
paragraph.pdf_paragraph_composition = self.parse_translate_output(
|
|
632
|
-
translate_input, translated_text
|
|
745
|
+
translate_input, translated_text, tracker.last_llm_translate_tracker()
|
|
633
746
|
)
|
|
634
747
|
for composition in paragraph.pdf_paragraph_composition:
|
|
635
748
|
if (
|
|
@@ -646,35 +759,75 @@ class ILTranslator:
|
|
|
646
759
|
text: str,
|
|
647
760
|
title_paragraph: PdfParagraph | None = None,
|
|
648
761
|
local_title_paragraph: PdfParagraph | None = None,
|
|
762
|
+
translate_input: TranslateInput | None = None,
|
|
649
763
|
):
|
|
650
764
|
if self.translation_config.custom_system_prompt:
|
|
651
765
|
llm_input = [self.translation_config.custom_system_prompt]
|
|
652
766
|
else:
|
|
653
767
|
llm_input = [
|
|
654
|
-
"You are a professional
|
|
768
|
+
f"You are a professional and reliable machine translation engine responsible for translating the input text into {self.translation_config.lang_out}."
|
|
655
769
|
]
|
|
770
|
+
llm_hint = []
|
|
656
771
|
|
|
657
772
|
if title_paragraph:
|
|
658
|
-
|
|
773
|
+
llm_hint.append(
|
|
659
774
|
f"The first title in the full text: {title_paragraph.unicode}"
|
|
660
775
|
)
|
|
661
776
|
if (
|
|
662
777
|
local_title_paragraph
|
|
663
778
|
and local_title_paragraph.debug_id != title_paragraph.debug_id
|
|
664
779
|
):
|
|
665
|
-
|
|
780
|
+
llm_hint.append(
|
|
666
781
|
f"The most similar title in the full text: {local_title_paragraph.unicode}"
|
|
667
782
|
)
|
|
783
|
+
|
|
784
|
+
if self.translation_config.add_formula_placehold_hint:
|
|
785
|
+
placeholders_hint = translate_input.get_placeholders_hint()
|
|
786
|
+
if placeholders_hint:
|
|
787
|
+
llm_hint.append(
|
|
788
|
+
f"This is the formula placeholder hint: \n{placeholders_hint}"
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
if llm_hint:
|
|
792
|
+
llm_input.append(
|
|
793
|
+
"When translating, please refer to the following information to improve translation quality:"
|
|
794
|
+
)
|
|
795
|
+
for i, line in enumerate(llm_hint):
|
|
796
|
+
llm_input.append(f"{i}. {line}")
|
|
797
|
+
llm_input.append("When translating, please follow the following rules:")
|
|
798
|
+
|
|
799
|
+
rich_text_left_placeholder = (
|
|
800
|
+
self.translate_engine.get_rich_text_left_placeholder(1)
|
|
801
|
+
)
|
|
802
|
+
if isinstance(rich_text_left_placeholder, tuple):
|
|
803
|
+
rich_text_left_placeholder = rich_text_left_placeholder[0]
|
|
804
|
+
rich_text_right_placeholder = (
|
|
805
|
+
self.translate_engine.get_rich_text_right_placeholder(2)
|
|
806
|
+
)
|
|
807
|
+
if isinstance(rich_text_right_placeholder, tuple):
|
|
808
|
+
rich_text_right_placeholder = rich_text_right_placeholder[0]
|
|
809
|
+
|
|
668
810
|
# Create a structured prompt template for LLM translation
|
|
669
811
|
llm_input.append(
|
|
670
|
-
f'
|
|
812
|
+
f'1. Do not translate style tags, such as "{rich_text_left_placeholder}xxx{rich_text_right_placeholder}"!'
|
|
671
813
|
)
|
|
672
814
|
|
|
815
|
+
formula_placeholder = self.translate_engine.get_formular_placeholder(3)
|
|
816
|
+
if isinstance(formula_placeholder, tuple):
|
|
817
|
+
formula_placeholder = formula_placeholder[0]
|
|
818
|
+
|
|
819
|
+
llm_input.append(
|
|
820
|
+
f'2. Do not translate formula placeholders, such as "{formula_placeholder}". The system will automatically replace the placeholders with the corresponding formulas.'
|
|
821
|
+
)
|
|
673
822
|
llm_input.append(
|
|
674
|
-
|
|
823
|
+
"3. If there is no need to translate (such as proper nouns, codes, etc.), then return the original text."
|
|
675
824
|
)
|
|
825
|
+
llm_input.append(
|
|
826
|
+
"4. Only output the translation result without explanations and annotations."
|
|
827
|
+
)
|
|
828
|
+
llm_input.append(f"5. Translate text into {self.translation_config.lang_out}.")
|
|
676
829
|
prompt_template = f"""
|
|
677
|
-
|
|
830
|
+
Now, please carefully read the following text to be translated and directly output your translation.\n\n{text}
|
|
678
831
|
|
|
679
832
|
"""
|
|
680
833
|
llm_input.append(prompt_template)
|
|
@@ -704,18 +857,20 @@ class ILTranslator:
|
|
|
704
857
|
)
|
|
705
858
|
if text is None:
|
|
706
859
|
return
|
|
707
|
-
|
|
860
|
+
llm_translate_tracker = tracker.new_llm_translate_tracker()
|
|
708
861
|
# Perform translation
|
|
709
862
|
if self.support_llm_translate:
|
|
710
863
|
llm_prompt = self.generate_prompt_for_llm(
|
|
711
|
-
text, title_paragraph, local_title_paragraph
|
|
864
|
+
text, title_paragraph, local_title_paragraph, translate_input
|
|
712
865
|
)
|
|
866
|
+
llm_translate_tracker.set_input(llm_prompt)
|
|
713
867
|
translated_text = self.translate_engine.llm_translate(
|
|
714
868
|
llm_prompt,
|
|
715
869
|
rate_limit_params={
|
|
716
870
|
"paragraph_token_count": paragraph_token_count
|
|
717
871
|
},
|
|
718
872
|
)
|
|
873
|
+
llm_translate_tracker.set_output(translated_text)
|
|
719
874
|
else:
|
|
720
875
|
translated_text = self.translate_engine.translate(
|
|
721
876
|
text,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import copy
|
|
1
2
|
import json
|
|
2
3
|
import logging
|
|
3
4
|
import re
|
|
@@ -11,6 +12,7 @@ from babeldoc.document_il import Document
|
|
|
11
12
|
from babeldoc.document_il import Page
|
|
12
13
|
from babeldoc.document_il import PdfFont
|
|
13
14
|
from babeldoc.document_il import PdfParagraph
|
|
15
|
+
from babeldoc.document_il.midend import il_translator
|
|
14
16
|
from babeldoc.document_il.midend.il_translator import DocumentTranslateTracker
|
|
15
17
|
from babeldoc.document_il.midend.il_translator import ILTranslator
|
|
16
18
|
from babeldoc.document_il.midend.il_translator import PageTranslateTracker
|
|
@@ -94,9 +96,11 @@ class ILTranslatorLLMOnly:
|
|
|
94
96
|
# Try to find the first title paragraph
|
|
95
97
|
title_paragraph = self.find_title_paragraph(docs)
|
|
96
98
|
self.translation_config.shared_context_cross_split_part.first_paragraph = (
|
|
99
|
+
copy.deepcopy(title_paragraph)
|
|
100
|
+
)
|
|
101
|
+
self.translation_config.shared_context_cross_split_part.recent_title_paragraph = copy.deepcopy(
|
|
97
102
|
title_paragraph
|
|
98
103
|
)
|
|
99
|
-
self.translation_config.shared_context_cross_split_part.recent_title_paragraph = title_paragraph
|
|
100
104
|
if title_paragraph:
|
|
101
105
|
logger.info(f"Found first title paragraph: {title_paragraph.unicode}")
|
|
102
106
|
|
|
@@ -169,9 +173,11 @@ class ILTranslatorLLMOnly:
|
|
|
169
173
|
total_token_count += self.calc_token_count(paragraph.unicode)
|
|
170
174
|
paragraphs.append(paragraph)
|
|
171
175
|
if paragraph.layout_label == "title":
|
|
172
|
-
self.shared_context_cross_split_part.recent_title_paragraph =
|
|
176
|
+
self.shared_context_cross_split_part.recent_title_paragraph = (
|
|
177
|
+
copy.deepcopy(paragraph)
|
|
178
|
+
)
|
|
173
179
|
|
|
174
|
-
if total_token_count >
|
|
180
|
+
if total_token_count > 200 or len(paragraphs) > 10:
|
|
175
181
|
executor.submit(
|
|
176
182
|
self.translate_paragraph,
|
|
177
183
|
BatchParagraph(paragraphs, tracker),
|
|
@@ -217,7 +223,7 @@ class ILTranslatorLLMOnly:
|
|
|
217
223
|
should_translate_paragraph = []
|
|
218
224
|
try:
|
|
219
225
|
inputs = []
|
|
220
|
-
|
|
226
|
+
llm_translate_trackers = []
|
|
221
227
|
for i in range(len(batch_paragraph.paragraphs)):
|
|
222
228
|
paragraph = batch_paragraph.paragraphs[i]
|
|
223
229
|
tracker = batch_paragraph.trackers[i]
|
|
@@ -227,20 +233,31 @@ class ILTranslatorLLMOnly:
|
|
|
227
233
|
if text is None:
|
|
228
234
|
pbar.advance(1)
|
|
229
235
|
continue
|
|
236
|
+
llm_translate_tracker = tracker.new_llm_translate_tracker()
|
|
230
237
|
should_translate_paragraph.append(i)
|
|
231
|
-
|
|
238
|
+
llm_translate_trackers.append(llm_translate_tracker)
|
|
239
|
+
inputs.append(
|
|
240
|
+
(text, translate_input, paragraph, tracker, llm_translate_tracker)
|
|
241
|
+
)
|
|
232
242
|
if not inputs:
|
|
233
243
|
return
|
|
234
244
|
json_format_input = []
|
|
235
245
|
|
|
236
246
|
for id_, input_text in enumerate(inputs):
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
247
|
+
ti: il_translator.ILTranslator.TranslateInput = input_text[1]
|
|
248
|
+
placeholders_hint = ti.get_placeholders_hint()
|
|
249
|
+
obj = {
|
|
250
|
+
"id": id_,
|
|
251
|
+
"input": input_text[0],
|
|
252
|
+
"layout_label": input_text[2].layout_label,
|
|
253
|
+
}
|
|
254
|
+
if (
|
|
255
|
+
placeholders_hint
|
|
256
|
+
and self.translation_config.add_formula_placehold_hint
|
|
257
|
+
):
|
|
258
|
+
obj["formula_placeholders_hint"] = placeholders_hint
|
|
259
|
+
json_format_input.append(obj)
|
|
260
|
+
|
|
244
261
|
json_format_input = json.dumps(
|
|
245
262
|
json_format_input, ensure_ascii=False, indent=2
|
|
246
263
|
)
|
|
@@ -248,24 +265,57 @@ class ILTranslatorLLMOnly:
|
|
|
248
265
|
llm_input = [self.translation_config.custom_system_prompt]
|
|
249
266
|
else:
|
|
250
267
|
llm_input = [
|
|
251
|
-
"You are a professional
|
|
268
|
+
f"You are a professional and reliable machine translation engine responsible for translating the input text into {self.translation_config.lang_out}."
|
|
252
269
|
]
|
|
253
270
|
|
|
271
|
+
llm_hint = []
|
|
254
272
|
if title_paragraph:
|
|
255
|
-
|
|
273
|
+
llm_hint.append(
|
|
256
274
|
f"The first title in the full text: {title_paragraph.unicode}"
|
|
257
275
|
)
|
|
258
276
|
if (
|
|
259
277
|
local_title_paragraph
|
|
260
278
|
and local_title_paragraph.debug_id != title_paragraph.debug_id
|
|
261
279
|
):
|
|
262
|
-
|
|
280
|
+
llm_hint.append(
|
|
263
281
|
f"The most similar title in the full text: {local_title_paragraph.unicode}"
|
|
264
282
|
)
|
|
283
|
+
|
|
284
|
+
if llm_hint:
|
|
285
|
+
llm_input.append(
|
|
286
|
+
"When translating, please refer to the following information to improve translation quality:"
|
|
287
|
+
)
|
|
288
|
+
for i, line in enumerate(llm_hint):
|
|
289
|
+
llm_input.append(f"{i}. {line}")
|
|
290
|
+
llm_input.append("When translating, please follow the following rules:")
|
|
291
|
+
# Create a structured prompt template for LLM translation
|
|
292
|
+
rich_text_left_placeholder = (
|
|
293
|
+
self.translate_engine.get_rich_text_left_placeholder(1)
|
|
294
|
+
)
|
|
295
|
+
if isinstance(rich_text_left_placeholder, tuple):
|
|
296
|
+
rich_text_left_placeholder = rich_text_left_placeholder[0]
|
|
297
|
+
rich_text_right_placeholder = (
|
|
298
|
+
self.translate_engine.get_rich_text_right_placeholder(2)
|
|
299
|
+
)
|
|
300
|
+
if isinstance(rich_text_right_placeholder, tuple):
|
|
301
|
+
rich_text_right_placeholder = rich_text_right_placeholder[0]
|
|
302
|
+
llm_input.append(
|
|
303
|
+
f'1. Do not translate style tags, such as "{rich_text_left_placeholder}xxx{rich_text_right_placeholder}"!'
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
formula_placeholder = self.translate_engine.get_formular_placeholder(3)
|
|
307
|
+
if isinstance(formula_placeholder, tuple):
|
|
308
|
+
formula_placeholder = formula_placeholder[0]
|
|
309
|
+
llm_input.append(
|
|
310
|
+
f'2. Do not translate formula placeholders, such as "{formula_placeholder}". The system will automatically replace the placeholders with the corresponding formulas.'
|
|
311
|
+
)
|
|
312
|
+
llm_input.append(
|
|
313
|
+
"3. If there is no need to translate (such as proper nouns, codes, etc.), then return the original text."
|
|
314
|
+
)
|
|
265
315
|
# Create a structured prompt template for LLM translation
|
|
266
316
|
prompt_template = (
|
|
267
317
|
f"""
|
|
268
|
-
You will be given a JSON formatted input containing entries with "id" and "input" fields.
|
|
318
|
+
4. You will be given a JSON formatted input containing entries with "id" and "input" fields.
|
|
269
319
|
|
|
270
320
|
For each entry in the JSON, translate the contents of the "input" field into {self.translation_config.lang_out}.
|
|
271
321
|
Write the translation back into the "output" field for that entry.
|
|
@@ -280,7 +330,12 @@ class ILTranslatorLLMOnly:
|
|
|
280
330
|
{
|
|
281
331
|
"id": 1,
|
|
282
332
|
"input": "Source",
|
|
283
|
-
"layout_label": "plain text"
|
|
333
|
+
"layout_label": "plain text",
|
|
334
|
+
// this is optional
|
|
335
|
+
"formula_placeholders_hint": {
|
|
336
|
+
"placeholder1": "hint1",
|
|
337
|
+
"placeholder2": "hint2"
|
|
338
|
+
}
|
|
284
339
|
}
|
|
285
340
|
```
|
|
286
341
|
Output:
|
|
@@ -296,13 +351,6 @@ class ILTranslatorLLMOnly:
|
|
|
296
351
|
"""
|
|
297
352
|
)
|
|
298
353
|
llm_input.append(prompt_template)
|
|
299
|
-
llm_input.append(
|
|
300
|
-
f'Please do not translate style tags like "{self.translate_engine.get_rich_text_left_placeholder(1)}xxx{self.translate_engine.get_rich_text_right_placeholder(2)}"!'
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
llm_input.append(
|
|
304
|
-
f'Please do not translate formula placeholders like "{self.translate_engine.get_formular_placeholder(3)}"!'
|
|
305
|
-
)
|
|
306
354
|
llm_input.append(
|
|
307
355
|
f"""Here is the input:
|
|
308
356
|
|
|
@@ -311,17 +359,24 @@ class ILTranslatorLLMOnly:
|
|
|
311
359
|
```"""
|
|
312
360
|
)
|
|
313
361
|
final_input = "\n".join(llm_input).strip()
|
|
362
|
+
for llm_translate_tracker in llm_translate_trackers:
|
|
363
|
+
llm_translate_tracker.set_input(final_input)
|
|
314
364
|
llm_output = self.translate_engine.llm_translate(
|
|
315
365
|
final_input,
|
|
316
366
|
rate_limit_params={"paragraph_token_count": paragraph_token_count},
|
|
317
367
|
)
|
|
368
|
+
for llm_translate_tracker in llm_translate_trackers:
|
|
369
|
+
llm_translate_tracker.set_output(llm_output)
|
|
318
370
|
llm_output = llm_output.strip()
|
|
319
371
|
|
|
320
372
|
llm_output = self._clean_json_output(llm_output)
|
|
321
373
|
|
|
322
374
|
parsed_output = json.loads(llm_output)
|
|
323
375
|
|
|
324
|
-
translation_results = {
|
|
376
|
+
translation_results = {
|
|
377
|
+
item["id"]: item.get("output", item.get("input"))
|
|
378
|
+
for item in parsed_output
|
|
379
|
+
}
|
|
325
380
|
|
|
326
381
|
if len(translation_results) != len(inputs):
|
|
327
382
|
raise Exception(
|
|
@@ -347,6 +402,7 @@ class ILTranslatorLLMOnly:
|
|
|
347
402
|
|
|
348
403
|
# Get the original input for this translation
|
|
349
404
|
translate_input = inputs[id_][1]
|
|
405
|
+
llm_translate_tracker = inputs[id_][4]
|
|
350
406
|
|
|
351
407
|
input_unicode = inputs[id_][2].unicode
|
|
352
408
|
output_unicode = translated_text
|
|
@@ -355,6 +411,9 @@ class ILTranslatorLLMOnly:
|
|
|
355
411
|
output_token_count = self.calc_token_count(output_unicode)
|
|
356
412
|
|
|
357
413
|
if not (0.3 < output_token_count / input_token_count < 3):
|
|
414
|
+
llm_translate_tracker.set_error_message(
|
|
415
|
+
f"Translation result is too long or too short. Input: {input_token_count}, Output: {output_token_count}"
|
|
416
|
+
)
|
|
358
417
|
logger.warning(
|
|
359
418
|
f"Translation result is too long or too short. Input: {input_token_count}, Output: {output_token_count}"
|
|
360
419
|
)
|
|
@@ -362,6 +421,9 @@ class ILTranslatorLLMOnly:
|
|
|
362
421
|
|
|
363
422
|
edit_distance = Levenshtein.distance(input_unicode, output_unicode)
|
|
364
423
|
if edit_distance < 5 and input_token_count > 20:
|
|
424
|
+
llm_translate_tracker.set_error_message(
|
|
425
|
+
f"Translation result edit distance is too small. distance: {edit_distance}, input: {input_unicode}, output: {output_unicode}"
|
|
426
|
+
)
|
|
365
427
|
logger.warning(
|
|
366
428
|
f"Translation result edit distance is too small. distance: {edit_distance}, input: {input_unicode}, output: {output_unicode}"
|
|
367
429
|
)
|
|
@@ -377,11 +439,15 @@ class ILTranslatorLLMOnly:
|
|
|
377
439
|
if pbar:
|
|
378
440
|
pbar.advance(1)
|
|
379
441
|
except Exception as e:
|
|
380
|
-
|
|
442
|
+
error_message = f"Error translating paragraph. Error: {e}."
|
|
443
|
+
logger.exception(error_message)
|
|
381
444
|
# Ignore error and continue
|
|
445
|
+
for llm_translate_tracker in llm_translate_trackers:
|
|
446
|
+
llm_translate_tracker.set_error_message(error_message)
|
|
382
447
|
continue
|
|
383
448
|
finally:
|
|
384
449
|
if should_fallback:
|
|
450
|
+
inputs[id_][4].set_fallback_to_translate()
|
|
385
451
|
logger.warning(
|
|
386
452
|
f"Fallback to simple translation. paragraph id: {inputs[id_][2].debug_id}"
|
|
387
453
|
)
|
|
@@ -402,7 +468,11 @@ class ILTranslatorLLMOnly:
|
|
|
402
468
|
)
|
|
403
469
|
|
|
404
470
|
except Exception as e:
|
|
405
|
-
|
|
471
|
+
error_message = f"Error {e} during translation. try fallback"
|
|
472
|
+
logger.warning(error_message)
|
|
473
|
+
for llm_translate_tracker in llm_translate_trackers:
|
|
474
|
+
llm_translate_tracker.set_error_message(error_message)
|
|
475
|
+
llm_translate_tracker.set_fallback_to_translate()
|
|
406
476
|
if not should_translate_paragraph:
|
|
407
477
|
should_translate_paragraph = list(
|
|
408
478
|
range(len(batch_paragraph.paragraphs))
|
|
@@ -277,11 +277,14 @@ class OpenAITranslator(BaseTranslator):
|
|
|
277
277
|
logger.exception("Error updating token count")
|
|
278
278
|
|
|
279
279
|
def get_formular_placeholder(self, placeholder_id: int):
|
|
280
|
-
return "{
|
|
280
|
+
return "{v" + str(placeholder_id) + "}", f"{{\\s*v\\s*{placeholder_id}\\s*}}"
|
|
281
281
|
return "{{" + str(placeholder_id) + "}}"
|
|
282
282
|
|
|
283
283
|
def get_rich_text_left_placeholder(self, placeholder_id: int):
|
|
284
|
-
return
|
|
284
|
+
return (
|
|
285
|
+
f"<style id='{placeholder_id}'>",
|
|
286
|
+
f"<\\s*style\\s*id\\s*=\\s*'\\s*{placeholder_id}\\s*'\\s*>",
|
|
287
|
+
)
|
|
285
288
|
|
|
286
289
|
def get_rich_text_right_placeholder(self, placeholder_id: int):
|
|
287
|
-
return "</style>"
|
|
290
|
+
return "</style>", r"<\s*\/\s*style\s*>"
|
|
@@ -651,8 +651,8 @@ def _do_translate_single(
|
|
|
651
651
|
|
|
652
652
|
mediabox_data = fix_media_box(doc_pdf2zh)
|
|
653
653
|
|
|
654
|
-
for page in doc_pdf2zh:
|
|
655
|
-
|
|
654
|
+
# for page in doc_pdf2zh:
|
|
655
|
+
# page.insert_font(resfont, None)
|
|
656
656
|
|
|
657
657
|
resfont = None
|
|
658
658
|
doc_pdf2zh.save(temp_pdf_path)
|
|
@@ -70,6 +70,7 @@ class TranslationConfig:
|
|
|
70
70
|
skip_scanned_detection: bool = False,
|
|
71
71
|
ocr_workaround: bool = False,
|
|
72
72
|
custom_system_prompt: str | None = None,
|
|
73
|
+
add_formula_placehold_hint: bool = False,
|
|
73
74
|
):
|
|
74
75
|
self.translator = translator
|
|
75
76
|
|
|
@@ -155,6 +156,7 @@ class TranslationConfig:
|
|
|
155
156
|
self.table_model = table_model
|
|
156
157
|
self.show_char_box = show_char_box
|
|
157
158
|
self.custom_system_prompt = custom_system_prompt
|
|
159
|
+
self.add_formula_placehold_hint = add_formula_placehold_hint
|
|
158
160
|
|
|
159
161
|
def parse_pages(self, pages_str: str | None) -> list[tuple[int, int]] | None:
|
|
160
162
|
"""解析页码字符串,返回页码范围列表
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "BabelDOC"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.41"
|
|
4
4
|
description = "Yet Another Document Translator"
|
|
5
5
|
license = "AGPL-3.0"
|
|
6
6
|
readme = "README.md"
|
|
@@ -150,7 +150,7 @@ pythonpath = [".", "src"]
|
|
|
150
150
|
testpaths = ["tests"]
|
|
151
151
|
|
|
152
152
|
[bumpver]
|
|
153
|
-
current_version = "0.3.
|
|
153
|
+
current_version = "0.3.41"
|
|
154
154
|
version_pattern = "MAJOR.MINOR.PATCH[.PYTAGNUM]"
|
|
155
155
|
|
|
156
156
|
[bumpver.file_patterns]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.39"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.3.39 → babeldoc-0.3.41}/babeldoc/document_il/utils/priority_thread_pool_executor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.3.39 → babeldoc-0.3.41}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-big-logo-with-transparent-background.png
RENAMED
|
File without changes
|
{babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-big-logo-with-transparent-background.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-small-logo-with-transparent-background.png
RENAMED
|
File without changes
|
{babeldoc-0.3.39 → babeldoc-0.3.41}/docs/images/babeldoc-small-logo-with-transparent-background.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|