DiffuseCraftMod / translate_ui.py
Menyu's picture
modified: app.py
6c84841
"""
这个文件提供了将UI翻译应用到Gradio组件的功能
"""
import gradio as gr
from typing import Dict, Any, List, Tuple, Optional, Union, Callable
import json
# 避免循环导入
_ui_translations = None
_preserve_terms = None
_special_translations = {}
def _load_translations():
"""延迟加载翻译数据以避免循环导入"""
global _ui_translations, _preserve_terms, _special_translations
if _ui_translations is None:
from translations import (
UI_TRANSLATIONS, PRESERVE_TERMS, CIVITAI_BASEMODEL_CN, INTERFACE_MODES_CN,
MODEL_TYPES_CN, GENRE_CN, SPEED_CN, TAG_TYPES_CN, TASK_TYPES_CN,
IP_ADAPTER_MODES_CN, RECOM_PROMPT_TYPES_CN, RATING_CN, ASPECT_RATIO_CN,
LENGTH_CN, IDENTITY_CN
)
_ui_translations = UI_TRANSLATIONS
_preserve_terms = PRESERVE_TERMS
_special_translations = {
"interface_modes": INTERFACE_MODES_CN,
"model_types": MODEL_TYPES_CN,
"genre": GENRE_CN,
"speed": SPEED_CN,
"tag_types": TAG_TYPES_CN,
"task_types": TASK_TYPES_CN,
"ip_adapter_modes": IP_ADAPTER_MODES_CN,
"recom_prompt_types": RECOM_PROMPT_TYPES_CN,
"rating": RATING_CN,
"aspect_ratio": ASPECT_RATIO_CN,
"length": LENGTH_CN,
"identity": IDENTITY_CN,
"civitai_basemodel": CIVITAI_BASEMODEL_CN,
}
def translate_text(text: str) -> str:
"""翻译文本,保留特定术语"""
if not text or not isinstance(text, str):
return text
# 确保翻译数据已加载
if _ui_translations is None:
_load_translations()
# 直接翻译
if text in _ui_translations:
return _ui_translations[text]
# 对于包含保留术语的文本,确保术语不被翻译
result = text
for term in _preserve_terms:
# 避免替换部分单词,确保是完整单词或由空格/标点分隔
if term in result and (
term == result or
result.startswith(f"{term} ") or
result.endswith(f" {term}") or
f" {term} " in result or
result.startswith(f"{term},") or
result.endswith(f", {term}") or
f", {term}," in result
):
# 保留这个术语
pass
return result
def translate_dropdown_choices(choices: List) -> List:
"""翻译下拉菜单选项"""
if not choices:
return choices
if isinstance(choices[0], tuple):
# 如果是元组列表 [(label, value), ...]
return [(translate_text(label), value) for label, value in choices]
else:
# 如果是简单列表 [value1, value2, ...]
return [translate_text(choice) for choice in choices]
def translate_dict_choices(choices: Dict) -> Dict:
"""翻译字典选项的键"""
if not choices:
return choices
return {translate_text(k): v for k, v in choices.items()}
def apply_translation_to_component(component: Any) -> Any:
"""将翻译应用到单个Gradio组件"""
if not component:
return component
# 确保翻译数据已加载
if _ui_translations is None:
_load_translations()
# 不同类型组件的翻译逻辑
if isinstance(component, (gr.Button, gr.UploadButton)):
if hasattr(component, 'value') and component.value:
component.value = translate_text(component.value)
elif isinstance(component, (gr.Textbox, gr.Number, gr.Slider, gr.Radio, gr.Checkbox,
gr.Dropdown, gr.Image, gr.Gallery, gr.CheckboxGroup)):
if hasattr(component, 'label') and component.label:
component.label = translate_text(component.label)
if hasattr(component, 'info') and component.info:
component.info = translate_text(component.info)
# 对于有choices属性的组件
if hasattr(component, 'choices') and component.choices:
# 处理不同类型的选项
if isinstance(component, gr.Radio):
# 特殊类型翻译
if set(component.choices) == set(["Simple", "Standard", "Fast", "LoRA"]):
component.choices = [_special_translations["interface_modes"].get(c, c) for c in component.choices]
elif set(component.choices) == set(["None", "Auto", "Animagine", "Pony"]):
component.choices = [_special_translations["model_types"].get(c, c) for c in component.choices]
elif set(component.choices) == set(["Anime", "Photo"]):
component.choices = [_special_translations["genre"].get(c, c) for c in component.choices]
elif set(component.choices) == set(["Fast", "Standard", "Heavy"]):
component.choices = [_special_translations["speed"].get(c, c) for c in component.choices]
elif set(component.choices) & set(["danbooru", "e621", "body", "dress", "all"]):
component.choices = [_special_translations["tag_types"].get(c, c) for c in component.choices]
elif set(component.choices) & set(["original", "style"]):
component.choices = [_special_translations["ip_adapter_modes"].get(c, c) for c in component.choices]
elif set(component.choices) & set(["None", "Auto", "Animagine", "Pony"]):
component.choices = [_special_translations["recom_prompt_types"].get(c, c) for c in component.choices]
else:
component.choices = [translate_text(c) for c in component.choices]
elif isinstance(component, gr.CheckboxGroup):
if any(c in _special_translations["civitai_basemodel"] for c in component.choices):
component.choices = [_special_translations["civitai_basemodel"].get(c, c) for c in component.choices]
else:
component.choices = [translate_text(c) for c in component.choices]
else:
component.choices = translate_dropdown_choices(component.choices)
# 对于有值的组件
if hasattr(component, 'placeholder') and component.placeholder:
component.placeholder = translate_text(component.placeholder)
elif isinstance(component, gr.Markdown):
if hasattr(component, 'value') and component.value:
component.value = translate_text(component.value)
return component
def apply_translation_to_ui(block: gr.Blocks) -> None:
"""递归地将翻译应用到整个UI"""
if not block:
return
# 如果是Blocks实例处理其子项
if isinstance(block, gr.Blocks):
for child in block.children:
apply_translation_to_ui(child)
# 如果是Row, Column, Group等容器
elif hasattr(block, 'children'):
for child in block.children:
apply_translation_to_ui(child)
# 如果是Accordion、Tab等特殊容器
elif isinstance(block, (gr.Accordion, gr.Tab, gr.TabItem)):
# 翻译标签
if hasattr(block, 'label') and block.label:
block.label = translate_text(block.label)
# 处理子项
if hasattr(block, 'children'):
for child in block.children:
apply_translation_to_ui(child)
# 如果是具体的组件
else:
apply_translation_to_component(block)
def translate_task_choices(task_choices: List[str]) -> List[str]:
"""翻译任务选项"""
return [TASK_TYPES_CN.get(task, task) for task in task_choices]
def create_translating_app(original_app: gr.Blocks) -> gr.Blocks:
"""创建应用翻译后的版本"""
# 复制原始app
app_copy = original_app
# 应用翻译
apply_translation_to_ui(app_copy)
return app_copy
def monkey_patch_gradio():
"""猴子补丁Gradio组件以自动翻译"""
# 确保翻译数据已加载
if _ui_translations is None:
_load_translations()
# 保存原始方法
original_textbox = gr.Textbox
original_button = gr.Button
original_checkbox = gr.Checkbox
original_radio = gr.Radio
original_slider = gr.Slider
original_dropdown = gr.Dropdown
original_markdown = gr.Markdown
original_tab = gr.Tab
original_accordion = gr.Accordion
# 替换方法
def patched_textbox(*args, **kwargs):
if 'label' in kwargs and kwargs['label']:
kwargs['label'] = translate_text(kwargs['label'])
if 'placeholder' in kwargs and kwargs['placeholder']:
kwargs['placeholder'] = translate_text(kwargs['placeholder'])
if 'info' in kwargs and kwargs['info']:
kwargs['info'] = translate_text(kwargs['info'])
return original_textbox(*args, **kwargs)
def patched_button(*args, **kwargs):
if 'value' in kwargs and kwargs['value']:
kwargs['value'] = translate_text(kwargs['value'])
return original_button(*args, **kwargs)
def patched_checkbox(*args, **kwargs):
if 'label' in kwargs and kwargs['label']:
kwargs['label'] = translate_text(kwargs['label'])
if 'info' in kwargs and kwargs['info']:
kwargs['info'] = translate_text(kwargs['info'])
return original_checkbox(*args, **kwargs)
def patched_radio(*args, **kwargs):
if 'label' in kwargs and kwargs['label']:
kwargs['label'] = translate_text(kwargs['label'])
if 'info' in kwargs and kwargs['info']:
kwargs['info'] = translate_text(kwargs['info'])
if 'choices' in kwargs and kwargs['choices']:
# 特殊类型翻译
if set(kwargs['choices']) == set(["Simple", "Standard", "Fast", "LoRA"]):
kwargs['choices'] = [_special_translations["interface_modes"].get(c, c) for c in kwargs['choices']]
elif set(kwargs['choices']) == set(["None", "Auto", "Animagine", "Pony"]):
kwargs['choices'] = [_special_translations["model_types"].get(c, c) for c in kwargs['choices']]
elif set(kwargs['choices']) == set(["Anime", "Photo"]):
kwargs['choices'] = [_special_translations["genre"].get(c, c) for c in kwargs['choices']]
elif set(kwargs['choices']) == set(["Fast", "Standard", "Heavy"]):
kwargs['choices'] = [_special_translations["speed"].get(c, c) for c in kwargs['choices']]
elif set(kwargs['choices']) & set(["danbooru", "e621", "body", "dress", "all"]):
kwargs['choices'] = [_special_translations["tag_types"].get(c, c) for c in kwargs['choices']]
elif set(kwargs['choices']) & set(["original", "style"]):
kwargs['choices'] = [_special_translations["ip_adapter_modes"].get(c, c) for c in kwargs['choices']]
elif set(kwargs['choices']) & set(["None", "Auto", "Animagine", "Pony"]):
kwargs['choices'] = [_special_translations["recom_prompt_types"].get(c, c) for c in kwargs['choices']]
else:
kwargs['choices'] = [translate_text(c) for c in kwargs['choices']]
return original_radio(*args, **kwargs)
def patched_slider(*args, **kwargs):
if 'label' in kwargs and kwargs['label']:
kwargs['label'] = translate_text(kwargs['label'])
if 'info' in kwargs and kwargs['info']:
kwargs['info'] = translate_text(kwargs['info'])
return original_slider(*args, **kwargs)
def patched_dropdown(*args, **kwargs):
if 'label' in kwargs and kwargs['label']:
kwargs['label'] = translate_text(kwargs['label'])
if 'info' in kwargs and kwargs['info']:
kwargs['info'] = translate_text(kwargs['info'])
if 'choices' in kwargs and kwargs['choices']:
# 识别并保留特殊的选择项
if isinstance(kwargs['choices'], list) and len(kwargs['choices']) > 0:
if isinstance(kwargs['choices'][0], tuple):
# 如果是元组列表 [(label, value), ...]
kwargs['choices'] = [(translate_text(label), value) for label, value in kwargs['choices']]
else:
# 如果是简单列表 [value1, value2, ...]
kwargs['choices'] = [translate_text(choice) for choice in kwargs['choices']]
return original_dropdown(*args, **kwargs)
def patched_markdown(*args, **kwargs):
if args and isinstance(args[0], str):
args = list(args)
args[0] = translate_text(args[0])
args = tuple(args)
if 'value' in kwargs and kwargs['value']:
kwargs['value'] = translate_text(kwargs['value'])
return original_markdown(*args, **kwargs)
def patched_tab(*args, **kwargs):
if 'label' in kwargs and kwargs['label']:
kwargs['label'] = translate_text(kwargs['label'])
return original_tab(*args, **kwargs)
def patched_accordion(*args, **kwargs):
if 'label' in kwargs and kwargs['label']:
kwargs['label'] = translate_text(kwargs['label'])
return original_accordion(*args, **kwargs)
# 应用补丁
gr.Textbox = patched_textbox
gr.Button = patched_button
gr.Checkbox = patched_checkbox
gr.Radio = patched_radio
gr.Slider = patched_slider
gr.Dropdown = patched_dropdown
gr.Markdown = patched_markdown
gr.Tab = patched_tab
gr.Accordion = patched_accordion
def translate_special_list(choice_list, list_type):
"""翻译特殊列表,如Civitai排序、时间段等"""
# 确保翻译数据已加载
if _ui_translations is None:
_load_translations()
# 处理已知的特殊列表
if list_type == "civitai_sort":
translations = {
"Highest Rated": "最高评分",
"Most Downloaded": "最多下载",
"Most Liked": "最多点赞",
"Most Discussed": "最多讨论",
"Most Collected": "最多收藏",
"Most Buzz": "最多热度",
"Newest": "最新"
}
return [translations.get(item, item) for item in choice_list]
elif list_type == "civitai_period":
translations = {
"AllTime": "所有时间",
"Year": "年",
"Month": "月",
"Week": "周",
"Day": "日"
}
return [translations.get(item, item) for item in choice_list]
elif list_type == "civitai_basemodel":
# 这些通常保持不变
return choice_list
# 默认直接翻译每一项
return [translate_text(item) for item in choice_list]
def get_translated_constants():
"""获取预先翻译好的常量"""
from modutils import CIVITAI_SORT, CIVITAI_PERIOD, CIVITAI_BASEMODEL
global _translated_civitai_sort, _translated_civitai_period
_translated_civitai_sort = translate_special_list(CIVITAI_SORT, "civitai_sort")
_translated_civitai_period = translate_special_list(CIVITAI_PERIOD, "civitai_period")
# CIVITAI_BASEMODEL通常不需要翻译