Skip to content

Commit 4d3c102

Browse files
committed
Commit remaining modified files to trigger action.
1 parent e45b400 commit 4d3c102

12 files changed

Lines changed: 265 additions & 65 deletions

File tree

desktop_qt_ui/app_logic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ def get_display_mapping(self, key: str) -> Optional[Dict[str, str]]:
374374
"auto_rotate_symbols": "竖排内横排", "rtl": "从右到左", "layout_mode": "排版模式",
375375
"upscaler": "超分模型", "revert_upscaling": "还原超分", "colorization_size": "上色大小",
376376
"denoise_sigma": "降噪强度", "colorizer": "上色模型", "verbose": "详细日志",
377-
"attempts": "重试次数", "ignore_errors": "忽略错误", "use_gpu": "使用 GPU",
377+
"attempts": "重试次数", "max_requests_per_minute": "每分钟最大请求数", "ignore_errors": "忽略错误", "use_gpu": "使用 GPU",
378378
"use_gpu_limited": "使用 GPU(受限)", "context_size": "上下文页数", "format": "输出格式",
379379
"overwrite": "覆盖已存在文件", "skip_no_text": "跳过无文本图像", "use_mtpe": "启用后期编辑(MTPE)",
380380
"save_text": "图片可编辑", "load_text": "导入翻译", "template": "导出原文",

desktop_qt_ui/core/config_models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class TranslatorSettings(BaseModel):
1111
# 相对路径,后端会用BASE_PATH拼接(打包后=_internal,开发时=项目根目录)
1212
gpt_config: Optional[str] = "examples/gpt_config-example.yaml"
1313
high_quality_prompt_path: Optional[str] = "dict/prompt_example.json"
14+
max_requests_per_minute: int = 0
1415

1516
@property
1617
def chatgpt_config(self):

desktop_qt_ui/main_view.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,29 @@ def _create_param_widgets(self, data, parent_layout, prefix=""):
189189
container = QWidget()
190190
hbox = QHBoxLayout(container)
191191
hbox.setContentsMargins(0, 0, 0, 0)
192-
combo = QComboBox()
192+
193+
# 创建自定义ComboBox,在下拉时刷新字体列表
194+
class RefreshableComboBox(QComboBox):
195+
def showPopup(self):
196+
current_text = self.currentText()
197+
self.clear()
198+
try:
199+
fonts_dir = resource_path('fonts')
200+
if os.path.isdir(fonts_dir):
201+
font_files = sorted([f for f in os.listdir(fonts_dir) if f.lower().endswith(('.ttf', '.otf', '.ttc'))])
202+
self.addItems(font_files)
203+
except Exception as e:
204+
print(f"Error scanning fonts directory: {e}")
205+
# 恢复之前选择的值
206+
if current_text:
207+
index = self.findText(current_text)
208+
if index >= 0:
209+
self.setCurrentIndex(index)
210+
else:
211+
self.setCurrentText(current_text)
212+
super().showPopup()
213+
214+
combo = RefreshableComboBox()
193215
try:
194216
fonts_dir = resource_path('fonts')
195217
if os.path.isdir(fonts_dir):

dict/prompt_example.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"translation_rules": [
1111
"1. 无额外内容: 严禁添加任何个人评论、译者注或括号内的解释。",
1212
"2. 音效翻译: 将所有日文音效(SFX)翻译成贴切且有表现力的中文拟声词或状声词。",
13-
"3. 一致性: 严格遵循下方提供的作品与角色信息列表,确保所有角色名、昵称、地点和专有名词的翻译在全文中保持绝对一致。"
13+
"3. 一致性: 严格遵循下方提供的作品与角色信息列表,确保所有角色名、昵称、地点和专有名词的翻译在全文中保持绝对一致。"
1414
],
1515
"project_data": {
1616
"title": "",
@@ -28,4 +28,4 @@
2828
"output_format": "仅提供翻译后的文本。每一行翻译文本都必须与输入的编号文本一一对应。"
2929
},
3030
"system_prompt": "Translate the text to {{{target_lang}}}. Provide ONLY the translated text, line by line, matching the input order. DO NOT include numbering, explanations, or any extra text."
31-
}
31+
}

dict/system_prompt_hq.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
{
2-
"system_prompt": "You are a professional manga translator with deep expertise in Japanese culture and modern internet slang. Your translation style aims to provide the best reading experience for mature readers who appreciate precise, natural dialogue.\n\n**TRANSLATION PHILOSOPHY:**\n\n1. **TONE CONSISTENCY**: Your translation tone and style must perfectly match the original Japanese work. Use natural, fluent colloquial language for daily conversations, and appropriate formal language for serious or formal scenes.\n\n2. **CHARACTER VOICE**: Deeply understand and maintain each character's unique personality and speaking style. Each character should have a distinct voice in the translation.\n\n3. **CULTURAL ADAPTATION**: Appropriately handle Japanese culture-specific expressions (such as honorifics and specific customs), making them natural and easy to understand in the {{{target_lang}}} context, rather than rigid literal translations.\n\n4. **SOUND EFFECTS**: Translate all Japanese sound effects (SFX) into appropriate and expressive {{{target_lang}}} onomatopoeia or sound words that feel natural to native speakers.\n\n**CRITICAL OUTPUT REQUIREMENTS (FOLLOW STRICTLY):**\n\n1. **DIRECT TRANSLATION ONLY**: Your output MUST contain ONLY the raw, translated text.\n - **EXCEPTION**: The only allowed non-translation symbol is `[BR]`, which is used for explicit line breaks.\n - DO NOT include the original text.\n - DO NOT include any explanations, greetings, apologies, or any conversational text.\n - DO NOT include internal markers like `[Original regions: X]` or `【Original regions: X】` in your output. These are only hints for you; do NOT output them.\n - DO NOT use Markdown formatting (like ```json or ```).\n - Other than the allowed `[BR]` symbol, any extra text will cause the script to fail.\n\n2. **MATCH LINE COUNT AND ORDER**: The number of lines in your output MUST EXACTLY match the number of text regions you are asked to translate. Your first line of output MUST be the translation for text region #1, your second line for #2, and so on. DO NOT CHANGE THE ORDER.\n\n3. **TRANSLATE EVERYTHING**: Translate all text provided, including sound effects and single characters. Do not leave any line untranslated.\n\n4. **NO EXTRA CONTENT**: Strictly prohibit adding any personal comments, translator notes, or explanations in parentheses.
3-
4-
5. **NO UNNECESSARY SPACES**: DO NOT add spaces in your translation unless it is between English words or other language words that require spaces. For Chinese/Japanese/Korean translations, DO NOT add spaces between characters in regular dialogue or text. However, spaces ARE allowed in titles and chapter numbers (e.g., "第93话 请你再说一遍"). If you need to separate clauses or sentences in regular text, use appropriate punctuation marks (like commas, periods, or full-width punctuation) instead of spaces.\n\n---\n\n**EXAMPLE OF CORRECT AND INCORRECT OUTPUT:**\n\n**[ CORRECT OUTPUT EXAMPLE ]**\nThis is a correct response. Notice it only contains the translated text, with each translation on a new line.\n\n(Imagine the user input was: \"1.うるさい!\", \"2. 黙れ!\")\n```\n吵死了!\n闭嘴!\n```\n\n**[ ❌ INCORRECT OUTPUT EXAMPLE ]**\nThis is an incorrect response because it includes extra text and explanations.\n\n(Imagine the user input was: \"1.うるさい!\", \"2. 黙れ!\")\n```\n好的,这是您的翻译:\n1. 吵死了!\n2. 闭嘴!\n```\n**REASONING:** The above example is WRONG because it includes \"好的,这是您的翻译:\" and numbering. Your response must be ONLY the translated text, line by line.\n\n---\n\n**FINAL INSTRUCTION:** Now, perform the translation task. Remember, your response must be clean, containing only the translated text that captures the natural voice and cultural nuance of the original work."
2+
"system_prompt": "You are a professional manga translator with deep expertise in Japanese culture and modern internet slang. Your translation style aims to provide the best reading experience for mature readers who appreciate precise, natural dialogue.\n\n**TRANSLATION PHILOSOPHY:**\n\n1. **TONE CONSISTENCY**: Your translation tone and style must perfectly match the original Japanese work. Use natural, fluent colloquial language for daily conversations, and appropriate formal language for serious or formal scenes.\n\n2. **CHARACTER VOICE**: Deeply understand and maintain each character's unique personality and speaking style. Each character should have a distinct voice in the translation.\n\n3. **CULTURAL ADAPTATION**: Appropriately handle Japanese culture-specific expressions (such as honorifics and specific customs), making them natural and easy to understand in the {{{target_lang}}} context, rather than rigid literal translations.\n\n4. **SOUND EFFECTS**: Translate all Japanese sound effects (SFX) into appropriate and expressive {{{target_lang}}} onomatopoeia or sound words that feel natural to native speakers.\n\n**CRITICAL OUTPUT REQUIREMENTS (FOLLOW STRICTLY):**\n\n1. **DIRECT TRANSLATION ONLY**: Your output MUST contain ONLY the raw, translated text.\n - **EXCEPTION**: The only allowed non-translation symbol is `[BR]`, which is used for explicit line breaks.\n - DO NOT include the original text.\n - DO NOT include any explanations, greetings, apologies, or any conversational text.\n - DO NOT include internal markers like `[Original regions: X]` or `【Original regions: X】` in your output. These are only hints for you; do NOT output them.\n - DO NOT use Markdown formatting (like ```json or ```).\n - Other than the allowed `[BR]` symbol, any extra text will cause the script to fail.\n\n2. **MATCH LINE COUNT AND ORDER**: The number of lines in your output MUST EXACTLY match the number of text regions you are asked to translate. Your first line of output MUST be the translation for text region #1, your second line for #2, and so on. DO NOT CHANGE THE ORDER.\n\n3. **TRANSLATE EVERYTHING**: Translate all text provided, including sound effects and single characters. Do not leave any line untranslated.\n\n4. **NO EXTRA CONTENT**: Strictly prohibit adding any personal comments, translator notes, or explanations in parentheses.\n\n5. **NO UNNECESSARY SPACES**: DO NOT add spaces in your translation unless it is between English words or other language words that require spaces. For Chinese/Japanese/Korean translations, DO NOT add spaces between characters in regular dialogue or text. However, spaces ARE allowed in titles and chapter numbers (e.g., \"第93话 请你再说一遍\"). If you need to separate clauses or sentences in regular text, use appropriate punctuation marks (like commas, periods, or full-width punctuation) instead of spaces.\n\n---\n\n**EXAMPLE OF CORRECT AND INCORRECT OUTPUT:**\n\n**[ CORRECT OUTPUT EXAMPLE ]**\nThis is a correct response. Notice it only contains the translated text, with each translation on a new line.\n\n(Imagine the user input was: \"1.うるさい!\", \"2. 黙れ!\")\n```\n吵死了!\n闭嘴!\n```\n\n**[ ❌ INCORRECT OUTPUT EXAMPLE ]**\nThis is an incorrect response because it includes extra text and explanations.\n\n(Imagine the user input was: \"1.うるさい!\", \"2. 黙れ!\")\n```\n好的,这是您的翻译:\n1. 吵死了!\n2. 闭嘴!\n```\n**REASONING:** The above example is WRONG because it includes \"好的,这是您的翻译:\" and numbering. Your response must be ONLY the translated text, line by line.\n\n---\n\n**FINAL INSTRUCTION:** Now, perform the translation task. Remember, your response must be clean, containing only the translated text that captures the natural voice and cultural nuance of the original work."
53
}

manga_translator/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ class TranslatorConfig(BaseModel):
254254
attempts: int = -1
255255
"""Retry attempts on encountered error. -1 means infinite times."""
256256

257+
# API请求频率限制配置
258+
max_requests_per_minute: int = 0
259+
"""Maximum API requests per minute. 0 means no limit."""
260+
257261
# 译后检查配置项
258262
enable_post_translation_check: bool = False
259263
"""Enable post-translation validation check"""

manga_translator/translators/gemini.py

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import asyncio
66
from typing import List
77
from .common import MissingAPIKeyException, InvalidServerResponse
8-
from .keys import GEMINI_API_KEY, GEMINI_MODEL
98
from .common_gpt import CommonGPTTranslator, _CommonGPTTranslator_JSON
109

1110

@@ -28,21 +27,35 @@ class GeminiTranslator(CommonGPTTranslator):
2827
_MAX_TOKENS_IN = _MAX_TOKENS // 2
2928

3029
def __init__(self):
31-
_CONFIG_KEY = 'gemini.' + GEMINI_MODEL
30+
# 重新加载 .env 文件以获取最新配置 - 必须在任何环境变量读取之前
31+
from dotenv import load_dotenv
32+
import os
33+
load_dotenv(override=True)
34+
35+
# 动态读取GEMINI_MODEL,而不是使用模块级常量
36+
self.model = os.getenv('GEMINI_MODEL', 'gemini-1.5-flash-002')
37+
38+
_CONFIG_KEY = 'gemini.' + self.model
3239
CommonGPTTranslator.__init__(self, config_key=_CONFIG_KEY)
3340
initColorama()
3441

3542
self.cached_content = None
3643
self.templateCache = None
3744
self.cachedVals={None}
45+
46+
# 初始化 attempts,将在 parse_args 中从 UI 配置读取
47+
# 默认值 -1 表示无限重试,用于向后兼容
48+
self.attempts = -1
49+
50+
# 初始化配置
51+
self._configure_api()
3852

39-
# 重新加载 .env 文件以获取最新配置
40-
from dotenv import load_dotenv
41-
load_dotenv(override=True)
42-
43-
# 重新读取环境变量
53+
def _configure_api(self):
54+
"""配置或重新配置Gemini API"""
4455
import os
45-
api_key = os.getenv('GEMINI_API_KEY', GEMINI_API_KEY)
56+
57+
# 直接从环境变量读取,不使用keys.py中的常量
58+
api_key = os.getenv('GEMINI_API_KEY', '')
4659
api_base = os.getenv('GEMINI_API_BASE')
4760

4861
if not api_key:
@@ -64,11 +77,11 @@ def __init__(self):
6477
is_official_api = not api_base or api_base == 'https://generativelanguage.googleapis.com' or api_base.startswith('https://generativelanguage.googleapis.com')
6578
if is_official_api:
6679
try:
67-
model_info = genai.get_model(f'models/{GEMINI_MODEL}')
80+
model_info = genai.get_model(f'models/{self.model}')
6881
self._MAX_TOKENS = model_info.output_token_limit
6982
self._MAX_TOKENS_IN = self._MAX_TOKENS // 2
7083
except Exception as e:
71-
self.logger.warning(f"Could not get model info for {GEMINI_MODEL}. Using default token limits. Error: {e}")
84+
self.logger.warning(f"Could not get model info for {self.model}. Using default token limits. Error: {e}")
7285

7386
# 只有在使用官方API时才设置safety_settings,第三方API可能不支持
7487
self.safety_settings = None
@@ -100,7 +113,7 @@ def __init__(self):
100113
"top_p": self.top_p,
101114
}
102115

103-
self.client = genai.GenerativeModel(GEMINI_MODEL, generation_config=generation_config, safety_settings=self.safety_settings)
116+
self.client = genai.GenerativeModel(self.model, generation_config=generation_config, safety_settings=self.safety_settings)
104117

105118
self.token_count = 0
106119
self.token_count_last = 0
@@ -115,6 +128,13 @@ def withinTokenLimit(self, text: str) -> bool:
115128

116129
def parse_args(self, args: CommonGPTTranslator):
117130
super().parse_args(args)
131+
# 从配置中读取重试次数
132+
self.attempts = getattr(args, 'attempts', self.attempts)
133+
# 从配置中读取RPM限制
134+
max_rpm = getattr(args, 'max_requests_per_minute', 0)
135+
if max_rpm > 0:
136+
self._MAX_REQUESTS_PER_MINUTE = max_rpm
137+
self.logger.info(f"Setting Gemini max requests per minute to: {max_rpm}")
118138
if self.json_mode:
119139
self._init_json_mode()
120140
else:
@@ -270,7 +290,7 @@ async def _request_translation(self, to_lang: str, prompt: str) -> str:
270290

271291
if system_instruction:
272292
model = genai.GenerativeModel(
273-
GEMINI_MODEL,
293+
self.model,
274294
generation_config=generation_config,
275295
safety_settings=self.safety_settings,
276296
system_instruction=system_instruction
@@ -344,14 +364,14 @@ async def _request_translation(self, to_lang: str, prompt: str) -> str:
344364
# Configure the model with system instruction and JSON config
345365
if system_instruction:
346366
model = genai.GenerativeModel(
347-
GEMINI_MODEL,
367+
self.translator.model,
348368
generation_config=json_config,
349369
safety_settings=self.translator.safety_settings,
350370
system_instruction=system_instruction
351371
)
352372
else:
353373
model = genai.GenerativeModel(
354-
GEMINI_MODEL,
374+
self.translator.model,
355375
generation_config=json_config,
356376
safety_settings=self.translator.safety_settings
357377
)

0 commit comments

Comments
 (0)