154 lines
10 KiB
Python
154 lines
10 KiB
Python
|
|
"""
|
||
|
|
Простая система локализации ru/en.
|
||
|
|
Использование:
|
||
|
|
from comfy_studio.core.i18n import tr, set_language
|
||
|
|
tr("search_placeholder") -> "Поиск по имени или промтам..." | "Search by name or prompts..."
|
||
|
|
"""
|
||
|
|
|
||
|
|
from typing import Dict
|
||
|
|
|
||
|
|
_current_language = "ru"
|
||
|
|
|
||
|
|
STRINGS: Dict[str, Dict[str, str]] = {
|
||
|
|
# --- Общее ---
|
||
|
|
"app_ready": {"ru": "Готов к работе", "en": "Ready"},
|
||
|
|
"error": {"ru": "Ошибка", "en": "Error"},
|
||
|
|
"success": {"ru": "Успешно", "en": "Success"},
|
||
|
|
"save": {"ru": "Сохранить", "en": "Save"},
|
||
|
|
"cancel": {"ru": "Отмена", "en": "Cancel"},
|
||
|
|
"delete": {"ru": "Удалить", "en": "Delete"},
|
||
|
|
"rename": {"ru": "Переименовать", "en": "Rename"},
|
||
|
|
|
||
|
|
# --- Левая панель ---
|
||
|
|
"search_placeholder": {"ru": "Поиск по имени или промтам...", "en": "Search by name or prompts..."},
|
||
|
|
"active_folder": {"ru": "Активная папка:", "en": "Active folder:"},
|
||
|
|
"subfolder_tree": {"ru": "Дерево подпапок:", "en": "Subfolder tree:"},
|
||
|
|
"filter_favorites": {"ru": "Только избранное", "en": "Favorites only"},
|
||
|
|
"filter_tag": {"ru": "Фильтр по тегу:", "en": "Filter by tag:"},
|
||
|
|
"all_tags": {"ru": "Все теги", "en": "All tags"},
|
||
|
|
|
||
|
|
# --- Центральная панель ---
|
||
|
|
"select_folder": {"ru": "Выберите папку...", "en": "Select a folder..."},
|
||
|
|
"folder_label": {"ru": "Папка", "en": "Folder"},
|
||
|
|
"zoom": {"ru": "Масштаб:", "en": "Zoom:"},
|
||
|
|
"files_shown": {"ru": "Файлов отображено", "en": "Files shown"},
|
||
|
|
|
||
|
|
# --- Правая панель ---
|
||
|
|
"no_image_selected": {"ru": "Нет выделенного изображения", "en": "No image selected"},
|
||
|
|
"preview_error": {"ru": "Ошибка рендеринга превью", "en": "Preview rendering error"},
|
||
|
|
"model": {"ru": "Модель:", "en": "Model:"},
|
||
|
|
"sampler": {"ru": "Сэмплер:", "en": "Sampler:"},
|
||
|
|
"rating": {"ru": "Рейтинг:", "en": "Rating:"},
|
||
|
|
"no_rating": {"ru": "Без рейтинга", "en": "No rating"},
|
||
|
|
"not_specified": {"ru": "Не указано", "en": "Not specified"},
|
||
|
|
"positive_prompt": {"ru": "Позитивный промт:", "en": "Positive prompt:"},
|
||
|
|
"negative_prompt": {"ru": "Негативный промт:", "en": "Negative prompt:"},
|
||
|
|
"save_changes": {"ru": "Сохранить изменения", "en": "Save changes"},
|
||
|
|
"workflow_not_found": {"ru": "Workflow не найден", "en": "Workflow not found"},
|
||
|
|
"tab_generation": {"ru": "Генерация", "en": "Generation"},
|
||
|
|
"tab_workflow": {"ru": "Workflow", "en": "Workflow"},
|
||
|
|
"copy_positive": {"ru": "Коп. Pos", "en": "Copy Pos"},
|
||
|
|
"copy_negative": {"ru": "Коп. Neg", "en": "Copy Neg"},
|
||
|
|
"copy_workflow": {"ru": "Коп. Work", "en": "Copy WF"},
|
||
|
|
"send_to_comfy": {"ru": "Отправить в ComfyUI", "en": "Send to ComfyUI"},
|
||
|
|
"favorite": {"ru": "★ Избранное", "en": "★ Favorite"},
|
||
|
|
"tags": {"ru": "Теги:", "en": "Tags:"},
|
||
|
|
"add_tag_placeholder": {"ru": "Новый тег + Enter", "en": "New tag + Enter"},
|
||
|
|
|
||
|
|
# --- Контекстное меню ---
|
||
|
|
"show_on_disk": {"ru": "Показать на диске", "en": "Show on disk"},
|
||
|
|
"delete_selected": {"ru": "Удалить выбранные", "en": "Delete selected"},
|
||
|
|
"rename_title": {"ru": "Переименование", "en": "Rename"},
|
||
|
|
"rename_prompt": {"ru": "Новое имя файла для", "en": "New filename for"},
|
||
|
|
"open_fullscreen": {"ru": "Открыть на весь экран", "en": "Open fullscreen"},
|
||
|
|
"toggle_favorite": {"ru": "Добавить/убрать из избранного", "en": "Toggle favorite"},
|
||
|
|
|
||
|
|
# --- Диалоги ---
|
||
|
|
"settings_title": {"ru": "Настройки", "en": "Settings"},
|
||
|
|
"settings_btn": {"ru": "Настройки", "en": "Settings"},
|
||
|
|
"comfy_api_url": {"ru": "Адрес API ComfyUI:", "en": "ComfyUI API URL:"},
|
||
|
|
"tracked_folders": {"ru": "Отслеживаемые папки генерации:", "en": "Tracked generation folders:"},
|
||
|
|
"add_folder": {"ru": "Добавить папку", "en": "Add folder"},
|
||
|
|
"remove_selected": {"ru": "Удалить выбранную", "en": "Remove selected"},
|
||
|
|
"language": {"ru": "Язык интерфейса:", "en": "Interface language:"},
|
||
|
|
"language_restart_note": {"ru": "Язык применится после перезапуска приложения.",
|
||
|
|
"en": "Language change takes effect after restart."},
|
||
|
|
"url_empty_error": {"ru": "Адрес API не может быть пустым.", "en": "API URL cannot be empty."},
|
||
|
|
"paths_empty_error": {"ru": "Должна быть добавлена хотя бы одна папка.",
|
||
|
|
"en": "At least one folder must be added."},
|
||
|
|
"choose_tracked_folder": {"ru": "Выбрать отслеживаемую папку", "en": "Select folder to track"},
|
||
|
|
"delete_confirm_title": {"ru": "Удаление", "en": "Delete"},
|
||
|
|
"delete_confirm_text": {"ru": "Вы действительно хотите безвозвратно удалить {n} файлов?",
|
||
|
|
"en": "Permanently delete {n} file(s)?"},
|
||
|
|
"deleted_from_disk": {"ru": "Удалено файлов с диска", "en": "Files deleted from disk"},
|
||
|
|
"copied_to_clipboard": {"ru": "Изображение скопировано в буфер обмена.",
|
||
|
|
"en": "Image copied to clipboard."},
|
||
|
|
"renamed_ok": {"ru": "Файл успешно переименован", "en": "File renamed"},
|
||
|
|
"rename_failed": {"ru": "Не удалось переименовать файл", "en": "Failed to rename file"},
|
||
|
|
"settings_applied": {"ru": "Настройки успешно применены.", "en": "Settings applied."},
|
||
|
|
"watcher_restarting": {"ru": "Перезапуск службы отслеживания...", "en": "Restarting watcher..."},
|
||
|
|
"params_updated": {"ru": "Параметры успешно обновлены.", "en": "Parameters updated."},
|
||
|
|
"db_save_error": {"ru": "Не удалось сохранить изменения в БД.", "en": "Failed to save changes to DB."},
|
||
|
|
|
||
|
|
# --- Перевод ---
|
||
|
|
"translate_to": {"ru": "Перевести на {lang}", "en": "Translate to {lang}"},
|
||
|
|
"translation_title": {"ru": "Перевод", "en": "Translation"},
|
||
|
|
"original_text": {"ru": "Оригинал:", "en": "Original:"},
|
||
|
|
"translated_text": {"ru": "Перевод (можно редактировать):",
|
||
|
|
"en": "Translation (editable):"},
|
||
|
|
"translating": {"ru": "Перевод...", "en": "Translating..."},
|
||
|
|
"apply": {"ru": "Применить", "en": "Apply"},
|
||
|
|
"translation_failed": {"ru": "Не удалось выполнить перевод", "en": "Translation failed"},
|
||
|
|
"translation_languages": {"ru": "Языки перевода (в контекстном меню):",
|
||
|
|
"en": "Translation languages (in context menu):"},
|
||
|
|
|
||
|
|
# --- Редактор workflow/prompt ---
|
||
|
|
"tab_prompt_json": {"ru": "Prompt JSON", "en": "Prompt JSON"},
|
||
|
|
"save_workflow": {"ru": "Сохранить workflow", "en": "Save workflow"},
|
||
|
|
"save_prompt_json": {"ru": "Сохранить prompt", "en": "Save prompt"},
|
||
|
|
"invalid_json": {"ru": "Некорректный JSON: {err}", "en": "Invalid JSON: {err}"},
|
||
|
|
"json_saved": {"ru": "JSON сохранён в БД.", "en": "JSON saved to DB."},
|
||
|
|
"format_json": {"ru": "Форматировать", "en": "Format"},
|
||
|
|
"prompt_json_not_found": {"ru": "Prompt не найден", "en": "Prompt not found"},
|
||
|
|
|
||
|
|
"randomize_seed": {"ru": "Новый seed при отправке", "en": "New seed on send"},
|
||
|
|
|
||
|
|
# --- ComfyUI ---
|
||
|
|
"sending_prompt": {"ru": "Отправка промта в ComfyUI...", "en": "Sending prompt to ComfyUI..."},
|
||
|
|
"prompt_queued": {"ru": "Промт успешно добавлен в очередь ComfyUI!",
|
||
|
|
"en": "Prompt queued in ComfyUI!"},
|
||
|
|
"prompt_send_failed": {"ru": "Не удалось отправить промт.", "en": "Failed to send prompt."},
|
||
|
|
"no_prompt_graph": {"ru": "У изображения отсутствует prompt-граф.",
|
||
|
|
"en": "Image has no prompt graph."},
|
||
|
|
"network_error_title": {"ru": "Ошибка сети / API", "en": "Network / API error"},
|
||
|
|
"task_id": {"ru": "ID задачи", "en": "Task ID"},
|
||
|
|
"comfy_connect_error": {"ru": "Не удалось подключиться к ComfyUI по адресу {url}.\nУбедитесь, что сервер запущен.",
|
||
|
|
"en": "Could not connect to ComfyUI at {url}.\nMake sure the server is running."},
|
||
|
|
"comfy_server_error": {"ru": "Сервер ComfyUI вернул ошибку {code}", "en": "ComfyUI server returned error {code}"},
|
||
|
|
"metadata_error": {"ru": "Ошибка метаданных изображения (неверный формат JSON)",
|
||
|
|
"en": "Image metadata error (invalid JSON)"},
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def set_language(lang: str) -> None:
|
||
|
|
global _current_language
|
||
|
|
_current_language = lang if lang in ("ru", "en") else "ru"
|
||
|
|
|
||
|
|
|
||
|
|
def get_language() -> str:
|
||
|
|
return _current_language
|
||
|
|
|
||
|
|
|
||
|
|
def tr(key: str, **kwargs) -> str:
|
||
|
|
"""Возвращает строку для текущего языка; {placeholders} подставляются из kwargs."""
|
||
|
|
entry = STRINGS.get(key)
|
||
|
|
if not entry:
|
||
|
|
return key
|
||
|
|
text = entry.get(_current_language) or entry.get("ru") or key
|
||
|
|
if kwargs:
|
||
|
|
try:
|
||
|
|
text = text.format(**kwargs)
|
||
|
|
except (KeyError, IndexError):
|
||
|
|
pass
|
||
|
|
return text
|