Spaces:
Paused
Paused
""" | |
这个文件提供了将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通常不需要翻译 |