Files
dinlo 34a40ae759 Initial release: Comfy-Studio v1.0.0
Десктопная галерея и менеджер изображений ComfyUI (PyQt6 + SQLite):
- мультипапочный мониторинг (watchdog), грид миниатюр, drag-and-drop
- теги, избранное, рейтинг, поиск по промтам
- парсер метаданных ComfyUI (PNG/WebP/JPEG), редактор workflow/prompt JSON
- перевод выделенного текста через контекстное меню (настраиваемые языки)
- отправка промта в очередь ComfyUI с внедрением правок и нового seed
- полноэкранный просмотр, локализация ru/en, тёмная тема

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 14:09:57 +08:00

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