Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions localize_zh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""
OneTrainer UI 汉化脚本
读取 zh_cn_map.py 中的翻译映射,替换 Base*.py 和 PySide6*.py 中的英文字符串。
"""
import os
import re
import sys

# 确保 UTF-8
sys.stdout.reconfigure(encoding='utf-8')

UI_DIR = os.path.join(os.path.dirname(__file__), 'modules', 'ui')

def load_translations():
"""加载翻译映射"""
map_file = os.path.join(os.path.dirname(__file__), 'zh_cn_map.py')
if not os.path.exists(map_file):
print(f"Error: {map_file} not found")
sys.exit(1)

# 动态导入
import importlib.util
spec = importlib.util.spec_from_file_location("zh_cn_map", map_file)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod.TRANSLATIONS

def apply_translations(translations, dry_run=False):
"""应用翻译到 UI 文件"""
stats = {"files_modified": 0, "strings_replaced": 0, "strings_not_found": []}

for filename in sorted(os.listdir(UI_DIR)):
if not filename.endswith('.py'):
continue
# 只处理 Base 和 PySide6 文件
if not (filename.startswith('Base') or filename.startswith('PySide6')):
continue

filepath = os.path.join(UI_DIR, filename)
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()

original = content
file_replacements = 0

for en, zh in translations.items():
# 匹配带引号的英文字符串
# 注意:要精确匹配,避免替换到变量名
pattern = '"' + re.escape(en) + '"'
replacement = '"' + zh + '"'

new_content = re.sub(pattern, replacement, content)
if new_content != content:
count = len(re.findall(pattern, content))
file_replacements += count
content = new_content

if content != original:
if not dry_run:
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
stats["files_modified"] += 1
stats["strings_replaced"] += file_replacements
print(f" {'[DRY] ' if dry_run else ''}{filename}: {file_replacements} replacements")

# 检查未匹配的翻译
for en in translations:
found = False
for filename in os.listdir(UI_DIR):
if not filename.endswith('.py'):
continue
if not (filename.startswith('Base') or filename.startswith('PySide6')):
continue
filepath = os.path.join(UI_DIR, filename)
with open(filepath, 'r', encoding='utf-8') as f:
if f'"{en}"' in f.read():
found = True
break
if not found:
stats["strings_not_found"].append(en)

return stats

def main():
dry_run = '--dry-run' in sys.argv

print("Loading translations...")
translations = load_translations()
print(f"Loaded {len(translations)} translations")

print(f"\n{'[DRY RUN] ' if dry_run else ''}Applying translations to UI files...")
stats = apply_translations(translations, dry_run)

print(f"\n=== Results ===")
print(f"Files modified: {stats['files_modified']}")
print(f"Strings replaced: {stats['strings_replaced']}")

if stats["strings_not_found"]:
print(f"Strings not found in UI files ({len(stats['strings_not_found'])}):")
for s in stats["strings_not_found"][:10]:
print(f" - {s}")
if len(stats["strings_not_found"]) > 10:
print(f" ... and {len(stats['strings_not_found']) - 10} more")

if __name__ == '__main__':
main()
12 changes: 6 additions & 6 deletions modules/ui/BaseAdditionalEmbeddingsTabView.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ def build_content(self, top_frame, bottom_frame, ui_state, i, save_command, remo

# embedding model names
self.components.label(top_frame, 0, 2, "base embedding:",
tooltip="The base embedding to train on. Leave empty to create a new embedding")
tooltip="训练的基础嵌入,留空创建新嵌入")
self.components.path_entry(
top_frame, 0, 3, self.ui_state, "model_name",
mode="file", path_modifier=path_util.json_path_modifier
)

# placeholder
self.components.label(top_frame, 0, 4, "placeholder:",
tooltip="The placeholder used when using the embedding in a prompt")
tooltip="在提示词中使用嵌入的占位符")
self.components.entry(top_frame, 0, 5, self.ui_state, "placeholder")

# token count
self.components.label(top_frame, 0, 6, "token count:",
tooltip="The token count used when creating a new embedding. Leave empty to auto detect from the initial embedding text.")
tooltip="新嵌入的Token数,留空自动检测")
self.components.entry(top_frame, 0, 7, self.ui_state, "token_count", width=40)

# trainable
Expand All @@ -56,17 +56,17 @@ def build_content(self, top_frame, bottom_frame, ui_state, i, save_command, remo

# output embedding
self.components.label(bottom_frame, 0, 2, "output embedding:",
tooltip="Output embeddings are calculated at the output of the text encoder, not the input. This can improve results for larger text encoders and lower VRAM usage.")
tooltip="在文本编码器输出处计算嵌入,可改善大文本编码器效果并降低显存")
self.components.switch(bottom_frame, 0, 3, self.ui_state, "is_output_embedding", width=40)

# stop training after
self.components.label(bottom_frame, 0, 4, "stop training after:",
tooltip="When to stop training the embedding")
tooltip="何时停止训练嵌入")
self.components.time_entry(bottom_frame, 0, 5, self.ui_state, "stop_training_after", "stop_training_after_unit")

# initial embedding text
self.components.label(bottom_frame, 0, 6, "initial embedding text:",
tooltip="The initial embedding text used when creating a new embedding")
tooltip="创建新嵌入时的初始文本")
self.components.entry(bottom_frame, 0, 7, self.ui_state, "initial_embedding_text")

def configure_element(self):
Expand Down
26 changes: 13 additions & 13 deletions modules/ui/BaseCaptionUIView.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,27 @@ def draw_mask_editing_mode(self, *args): pass
def fill_mask_editing_mode(self, *args): pass

def build_top_bar(self, frame, controller, ui_state):
self.components.button(frame, 0, 0, "Open", self.open_directory,
tooltip="open a new directory")
self.components.button(frame, 0, 1, "Generate Masks", self.open_mask_window,
tooltip="open a dialog to automatically generate masks")
self.components.button(frame, 0, 2, "Generate Captions", self.open_caption_window,
tooltip="open a dialog to automatically generate captions")
self.components.button(frame, 0, 0, "打开", self.open_directory,
tooltip="打开新目录")
self.components.button(frame, 0, 1, "生成遮罩", self.open_mask_window,
tooltip="打开自动生成遮罩对话框")
self.components.button(frame, 0, 2, "生成标签", self.open_caption_window,
tooltip="打开自动生成标签对话框")

if platform.system() == "Windows":
self.components.button(frame, 0, 3, "Open in Explorer", self.open_in_explorer,
tooltip="open the current image in Explorer")
self.components.button(frame, 0, 3, "在资源管理器中打开", self.open_in_explorer,
tooltip="在资源管理器中打开当前图像")

self.components.switch(frame, 0, 4, ui_state, "include_subdirectories",
text="include subdirectories")

frame.grid_columnconfigure(5, weight=1)

self.components.button(frame, 0, 6, "Help", controller.print_help,
self.components.button(frame, 0, 6, "帮助", controller.print_help,
tooltip=controller.help_text)

def build_mask_buttons(self, right_frame):
self.components.button(right_frame, 0, 0, "Draw", self.draw_mask_editing_mode,
tooltip="draw a mask using a brush")
self.components.button(right_frame, 0, 1, "Fill", self.fill_mask_editing_mode,
tooltip="draw a mask using a fill tool")
self.components.button(right_frame, 0, 0, "绘制", self.draw_mask_editing_mode,
tooltip="用画笔绘制遮罩")
self.components.button(right_frame, 0, 1, "填充", self.fill_mask_editing_mode,
tooltip="用填充工具绘制遮罩")
Loading