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