File size: 15,674 Bytes
6c84841
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
"""

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