BabelDOC 0.5.19__tar.gz → 0.5.20__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.5.19 → babeldoc-0.5.20}/PKG-INFO +1 -1
- babeldoc-0.5.20/babeldoc/__init__.py +1 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/const.py +1 -1
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/automatic_term_extractor.py +37 -11
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/il_translator.py +265 -99
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/il_translator_llm_only.py +171 -149
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/main.py +23 -1
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/translator/translator.py +11 -6
- {babeldoc-0.5.19 → babeldoc-0.5.20}/pyproject.toml +2 -2
- babeldoc-0.5.19/babeldoc/__init__.py +0 -1
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.cursorignore +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/PULL_REQUEST_TEMPLATE/pr_form.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/dependabot.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/labels.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/release-drafter.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/workflows/codeql.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/workflows/docs.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/workflows/labeler.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/workflows/lint.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/workflows/pr-lint.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/workflows/publish-to-pypi.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.github/workflows/test.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.gitignore +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/.pre-commit-config.yaml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/LICENSE +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/README.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/assets/assets.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/assets/embedding_assets_metadata.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/asynchronize/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/babeldoc_exception/BabelDOCException.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/babeldoc_exception/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/README.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/base_doclayout.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/doclayout.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/rpc_doclayout.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/rpc_doclayout2.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/rpc_doclayout3.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/rpc_doclayout4.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/rpc_doclayout5.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/rpc_doclayout6.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/rpc_doclayout7.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/docvision/table_detection/rapidocr.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/babelpdf/base14.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/babelpdf/cidfont.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/babelpdf/encoding.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/babelpdf/utils.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/babelpdf/win_core.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/converter.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/backend/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/backend/pdf_creater.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/frontend/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/frontend/il_creater.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/il_version_1.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/il_version_1.rnc +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/il_version_1.rng +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/il_version_1.xsd +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/add_debug_information.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/detect_scanned_file.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/layout_parser.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/paragraph_finder.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/remove_descent.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/styles_and_formulas.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/table_parser.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/midend/typesetting.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/extract_char.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/fontmap.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/formular_helper.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/layout_helper.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/matrix_helper.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/mupdf_helper.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/paragraph_helper.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/spatial_analyzer.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/style_helper.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/utils/zstd_helper.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/document_il/xml_converter.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/high_level.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/pdfinterp.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/result_merger.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/split_manager.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/format/pdf/translation_config.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/glossary.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/LICENSE +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/_saslprep.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/arcfour.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/ascii85.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/casting.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/ccitt.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/78-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/78-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/78-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/78-RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/78-RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/78-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/78ms-RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/78ms-RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/83pv-RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/83pv-RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/90ms-RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/90ms-RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/90msp-RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/90msp-RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/90pv-RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/90pv-RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Add-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Add-RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Add-RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Add-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/B5pc-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/B5pc-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/CNS-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/CNS-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/CNS1-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/CNS1-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/CNS2-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/CNS2-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/ETHK-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/ETHK-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/ETen-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/ETen-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/ETenms-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/ETenms-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Ext-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Ext-RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Ext-RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Ext-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GB-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GB-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GB-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GB-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBK-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBK-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBK2K-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBK2K-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBKp-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBKp-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBT-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBT-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBT-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBT-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBTpc-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBTpc-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBpc-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/GBpc-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKdla-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKdla-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKdlb-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKdlb-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKgccs-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKgccs-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKm314-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKm314-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKm471-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKm471-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKscs-B5-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/HKscs-B5-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Hankaku-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Hankaku-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Hiragana-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Hiragana-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSC-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSC-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSC-Johab-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSC-Johab-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSCms-UHC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSCms-UHC-HW-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSCms-UHC-HW-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSCms-UHC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSCpc-EUC-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/KSCpc-EUC-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Katakana-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Katakana-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/NWP-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/NWP-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/README.txt +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/RKSJ-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/RKSJ-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Roman-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/Roman-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniCNS-UCS2-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniCNS-UCS2-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniCNS-UTF16-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniCNS-UTF16-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniCNS-UTF32-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniCNS-UTF32-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniCNS-UTF8-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniCNS-UTF8-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniGB-UCS2-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniGB-UCS2-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniGB-UTF16-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniGB-UTF16-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniGB-UTF32-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniGB-UTF32-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniGB-UTF8-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniGB-UTF8-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UCS2-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UCS2-HW-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UCS2-HW-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UCS2-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UTF16-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UTF16-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UTF32-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UTF32-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UTF8-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS-UTF8-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS2004-UTF16-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS2004-UTF16-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS2004-UTF32-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS2004-UTF32-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS2004-UTF8-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJIS2004-UTF8-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJISX0213-UTF32-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJISX0213-UTF32-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJISX02132004-UTF32-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniJISX02132004-UTF32-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniKS-UCS2-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniKS-UCS2-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniKS-UTF16-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniKS-UTF16-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniKS-UTF32-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniKS-UTF32-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniKS-UTF8-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/UniKS-UTF8-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/WP-Symbol-H.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/WP-Symbol-V.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/to-unicode-Adobe-CNS1.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/to-unicode-Adobe-GB1.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/to-unicode-Adobe-Japan1.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmap/to-unicode-Adobe-Korea1.pickle.gz +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/cmapdb.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/converter.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/data_structures.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/encodingdb.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/fontmetrics.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/glyphlist.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/high_level.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/image.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/jbig2.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/latin_enc.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/layout.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/lzw.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdfcolor.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdfdevice.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdfdocument.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdfexceptions.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdffont.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdfinterp.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdfpage.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdfparser.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/pdftypes.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/psexceptions.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/psparser.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/py.typed +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/runlength.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/settings.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/pdfminer/utils.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/progress_monitor.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/tools/generate_font_metadata.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/tools/italic_assistance.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/tools/italic_recognize_tool.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/translator/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/translator/cache.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/utils/__init__.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/utils/atomic_integer.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/utils/memory.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/babeldoc/utils/priority_thread_pool_executor.py +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/CODE_OF_CONDUCT.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/CONTRIBUTING.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/CONTRIBUTOR_REWARD.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/ImplementationDetails/README.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/README.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/deploy.sh +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/example/demo_glossary.csv +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-banner.png +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-big-logo.png +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-contributor_reward_example.png +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-preview.gif +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/images/babeldoc-small-logo.png +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/index.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/intro-to-pdf-object.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/requirements.txt +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/docs/supported_languages.md +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/mkdocs.yml +0 -0
- {babeldoc-0.5.19 → babeldoc-0.5.20}/tests/test_translation_cache_cleanup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.20"
|
|
@@ -29,25 +29,39 @@ if TYPE_CHECKING:
|
|
|
29
29
|
logger = logging.getLogger(__name__)
|
|
30
30
|
|
|
31
31
|
LLM_PROMPT_TEMPLATE: str = """
|
|
32
|
-
You are an expert multilingual terminologist.
|
|
33
|
-
Key terms include:
|
|
34
|
-
1. Named Entities (people, organizations, locations, dates, etc.).
|
|
35
|
-
2. Subject-specific nouns or noun phrases that are repeated or central to the text's meaning.
|
|
32
|
+
You are an expert multilingual terminologist. Extract key terms from the text and translate them into {target_language}.
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
### Extraction Rules
|
|
35
|
+
1. Include only: named entities (people, orgs, locations, theorem/algorithm names, dates) and domain-specific nouns/noun phrases essential to meaning.
|
|
36
|
+
2. No full sentences. Ignore function words.
|
|
37
|
+
3. Use minimal noun phrases (≤5 words unless a named entity). No generic academic nouns (e.g., model, case, property) unless part of a standard term.
|
|
38
|
+
4. No mathematical items: variables (X1, a, ε), symbols (=, +, →, ⊥⊥, ∈), subscripts/superscripts, formula fragments, mappings (T: H1→H2), etc. Keep only natural-language concepts.
|
|
39
|
+
5. Extract each term once. Keep order of first appearance.
|
|
40
|
+
|
|
41
|
+
### Translation Rules
|
|
42
|
+
1. Translate each term into {target_language}.
|
|
43
|
+
2. If in the reference glossary, use its translation exactly.
|
|
44
|
+
3. Keep proper names in original language unless a well-known translation exists.
|
|
45
|
+
4. Ensure consistent translations.
|
|
40
46
|
|
|
41
47
|
{reference_glossary_section}
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
### Output Format
|
|
50
|
+
- Return ONLY a valid JSON array.
|
|
51
|
+
- Each element: {{"src": "...", "tgt": "..."}}.
|
|
52
|
+
- No comments, no backticks, no extra text.
|
|
53
|
+
- If no terms: [].
|
|
54
|
+
|
|
55
|
+
### Example
|
|
56
|
+
For terms “LLM”, “GPT”:
|
|
57
|
+
{example_output}
|
|
44
58
|
|
|
45
59
|
Input Text:
|
|
46
60
|
```
|
|
47
61
|
{text_to_process}
|
|
48
62
|
```
|
|
49
63
|
|
|
50
|
-
Return JSON ONLY
|
|
64
|
+
Return JSON ONLY. NO OTHER TEXT.
|
|
51
65
|
Result:
|
|
52
66
|
"""
|
|
53
67
|
|
|
@@ -77,6 +91,7 @@ class DocumentTermExtractTracker:
|
|
|
77
91
|
paragraphs = []
|
|
78
92
|
for para in page.paragraph:
|
|
79
93
|
o_str = getattr(para, "output", None)
|
|
94
|
+
i_str = getattr(para, "input", None)
|
|
80
95
|
pdf_unicodes = getattr(para, "pdf_unicodes", None)
|
|
81
96
|
if not pdf_unicodes:
|
|
82
97
|
continue
|
|
@@ -84,6 +99,7 @@ class DocumentTermExtractTracker:
|
|
|
84
99
|
{
|
|
85
100
|
"pdf_unicodes": pdf_unicodes,
|
|
86
101
|
"output": o_str,
|
|
102
|
+
"input": i_str,
|
|
87
103
|
},
|
|
88
104
|
)
|
|
89
105
|
pages.append({"paragraph": paragraphs})
|
|
@@ -110,6 +126,9 @@ class ParagraphTermExtractTracker:
|
|
|
110
126
|
def set_output(self, output: str):
|
|
111
127
|
self.output = output
|
|
112
128
|
|
|
129
|
+
def set_input(self, _input: str):
|
|
130
|
+
self.input = _input
|
|
131
|
+
|
|
113
132
|
|
|
114
133
|
class AutomaticTermExtractor:
|
|
115
134
|
stage_name = "Automatic Term Extraction"
|
|
@@ -299,8 +318,12 @@ class AutomaticTermExtractor:
|
|
|
299
318
|
target_language=self.translation_config.lang_out,
|
|
300
319
|
text_to_process="\n\n".join(inputs),
|
|
301
320
|
reference_glossary_section=reference_glossary_section,
|
|
321
|
+
example_output="""[
|
|
322
|
+
{"src": "LLM", "tgt": "大语言模型"},
|
|
323
|
+
{"src": "GPT", "tgt": "GPT"}
|
|
324
|
+
]""",
|
|
302
325
|
)
|
|
303
|
-
|
|
326
|
+
tracker.set_input(prompt)
|
|
304
327
|
output = self.translate_engine.llm_translate(
|
|
305
328
|
prompt,
|
|
306
329
|
rate_limit_params={
|
|
@@ -363,7 +386,10 @@ class AutomaticTermExtractor:
|
|
|
363
386
|
end_cache_hit_prompt - start_cache_hit_prompt,
|
|
364
387
|
)
|
|
365
388
|
|
|
366
|
-
if
|
|
389
|
+
if (
|
|
390
|
+
self.translation_config.debug
|
|
391
|
+
or self.translation_config.working_dir is not None
|
|
392
|
+
):
|
|
367
393
|
path = self.translation_config.get_working_file_path(
|
|
368
394
|
"term_extractor_tracking.json"
|
|
369
395
|
)
|
|
@@ -6,6 +6,7 @@ import logging
|
|
|
6
6
|
import re
|
|
7
7
|
import threading
|
|
8
8
|
from pathlib import Path
|
|
9
|
+
from string import Template
|
|
9
10
|
|
|
10
11
|
import tiktoken
|
|
11
12
|
from tqdm import tqdm
|
|
@@ -46,6 +47,33 @@ from babeldoc.utils.priority_thread_pool_executor import PriorityThreadPoolExecu
|
|
|
46
47
|
logger = logging.getLogger(__name__)
|
|
47
48
|
|
|
48
49
|
|
|
50
|
+
PROMPT_TEMPLATE = Template(
|
|
51
|
+
"""$role_block
|
|
52
|
+
|
|
53
|
+
## Rules
|
|
54
|
+
|
|
55
|
+
1. Keep the structure exactly unchanged: do NOT add/remove/reorder any tags, placeholders, or tokens.
|
|
56
|
+
2. Keep all tags unchanged (e.g., <style>, <b>, </style>).
|
|
57
|
+
- Translate human-readable text inside tags.
|
|
58
|
+
- Do NOT translate text inside <code>…</code>.
|
|
59
|
+
3. Do NOT translate or alter placeholders: {v1}, {name}, %s, %d, [[...]], %%...%%.
|
|
60
|
+
4. If the entire input is pure code/identifiers, return it unchanged.
|
|
61
|
+
5. Translate ALL human-readable content into $lang_out.
|
|
62
|
+
|
|
63
|
+
$glossary_block
|
|
64
|
+
|
|
65
|
+
$context_block
|
|
66
|
+
|
|
67
|
+
## Output
|
|
68
|
+
|
|
69
|
+
Output ONLY the translated $lang_out text. No explanations, no backticks, no extra text.
|
|
70
|
+
|
|
71
|
+
Now translate the following text:
|
|
72
|
+
|
|
73
|
+
$text_to_translate"""
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
49
77
|
class RichTextPlaceholder:
|
|
50
78
|
def __init__(
|
|
51
79
|
self,
|
|
@@ -167,6 +195,12 @@ class DocumentTranslateTracker:
|
|
|
167
195
|
pdf_unicode = getattr(para, "pdf_unicode", None)
|
|
168
196
|
llm_translate_trackers = getattr(para, "llm_translate_trackers", None)
|
|
169
197
|
placeholders = getattr(para, "placeholders", None)
|
|
198
|
+
original_placeholders = getattr(para, "original_placeholders", None)
|
|
199
|
+
removed_hallucinated_placeholders = getattr(
|
|
200
|
+
para,
|
|
201
|
+
"removed_hallucinated_placeholders",
|
|
202
|
+
None,
|
|
203
|
+
)
|
|
170
204
|
|
|
171
205
|
llm_translate_trackers_json = []
|
|
172
206
|
if llm_translate_trackers:
|
|
@@ -188,6 +222,8 @@ class DocumentTranslateTracker:
|
|
|
188
222
|
"placeholders": placeholders_json,
|
|
189
223
|
"multi_paragraph_id": getattr(para, "multi_paragraph_id", None),
|
|
190
224
|
"multi_paragraph_index": getattr(para, "multi_paragraph_index", None),
|
|
225
|
+
"original_placeholders": original_placeholders,
|
|
226
|
+
"removed_hallucinated_placeholders": removed_hallucinated_placeholders,
|
|
191
227
|
}
|
|
192
228
|
paragraphs.append(
|
|
193
229
|
paragraph_json,
|
|
@@ -208,6 +244,8 @@ class PageTranslateTracker:
|
|
|
208
244
|
class ParagraphTranslateTracker:
|
|
209
245
|
def __init__(self):
|
|
210
246
|
self.llm_translate_trackers = []
|
|
247
|
+
self.original_placeholders: dict[str, int] = {}
|
|
248
|
+
self.removed_hallucinated_placeholders: dict[str, int] = {}
|
|
211
249
|
|
|
212
250
|
def set_pdf_unicode(self, unicode: str):
|
|
213
251
|
self.pdf_unicode = unicode
|
|
@@ -220,6 +258,10 @@ class ParagraphTranslateTracker:
|
|
|
220
258
|
):
|
|
221
259
|
self.placeholders = placeholders
|
|
222
260
|
|
|
261
|
+
def set_original_placeholders(self, placeholders: dict[str, int] | None):
|
|
262
|
+
"""Record original placeholder-like tokens from the source text."""
|
|
263
|
+
self.original_placeholders = placeholders or {}
|
|
264
|
+
|
|
223
265
|
def record_multi_paragraph_id(self, mid):
|
|
224
266
|
self.multi_paragraph_id = mid
|
|
225
267
|
|
|
@@ -229,6 +271,14 @@ class ParagraphTranslateTracker:
|
|
|
229
271
|
def set_output(self, output: str):
|
|
230
272
|
self.output = output
|
|
231
273
|
|
|
274
|
+
def record_removed_hallucinated_placeholder(self, token: str):
|
|
275
|
+
"""Record placeholder-like tokens removed from translated text."""
|
|
276
|
+
if not token:
|
|
277
|
+
return
|
|
278
|
+
self.removed_hallucinated_placeholders[token] = (
|
|
279
|
+
self.removed_hallucinated_placeholders.get(token, 0) + 1
|
|
280
|
+
)
|
|
281
|
+
|
|
232
282
|
def new_llm_translate_tracker(self) -> LLMTranslateTracker:
|
|
233
283
|
tracker = LLMTranslateTracker()
|
|
234
284
|
self.llm_translate_trackers.append(tracker)
|
|
@@ -315,6 +365,20 @@ class ILTranslator:
|
|
|
315
365
|
self.add_content_filter_hint_lock = threading.Lock()
|
|
316
366
|
self.docs = None
|
|
317
367
|
|
|
368
|
+
# Pre-compile patterns for placeholder-like tokens that may be hallucinated by LLM.
|
|
369
|
+
# We only consider the same shapes as our own formula & rich-text placeholders.
|
|
370
|
+
self._formula_placeholder_pattern = re.compile(
|
|
371
|
+
self.translate_engine.get_formular_placeholder(r"\d+")[1], re.IGNORECASE
|
|
372
|
+
)
|
|
373
|
+
self._style_left_placeholder_pattern = re.compile(
|
|
374
|
+
self.translate_engine.get_rich_text_left_placeholder(r"\d+")[1],
|
|
375
|
+
re.IGNORECASE,
|
|
376
|
+
)
|
|
377
|
+
self._style_right_placeholder_pattern = re.compile(
|
|
378
|
+
self.translate_engine.get_rich_text_right_placeholder(r"\d+")[1],
|
|
379
|
+
re.IGNORECASE,
|
|
380
|
+
)
|
|
381
|
+
|
|
318
382
|
def calc_token_count(self, text: str) -> int:
|
|
319
383
|
try:
|
|
320
384
|
return len(self.tokenizer.encode(text, disallowed_special=()))
|
|
@@ -351,7 +415,10 @@ class ILTranslator:
|
|
|
351
415
|
|
|
352
416
|
path = self.translation_config.get_working_file_path("translate_tracking.json")
|
|
353
417
|
|
|
354
|
-
if
|
|
418
|
+
if (
|
|
419
|
+
self.translation_config.debug
|
|
420
|
+
or self.translation_config.working_dir is not None
|
|
421
|
+
):
|
|
355
422
|
logger.debug(f"save translate tracking to {path}")
|
|
356
423
|
with Path(path).open("w", encoding="utf-8") as f:
|
|
357
424
|
f.write(tracker.to_json())
|
|
@@ -419,6 +486,13 @@ class ILTranslator:
|
|
|
419
486
|
self.unicode = unicode
|
|
420
487
|
self.placeholders = placeholders
|
|
421
488
|
self.base_style = base_style
|
|
489
|
+
# Original placeholder-like tokens extracted from the source text.
|
|
490
|
+
# Key: exact matched token string; Value: occurrence count.
|
|
491
|
+
self.original_placeholder_tokens: dict[str, int] = {}
|
|
492
|
+
|
|
493
|
+
def set_original_placeholder_tokens(self, tokens: dict[str, int] | None):
|
|
494
|
+
"""Attach original placeholder-like tokens from source text."""
|
|
495
|
+
self.original_placeholder_tokens = tokens or {}
|
|
422
496
|
|
|
423
497
|
def get_placeholders_hint(self) -> dict[str, str] | None:
|
|
424
498
|
hint = {}
|
|
@@ -510,6 +584,22 @@ class ILTranslator:
|
|
|
510
584
|
# Skip paragraphs with only placeholders
|
|
511
585
|
if is_placeholder_only_paragraph(paragraph):
|
|
512
586
|
return None
|
|
587
|
+
|
|
588
|
+
# Extract original placeholder-like tokens from the raw paragraph text
|
|
589
|
+
original_placeholder_tokens: dict[str, int] = {}
|
|
590
|
+
|
|
591
|
+
def scan_placeholder_tokens(text: str, tokens: dict[str, int]):
|
|
592
|
+
for pattern in (
|
|
593
|
+
self._formula_placeholder_pattern,
|
|
594
|
+
self._style_left_placeholder_pattern,
|
|
595
|
+
self._style_right_placeholder_pattern,
|
|
596
|
+
):
|
|
597
|
+
for match in pattern.finditer(text):
|
|
598
|
+
token = match.group(0)
|
|
599
|
+
tokens[token] = tokens.get(token, 0) + 1
|
|
600
|
+
|
|
601
|
+
if paragraph.unicode:
|
|
602
|
+
scan_placeholder_tokens(paragraph.unicode, original_placeholder_tokens)
|
|
513
603
|
if len(paragraph.pdf_paragraph_composition) == 1:
|
|
514
604
|
# 如果整个段落只有一个组成部分,那么直接返回,不需要套占位符等
|
|
515
605
|
composition = paragraph.pdf_paragraph_composition[0]
|
|
@@ -518,7 +608,15 @@ class ILTranslator:
|
|
|
518
608
|
or composition.pdf_same_style_characters
|
|
519
609
|
or composition.pdf_character
|
|
520
610
|
):
|
|
521
|
-
|
|
611
|
+
translate_input = self.TranslateInput(
|
|
612
|
+
paragraph.unicode,
|
|
613
|
+
[],
|
|
614
|
+
paragraph.pdf_style,
|
|
615
|
+
)
|
|
616
|
+
translate_input.set_original_placeholder_tokens(
|
|
617
|
+
original_placeholder_tokens,
|
|
618
|
+
)
|
|
619
|
+
return translate_input
|
|
522
620
|
elif composition.pdf_formula:
|
|
523
621
|
# 不需要翻译纯公式
|
|
524
622
|
return None
|
|
@@ -627,7 +725,9 @@ class ILTranslator:
|
|
|
627
725
|
return self.get_translate_input(paragraph, page_font_map, True)
|
|
628
726
|
|
|
629
727
|
text = get_char_unicode_string(chars)
|
|
630
|
-
|
|
728
|
+
translate_input = self.TranslateInput(text, placeholders, paragraph.pdf_style)
|
|
729
|
+
translate_input.set_original_placeholder_tokens(original_placeholder_tokens)
|
|
730
|
+
return translate_input
|
|
631
731
|
|
|
632
732
|
def process_formula(
|
|
633
733
|
self,
|
|
@@ -668,6 +768,7 @@ class ILTranslator:
|
|
|
668
768
|
self,
|
|
669
769
|
input_text: TranslateInput,
|
|
670
770
|
output: str,
|
|
771
|
+
tracker: ParagraphTranslateTracker | None = None,
|
|
671
772
|
llm_translate_tracker: LLMTranslateTracker | None = None,
|
|
672
773
|
) -> [PdfParagraphComposition]:
|
|
673
774
|
result = []
|
|
@@ -715,9 +816,42 @@ class ILTranslator:
|
|
|
715
816
|
# 合并所有模式
|
|
716
817
|
combined_pattern = "|".join(patterns)
|
|
717
818
|
combined_placeholder_pattern = "|".join(placeholder_patterns)
|
|
819
|
+
# Build allowed placeholder tokens: originals from source + placeholders we injected.
|
|
820
|
+
allowed_placeholder_tokens: set[str] = set()
|
|
821
|
+
if getattr(input_text, "original_placeholder_tokens", None):
|
|
822
|
+
allowed_placeholder_tokens.update(input_text.original_placeholder_tokens)
|
|
823
|
+
for placeholder in input_text.placeholders:
|
|
824
|
+
if isinstance(placeholder, FormulaPlaceholder):
|
|
825
|
+
allowed_placeholder_tokens.add(placeholder.placeholder)
|
|
826
|
+
else:
|
|
827
|
+
allowed_placeholder_tokens.add(placeholder.left_placeholder)
|
|
828
|
+
allowed_placeholder_tokens.add(placeholder.right_placeholder)
|
|
718
829
|
|
|
719
830
|
def remove_placeholder(text: str):
|
|
720
|
-
|
|
831
|
+
"""Remove placeholder artifacts and hallucinated placeholder-like tokens."""
|
|
832
|
+
# First, remove any leftover placeholders built from our own regex patterns.
|
|
833
|
+
if combined_placeholder_pattern:
|
|
834
|
+
text = re.sub(
|
|
835
|
+
combined_placeholder_pattern,
|
|
836
|
+
"",
|
|
837
|
+
text,
|
|
838
|
+
flags=re.IGNORECASE,
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
# Then, detect placeholder-like tokens of the same shapes as our own
|
|
842
|
+
# formula and rich-text placeholders. Only keep those in the allowed set.
|
|
843
|
+
def _replace_token(match: re.Match) -> str:
|
|
844
|
+
token = match.group(0)
|
|
845
|
+
if token in allowed_placeholder_tokens:
|
|
846
|
+
return token
|
|
847
|
+
if tracker is not None:
|
|
848
|
+
tracker.record_removed_hallucinated_placeholder(token)
|
|
849
|
+
return ""
|
|
850
|
+
|
|
851
|
+
text = self._formula_placeholder_pattern.sub(_replace_token, text)
|
|
852
|
+
text = self._style_left_placeholder_pattern.sub(_replace_token, text)
|
|
853
|
+
text = self._style_right_placeholder_pattern.sub(_replace_token, text)
|
|
854
|
+
return text
|
|
721
855
|
|
|
722
856
|
# 找到所有匹配
|
|
723
857
|
last_end = 0
|
|
@@ -839,6 +973,9 @@ class ILTranslator:
|
|
|
839
973
|
return None, None
|
|
840
974
|
tracker.set_input(translate_input.unicode)
|
|
841
975
|
tracker.set_placeholders(translate_input.placeholders)
|
|
976
|
+
tracker.set_original_placeholders(
|
|
977
|
+
getattr(translate_input, "original_placeholder_tokens", None),
|
|
978
|
+
)
|
|
842
979
|
text = translate_input.unicode
|
|
843
980
|
if len(text) < self.translation_config.min_text_length:
|
|
844
981
|
logger.debug(
|
|
@@ -864,6 +1001,7 @@ class ILTranslator:
|
|
|
864
1001
|
paragraph.pdf_paragraph_composition = self.parse_translate_output(
|
|
865
1002
|
translate_input,
|
|
866
1003
|
translated_text,
|
|
1004
|
+
tracker,
|
|
867
1005
|
tracker.last_llm_translate_tracker(),
|
|
868
1006
|
)
|
|
869
1007
|
for composition in paragraph.pdf_paragraph_composition:
|
|
@@ -876,122 +1014,150 @@ class ILTranslator:
|
|
|
876
1014
|
)
|
|
877
1015
|
return True
|
|
878
1016
|
|
|
879
|
-
def
|
|
1017
|
+
def _build_role_block(self) -> str:
|
|
1018
|
+
"""Build the role block for LLM prompt.
|
|
1019
|
+
|
|
1020
|
+
Returns:
|
|
1021
|
+
Role block string with custom_system_prompt or default role description.
|
|
1022
|
+
"""
|
|
1023
|
+
custom_prompt = getattr(self.translation_config, "custom_system_prompt", None)
|
|
1024
|
+
if custom_prompt:
|
|
1025
|
+
role_block = custom_prompt.strip()
|
|
1026
|
+
if "Follow all rules strictly." not in role_block:
|
|
1027
|
+
if not role_block.endswith("\n"):
|
|
1028
|
+
role_block += "\n"
|
|
1029
|
+
role_block += "Follow all rules strictly."
|
|
1030
|
+
else:
|
|
1031
|
+
role_block = (
|
|
1032
|
+
f"You are a professional {self.translation_config.lang_out} native translator who needs to fluently translate text "
|
|
1033
|
+
f"into {self.translation_config.lang_out}.\n\n"
|
|
1034
|
+
"Follow all rules strictly."
|
|
1035
|
+
)
|
|
1036
|
+
return role_block
|
|
1037
|
+
|
|
1038
|
+
def _build_context_block(
|
|
880
1039
|
self,
|
|
881
|
-
text: str,
|
|
882
1040
|
title_paragraph: PdfParagraph | None = None,
|
|
883
1041
|
local_title_paragraph: PdfParagraph | None = None,
|
|
884
1042
|
translate_input: TranslateInput | None = None,
|
|
885
|
-
):
|
|
886
|
-
|
|
887
|
-
llm_input = [self.translation_config.custom_system_prompt]
|
|
888
|
-
else:
|
|
889
|
-
llm_input = [
|
|
890
|
-
f"You are a professional and reliable machine translation engine responsible for translating the input text into {self.translation_config.lang_out}."
|
|
891
|
-
]
|
|
892
|
-
|
|
893
|
-
llm_input.append("When translating, please follow the following rules:")
|
|
894
|
-
|
|
895
|
-
rich_text_left_placeholder = (
|
|
896
|
-
self.translate_engine.get_rich_text_left_placeholder(1)
|
|
897
|
-
)
|
|
898
|
-
if isinstance(rich_text_left_placeholder, tuple):
|
|
899
|
-
rich_text_left_placeholder = rich_text_left_placeholder[0]
|
|
900
|
-
rich_text_right_placeholder = (
|
|
901
|
-
self.translate_engine.get_rich_text_right_placeholder(2)
|
|
902
|
-
)
|
|
903
|
-
if isinstance(rich_text_right_placeholder, tuple):
|
|
904
|
-
rich_text_right_placeholder = rich_text_right_placeholder[0]
|
|
905
|
-
|
|
906
|
-
# Create a structured prompt template for LLM translation
|
|
907
|
-
llm_input.append(
|
|
908
|
-
f'1. Do not translate style tags, such as "{rich_text_left_placeholder}xxx{rich_text_right_placeholder}"!'
|
|
909
|
-
)
|
|
910
|
-
|
|
911
|
-
formula_placeholder = self.translate_engine.get_formular_placeholder(3)
|
|
912
|
-
if isinstance(formula_placeholder, tuple):
|
|
913
|
-
formula_placeholder = formula_placeholder[0]
|
|
1043
|
+
) -> str:
|
|
1044
|
+
"""Build the context/hints block for LLM prompt.
|
|
914
1045
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
"3. If there is no need to translate (such as proper nouns, codes, etc.), then return the original text."
|
|
920
|
-
)
|
|
921
|
-
llm_input.append(
|
|
922
|
-
f"4. Only output the translation result in {self.translation_config.lang_out} without explanations and annotations."
|
|
923
|
-
)
|
|
1046
|
+
Args:
|
|
1047
|
+
title_paragraph: First title paragraph in the document
|
|
1048
|
+
local_title_paragraph: Most recent title paragraph
|
|
1049
|
+
translate_input: TranslateInput containing placeholder hints
|
|
924
1050
|
|
|
925
|
-
|
|
1051
|
+
Returns:
|
|
1052
|
+
Context block string, empty if no context hints available
|
|
1053
|
+
"""
|
|
1054
|
+
context_lines: list[str] = []
|
|
1055
|
+
hint_idx = 1
|
|
926
1056
|
|
|
927
1057
|
if title_paragraph:
|
|
928
|
-
|
|
929
|
-
f"
|
|
930
|
-
)
|
|
931
|
-
if (
|
|
932
|
-
local_title_paragraph
|
|
933
|
-
and title_paragraph
|
|
934
|
-
and local_title_paragraph.debug_id != title_paragraph.debug_id
|
|
935
|
-
):
|
|
936
|
-
llm_context_hints.append(
|
|
937
|
-
f"The most similar title in the full text: {local_title_paragraph.unicode}"
|
|
1058
|
+
context_lines.append(
|
|
1059
|
+
f"{hint_idx}. First title in the full text: {title_paragraph.unicode}"
|
|
938
1060
|
)
|
|
1061
|
+
hint_idx += 1
|
|
1062
|
+
|
|
1063
|
+
if local_title_paragraph:
|
|
1064
|
+
is_different_from_global = True
|
|
1065
|
+
if title_paragraph:
|
|
1066
|
+
if local_title_paragraph.debug_id == title_paragraph.debug_id:
|
|
1067
|
+
is_different_from_global = False
|
|
1068
|
+
|
|
1069
|
+
if is_different_from_global:
|
|
1070
|
+
context_lines.append(
|
|
1071
|
+
f"{hint_idx}. The most recent title is: {local_title_paragraph.unicode}"
|
|
1072
|
+
)
|
|
1073
|
+
hint_idx += 1
|
|
939
1074
|
|
|
940
1075
|
if translate_input and self.translation_config.add_formula_placehold_hint:
|
|
941
1076
|
placeholders_hint = translate_input.get_placeholders_hint()
|
|
942
1077
|
if placeholders_hint:
|
|
943
|
-
|
|
944
|
-
f"
|
|
1078
|
+
context_lines.append(
|
|
1079
|
+
f"{hint_idx}. Formula placeholder hint:\n{placeholders_hint}"
|
|
945
1080
|
)
|
|
946
1081
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
for glossary in self._cached_glossaries:
|
|
951
|
-
# Get active entries for the current text being processed (passed as 'text')
|
|
952
|
-
active_entries = glossary.get_active_entries_for_text(text)
|
|
953
|
-
|
|
954
|
-
if active_entries:
|
|
955
|
-
current_glossary_md_entries: list[str] = []
|
|
956
|
-
for original_source, target_text in sorted(active_entries):
|
|
957
|
-
current_glossary_md_entries.append(
|
|
958
|
-
f"| {original_source} | {target_text} |"
|
|
959
|
-
)
|
|
1082
|
+
if context_lines:
|
|
1083
|
+
return "## Context / Hints\n" + "\n".join(context_lines) + "\n"
|
|
1084
|
+
return ""
|
|
960
1085
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
+ "\n".join(current_glossary_md_entries)
|
|
967
|
-
)
|
|
968
|
-
active_glossary_markdown_blocks.append(glossary_table_md)
|
|
1086
|
+
def _build_glossary_block(self, text: str) -> str:
|
|
1087
|
+
"""Build the glossary block for LLM prompt.
|
|
1088
|
+
|
|
1089
|
+
Args:
|
|
1090
|
+
text: Text to match against glossary entries
|
|
969
1091
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1092
|
+
Returns:
|
|
1093
|
+
Glossary block string with tables, empty if no active glossary entries
|
|
1094
|
+
"""
|
|
1095
|
+
if not self._cached_glossaries:
|
|
1096
|
+
return ""
|
|
1097
|
+
|
|
1098
|
+
glossary_entries_per_glossary: dict[str, list[tuple[str, str]]] = {}
|
|
1099
|
+
|
|
1100
|
+
for glossary in self._cached_glossaries:
|
|
1101
|
+
active_entries = glossary.get_active_entries_for_text(text)
|
|
1102
|
+
if active_entries:
|
|
1103
|
+
glossary_entries_per_glossary[glossary.name] = sorted(active_entries)
|
|
1104
|
+
|
|
1105
|
+
if not glossary_entries_per_glossary:
|
|
1106
|
+
return ""
|
|
1107
|
+
|
|
1108
|
+
glossary_block_lines: list[str] = [
|
|
1109
|
+
"## Glossary",
|
|
1110
|
+
"",
|
|
1111
|
+
"Always use the glossary's **Target Term** for any occurrence of its **Source Term** "
|
|
1112
|
+
"(including variants, inside tags, or broken across lines).",
|
|
1113
|
+
"",
|
|
1114
|
+
"Unlisted terms are translated naturally.",
|
|
1115
|
+
"",
|
|
1116
|
+
]
|
|
1117
|
+
|
|
1118
|
+
for glossary_name, entries in glossary_entries_per_glossary.items():
|
|
1119
|
+
glossary_block_lines.append(f"### Glossary: {glossary_name}")
|
|
1120
|
+
glossary_block_lines.append("")
|
|
1121
|
+
glossary_block_lines.append(
|
|
1122
|
+
"| Source Term | Target Term |\n|-------------|-------------|"
|
|
973
1123
|
)
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
current_hint_index += 1
|
|
978
|
-
|
|
979
|
-
if active_glossary_markdown_blocks:
|
|
980
|
-
llm_input.append(
|
|
981
|
-
f"{current_hint_index}. Strictly follow the glossary: use its target terms for all source terms (including variants), do not alter existing translations, and translate any unmatched terms naturally:"
|
|
982
|
-
)
|
|
983
|
-
current_hint_index += 1
|
|
984
|
-
for md_block in active_glossary_markdown_blocks:
|
|
985
|
-
llm_input.append(f"\n{md_block}\n")
|
|
1124
|
+
for original_source, target_text in entries:
|
|
1125
|
+
glossary_block_lines.append(f"| {original_source} | {target_text} |")
|
|
1126
|
+
glossary_block_lines.append("")
|
|
986
1127
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1128
|
+
return "\n".join(glossary_block_lines)
|
|
1129
|
+
|
|
1130
|
+
def generate_prompt_for_llm(
|
|
1131
|
+
self,
|
|
1132
|
+
text: str,
|
|
1133
|
+
title_paragraph: PdfParagraph | None = None,
|
|
1134
|
+
local_title_paragraph: PdfParagraph | None = None,
|
|
1135
|
+
translate_input: TranslateInput | None = None,
|
|
1136
|
+
):
|
|
1137
|
+
"""Generate LLM prompt using template-based approach.
|
|
991
1138
|
|
|
992
|
-
|
|
1139
|
+
Args:
|
|
1140
|
+
text: Text to be translated
|
|
1141
|
+
title_paragraph: First title paragraph in the document
|
|
1142
|
+
local_title_paragraph: Most recent title paragraph
|
|
1143
|
+
translate_input: TranslateInput containing placeholder information
|
|
993
1144
|
|
|
994
|
-
|
|
1145
|
+
Returns:
|
|
1146
|
+
Final LLM prompt string
|
|
1147
|
+
"""
|
|
1148
|
+
role_block = self._build_role_block()
|
|
1149
|
+
context_block = self._build_context_block(
|
|
1150
|
+
title_paragraph, local_title_paragraph, translate_input
|
|
1151
|
+
)
|
|
1152
|
+
glossary_block = self._build_glossary_block(text)
|
|
1153
|
+
|
|
1154
|
+
return PROMPT_TEMPLATE.substitute(
|
|
1155
|
+
role_block=role_block,
|
|
1156
|
+
glossary_block=glossary_block,
|
|
1157
|
+
context_block=context_block,
|
|
1158
|
+
lang_out=self.translation_config.lang_out,
|
|
1159
|
+
text_to_translate=text,
|
|
1160
|
+
)
|
|
995
1161
|
|
|
996
1162
|
def add_content_filter_hint(self, page: Page, paragraph: PdfParagraph):
|
|
997
1163
|
with self.add_content_filter_hint_lock:
|