Skip to content

Commit f378a21

Browse files
committed
feat: 为高质量翻译添加动态自定义提示词功能
1 parent 6d292cd commit f378a21

13 files changed

Lines changed: 222 additions & 99 deletions

File tree

.github/workflows/build-and-release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,15 @@ jobs:
154154
cp -r examples dist/manga-translator-cpu/_internal/
155155
cp -r fonts dist/manga-translator-cpu/_internal/
156156
cp -r models dist/manga-translator-cpu/_internal/
157+
cp -r dict dist/manga-translator-cpu/_internal/
157158
cp -r update_repository/metadata dist/manga-translator-cpu/_internal/update_repository/
158159
cp -r MangaStudio_Data dist/manga-translator-cpu/_internal/
159160
cp VERSION dist/manga-translator-cpu/_internal/
160161
161162
cp -r examples dist/manga-translator-gpu/_internal/
162163
cp -r fonts dist/manga-translator-gpu/_internal/
163164
cp -r models dist/manga-translator-gpu/_internal/
165+
cp -r dict dist/manga-translator-gpu/_internal/
164166
cp -r update_repository/metadata dist/manga-translator-gpu/_internal/update_repository/
165167
cp -r MangaStudio_Data dist/manga-translator-gpu/_internal/
166168
cp VERSION dist/manga-translator-gpu/_internal/

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@ temp_clone_check/
6161
.vscode/
6262
models/ocr/ocr-ctc.ckpt
6363
高质量翻译实现文档.md
64-
dict/high_quality_translation_prompt.txt
64+
dict/high_quality_translation_prompt.txt

build_packages.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,15 @@ def build_executables(self, version_type):
129129

130130
# In a CI environment, we assume dependencies are pre-installed by the workflow.
131131
print(f"Running PyInstaller for {version_type.upper()}...")
132-
cmd_pyinstaller = [python_exe, "-m", "PyInstaller", "--hidden-import=bsdiff4.core", spec_file]
132+
cmd_pyinstaller = [python_exe, "-m", "PyInstaller", spec_file]
133133
if not run_command_realtime(cmd_pyinstaller):
134134
print(f"PyInstaller build failed for {version_type.upper()}.")
135135
return False
136136

137137
print(f"\nRunning PyInstaller for Updater...")
138138
updater_spec_file = 'updater.spec'
139139
# Use the same python for consistency
140-
cmd_pyinstaller_updater = [str(python_exe), "-m", "PyInstaller", "--hidden-import=bsdiff4.core", updater_spec_file, "--distpath", "dist", "--workpath", f"build/updater_{version_type}"]
140+
cmd_pyinstaller_updater = [str(python_exe), "-m", "PyInstaller", updater_spec_file, "--distpath", "dist", "--workpath", f"build/updater_{version_type}"]
141141
if not run_command_realtime(cmd_pyinstaller_updater):
142142
print(f"PyInstaller build failed for Updater.")
143143
return False

desktop-ui/app.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ def load_translations(self):
534534
"no_text_lang_skip": "不跳过目标语言文本",
535535
"skip_lang": "跳过语言",
536536
"gpt_config": "GPT配置文件路径",
537+
"high_quality_prompt_path": "高质量翻译提示词",
537538
"translator_chain": "链式翻译",
538539
"selective_translation": "选择性翻译",
539540
"detector": "文本检测器",
@@ -714,6 +715,37 @@ def create_param_widgets(self, data, parent_frame, prefix="", start_row=0):
714715
label.grid(row=row, column=0, padx=5, pady=2, sticky="w")
715716
widget_frame.grid(row=row, column=1, padx=5, pady=2, sticky="ew")
716717
continue
718+
719+
elif full_key == "translator.high_quality_prompt_path":
720+
widget_frame = ctk.CTkFrame(parent_frame, fg_color="transparent")
721+
widget_frame.grid_columnconfigure(0, weight=1)
722+
723+
# The combobox will be populated by the scan function
724+
def on_prompt_select(filename):
725+
full_path = os.path.join(resource_path('dict'), filename) if filename else None
726+
self._save_widget_change("translator.high_quality_prompt_path", value=full_path)
727+
728+
widget = ctk.CTkComboBox(widget_frame, values=[], command=on_prompt_select)
729+
730+
# Bind the click event to refresh the list
731+
widget.bind('<Button-1>', lambda event: self._scan_and_update_hq_prompt_dropdown())
732+
733+
# When loading, we only have the full path, so we extract the filename
734+
filename = os.path.basename(value) if value and os.path.exists(value) else ""
735+
widget.set(filename)
736+
widget.grid(row=0, column=0, sticky="ew")
737+
738+
# Open directory button
739+
browse_button = ctk.CTkButton(widget_frame, text="打开目录", width=80, command=self._open_dict_directory)
740+
browse_button.grid(row=0, column=1, padx=(5, 0))
741+
742+
self.parameter_widgets[full_key] = widget
743+
label.grid(row=row, column=0, padx=5, pady=2, sticky="w")
744+
widget_frame.grid(row=row, column=1, padx=5, pady=2, sticky="ew")
745+
746+
# Initial scan
747+
self.app.after(100, self._scan_and_update_hq_prompt_dropdown)
748+
continue
717749

718750
is_bool_by_schema = self.param_schema.get(full_key) is bool
719751

@@ -1008,6 +1040,47 @@ def _open_font_directory(self):
10081040
if self.font_monitor:
10091041
self.font_monitor.refresh_fonts()
10101042

1043+
def _open_dict_directory(self):
1044+
dict_dir = resource_path('dict')
1045+
try:
1046+
if not os.path.exists(dict_dir):
1047+
os.makedirs(dict_dir)
1048+
if sys.platform == "win32":
1049+
os.startfile(dict_dir)
1050+
elif sys.platform == "darwin":
1051+
subprocess.run(["open", dict_dir])
1052+
else:
1053+
subprocess.run(["xdg-open", dict_dir])
1054+
except Exception as e:
1055+
self.update_log(f"Error opening dict directory: {e}\n")
1056+
# After opening, refresh the dropdown
1057+
self.app.after(1000, self._scan_and_update_hq_prompt_dropdown) # Add a small delay
1058+
1059+
def _scan_and_update_hq_prompt_dropdown(self):
1060+
try:
1061+
dict_dir = resource_path('dict')
1062+
if not os.path.isdir(dict_dir):
1063+
self.update_log(f"Prompt directory not found: {dict_dir}\n")
1064+
return []
1065+
1066+
prompt_files = sorted([f for f in os.listdir(dict_dir) if f.lower().endswith('.json')])
1067+
1068+
widget = self.parameter_widgets.get("translator.high_quality_prompt_path")
1069+
if widget and isinstance(widget, ctk.CTkComboBox):
1070+
current_value = widget.get()
1071+
widget.configure(values=prompt_files)
1072+
# Try to keep the current selection if it's still valid
1073+
if current_value in prompt_files:
1074+
widget.set(current_value)
1075+
else:
1076+
widget.set("")
1077+
1078+
self.update_log(f"Refreshed high-quality prompt list: {len(prompt_files)} files found.\n")
1079+
return prompt_files
1080+
except Exception as e:
1081+
self.update_log(f"Error scanning prompt directory: {e}\n")
1082+
return []
1083+
10111084
def _select_font_path(self):
10121085
font_path = filedialog.askopenfilename(
10131086
title="Select Font File",

dict/prompt_example.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"persona": "你是一位专业的漫画翻译家,对日本文化和现代俚语有深入的了解。你的翻译面向能够欣赏准确和自然对话的成熟读者。",
3+
"style_guide": "翻译的基调应与原作保持一致。在休闲对话中使用非正式语言,在严肃或正式场景中使用更正式的语言。保持角色的独特声音。",
4+
"rules": "1. 不要添加任何个人评论或注释。 2. 用适当的等效词语翻译所有的音效(SFX)。 3. 确保名称和特殊术语的翻译保持一致。",
5+
"output_format": "只提供翻译后的文本,每行对应一个翻译,与输入的编号文本相对应。"
6+
}

examples/config-example.json

Lines changed: 84 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,86 @@
11
{
2-
"filter_text": null,
3-
"kernel_size": 3,
4-
"mask_dilation_offset": 10,
5-
"translator": {
6-
"translator": "gemini_hq",
7-
"target_lang": "CHS",
8-
"no_text_lang_skip": false,
9-
"gpt_config": "../examples/gpt_config-example.yaml"
10-
},
11-
"ocr": {
12-
"use_mocr_merge": false,
13-
"ocr": "48px",
14-
"use_hybrid_ocr": false,
15-
"secondary_ocr": "mocr",
16-
"min_text_length": 0,
17-
"ignore_bubble": 0,
18-
"prob": 0.001,
19-
"merge_gamma": 0.8,
20-
"merge_sigma": 2.5,
21-
"kernel_size": 3
22-
},
23-
"detector": {
24-
"detector": "default",
25-
"detection_size": 2048,
26-
"text_threshold": 0.5,
27-
"det_rotate": false,
28-
"det_auto_rotate": false,
29-
"det_invert": false,
30-
"det_gamma_correct": false,
31-
"box_threshold": 0.5,
32-
"unclip_ratio": 2.5
33-
},
34-
"inpainter": {
35-
"inpainter": "lama_large",
36-
"inpainting_size": 2048,
37-
"inpainting_precision": "fp32"
38-
},
39-
"render": {
40-
"renderer": "default",
41-
"alignment": "auto",
42-
"disable_font_border": false,
43-
"font_size_offset": 0,
44-
"font_size_minimum": 0,
45-
"direction": "auto",
46-
"uppercase": false,
47-
"lowercase": false,
48-
"gimp_font": "Arial-Unicode-Regular.ttf",
49-
"font_path": "Arial-Unicode-Regular.ttf",
50-
"no_hyphenation": false,
51-
"font_color": ":FFFFFF",
52-
"rtl": true,
53-
"layout_mode": "smart_scaling"
54-
},
55-
"upscale": {
56-
"upscaler": "esrgan",
57-
"revert_upscaling": false
58-
},
59-
"colorizer": {
60-
"colorization_size": 576,
61-
"denoise_sigma": 30,
62-
"colorizer": "none"
63-
},
64-
"cli": {
65-
"verbose": true,
66-
"attempts": -1,
67-
"ignore_errors": false,
68-
"use_gpu": true,
69-
"use_gpu_limited": false,
70-
"context_size": 3,
71-
"format": "",
72-
"overwrite": true,
73-
"skip_no_text": false,
74-
"use_mtpe": false,
75-
"save_text": true,
76-
"load_text": false,
77-
"template": false,
78-
"prep_manual": false,
79-
"save_quality": 100,
80-
"batch_size": 1,
81-
"batch_concurrent": false,
82-
"high_quality_batch_size": 3
83-
},
84-
"last_output_path": "D:/xiazai/图片助手(ImageAssistant)_批量图片下载器/output"
2+
"filter_text": null,
3+
"kernel_size": 3,
4+
"mask_dilation_offset": 10,
5+
"translator": {
6+
"translator": "gemini_hq",
7+
"target_lang": "CHS",
8+
"no_text_lang_skip": false,
9+
"gpt_config": "../examples/gpt_config-example.yaml",
10+
"high_quality_prompt_path": "C:\\Users\\徐浩文\\manga-image-translator\\manga-translator-ui-package\\dict\\prompt_example.json"
11+
},
12+
"ocr": {
13+
"use_mocr_merge": false,
14+
"ocr": "48px",
15+
"use_hybrid_ocr": false,
16+
"secondary_ocr": "mocr",
17+
"min_text_length": 0,
18+
"ignore_bubble": 0,
19+
"prob": 0.001,
20+
"merge_gamma": 0.8,
21+
"merge_sigma": 2.5,
22+
"kernel_size": 3
23+
},
24+
"detector": {
25+
"detector": "default",
26+
"detection_size": 2048,
27+
"text_threshold": 0.5,
28+
"det_rotate": false,
29+
"det_auto_rotate": false,
30+
"det_invert": false,
31+
"det_gamma_correct": false,
32+
"box_threshold": 0.5,
33+
"unclip_ratio": 2.5
34+
},
35+
"inpainter": {
36+
"inpainter": "lama_large",
37+
"inpainting_size": 2048,
38+
"inpainting_precision": "fp32"
39+
},
40+
"render": {
41+
"renderer": "default",
42+
"alignment": "auto",
43+
"disable_font_border": false,
44+
"font_size_offset": 0,
45+
"font_size_minimum": 0,
46+
"direction": "auto",
47+
"uppercase": false,
48+
"lowercase": false,
49+
"gimp_font": "Arial-Unicode-Regular.ttf",
50+
"font_path": "Arial-Unicode-Regular.ttf",
51+
"no_hyphenation": false,
52+
"font_color": ":FFFFFF",
53+
"rtl": true,
54+
"layout_mode": "smart_scaling"
55+
},
56+
"upscale": {
57+
"upscaler": "esrgan",
58+
"revert_upscaling": false
59+
},
60+
"colorizer": {
61+
"colorization_size": 576,
62+
"denoise_sigma": 30,
63+
"colorizer": "none"
64+
},
65+
"cli": {
66+
"verbose": true,
67+
"attempts": -1,
68+
"ignore_errors": false,
69+
"use_gpu": true,
70+
"use_gpu_limited": false,
71+
"context_size": 3,
72+
"format": "",
73+
"overwrite": true,
74+
"skip_no_text": false,
75+
"use_mtpe": false,
76+
"save_text": true,
77+
"load_text": false,
78+
"template": false,
79+
"prep_manual": false,
80+
"save_quality": 100,
81+
"batch_size": 1,
82+
"batch_concurrent": false,
83+
"high_quality_batch_size": 3
84+
},
85+
"last_output_path": "D:/xiazai/图片助手(ImageAssistant)_批量图片下载器/output"
8586
}

manga-translator-cpu.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ a = Analysis(
1010
pathex=[],
1111
binaries=[],
1212
datas=py3langid_datas + unidic_datas,
13-
hiddenimports=['pydensecrf.eigen'],
13+
hiddenimports=['pydensecrf.eigen', 'bsdiff4.core'],
1414
hookspath=[],
1515
hooksconfig={},
1616
runtime_hooks=[],

manga-translator-gpu.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ a = Analysis(
1010
pathex=[],
1111
binaries=[],
1212
datas=py3langid_datas + unidic_datas,
13-
hiddenimports=['pydensecrf.eigen'],
13+
hiddenimports=['pydensecrf.eigen', 'bsdiff4.core'],
1414
hookspath=[],
1515
hooksconfig={},
1616
runtime_hooks=[],

manga_translator/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ class TranslatorConfig(BaseModel):
232232
"""Skip translation if source image is one of the provide languages, use comma to separate multiple languages. Example: JPN,ENG"""
233233
gpt_config: Optional[str] = None # todo: no more path
234234
"""Path to GPT config file, more info in README"""
235+
high_quality_prompt_path: Optional[str] = None
236+
"""Path to a JSON file containing custom prompts for high-quality translation."""
235237
translator_chain: Optional[str] = None
236238
"""Output of one translator goes in another. Example: --translator-chain "google:JPN;sugoi:ENG"."""
237239
selective_translation: Optional[str] = None

manga_translator/manga_translator.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,16 @@ async def _run_text_translation(self, config: Config, ctx: Context):
14611461
current_time = time.time()
14621462
self._model_usage_timestamps[("translation", config.translator.translator)] = current_time
14631463

1464+
# Load custom high-quality prompt from JSON file if specified
1465+
ctx.custom_prompt_json = None
1466+
if config.translator.high_quality_prompt_path and os.path.exists(config.translator.high_quality_prompt_path):
1467+
try:
1468+
with open(config.translator.high_quality_prompt_path, 'r', encoding='utf-8') as f:
1469+
ctx.custom_prompt_json = json.load(f)
1470+
logger.info(f"Successfully loaded custom high-quality prompt from {config.translator.high_quality_prompt_path}")
1471+
except Exception as e:
1472+
logger.error(f"Failed to load or parse custom prompt JSON from {config.translator.high_quality_prompt_path}: {e}")
1473+
14641474
if config.translator.translator in [Translator.gemini_hq, Translator.openai_hq]:
14651475
from PIL import Image
14661476
ctx.high_quality_batch_data = [{

0 commit comments

Comments
 (0)