| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- """Simple two-language localization (EN / RU) for the LF-MRI GUI."""
- from __future__ import annotations
- _lang: str = "en"
- _listeners: list = []
- def add_listener(callback) -> None:
- """Register a callable to be invoked on every language change."""
- if callback not in _listeners:
- _listeners.append(callback)
- def remove_listener(callback) -> None:
- """Unregister a previously added listener (silently ignores unknown callbacks)."""
- try:
- _listeners.remove(callback)
- except ValueError:
- pass
- _T: dict[str, dict[str, str]] = {
- # --- nav bar ---
- "tab_nav_sequence": {"en": "Sequence", "ru": "Последовательность"},
- "tab_nav_scanner": {"en": "Scanner", "ru": "Сканнер"},
- "tab_nav_fid": {"en": "FID", "ru": "FID"},
- "tab_nav_scanning": {"en": "Scanning", "ru": "Сканирование"},
- "tab_nav_spectro": {"en": "Spectroscopy", "ru": "Спектроскопия"},
- "active_tab": {"en": "Active", "ru": "Активно"},
- # --- scanner_tab ---
- "url_label": {"en": "Orchestrator URL:", "ru": "URL оркестратора:"},
- "btn_connect": {"en": "Connect", "ru": "Подключить"},
- "offline": {"en": "● Offline", "ru": "● Не подключён"},
- "online": {"en": "● Online", "ru": "● Подключён"},
- "conn_error": {"en": "● Error", "ru": "● Ошибка"},
- "grp_scenario": {"en": "Scenario", "ru": "Сценарий"},
- "btn_refresh": {"en": "Refresh", "ru": "Обновить"},
- "grp_seq_info": {"en": "Sequence Info", "ru": "Информация о посл-ти"},
- "no_seq_loaded": {"en": "No sequence loaded", "ru": "Посл-ть не загружена"},
- "grp_job": {"en": "Job", "ru": "Задание"},
- "no_job": {"en": "- no job -", "ru": "- нет задания -"},
- "btn_load_scenario": {"en": "Load Scenario", "ru": "Загрузить сценарий"},
- "btn_build_scenario": {"en": "Build custom…", "ru": "Собрать вручную…"},
- "btn_run_all": {"en": "Run All", "ru": "Запустить всё"},
- "btn_next_step": {"en": "Next Step", "ru": "Следующий шаг"},
- "btn_abort": {"en": "Abort", "ru": "Прервать"},
- "col_step": {"en": "Step", "ru": "Шаг"},
- "col_status": {"en": "Status", "ru": "Статус"},
- "col_result": {"en": "Result", "ru": "Результат"},
- "tab_step_result": {"en": "Step Result", "ru": "Результат шага"},
- "tab_step_params": {"en": "Step Params", "ru": "Параметры шага"},
- "tab_seq_info_view": {"en": "Sequence Info", "ru": "Инф. о посл-ти"},
- "tab_log": {"en": "Log", "ru": "Журнал"},
- # --- seq_interp_tab ---
- "btn_load_seq": {"en": "Load .seq", "ru": "Загрузить .seq"},
- "btn_hw_config": {"en": "HW Config", "ru": "Конфиг ЖО"},
- "btn_out_dir": {"en": "Output Dir", "ru": "Папка вывода"},
- "btn_run": {"en": "Run", "ru": "Запустить"},
- "btn_export": {"en": "Export", "ru": "Экспорт"},
- "btn_fit_all": {"en": "Fit All", "ru": "Вписать всё"},
- "blocks_closed": {"en": "Blocks ▾", "ru": "Блоки ▾"},
- "blocks_open": {"en": "Blocks ▴", "ru": "Блоки ▴"},
- "btn_send_scanner": {"en": "Send to Scanner", "ru": "На сканнер"},
- "grp_seq_metadata": {"en": "Sequence Metadata", "ru": "Метаданные посл-ти"},
- "grp_warnings": {"en": "Warnings", "ru": "Предупреждения"},
- "status_ready": {"en": "Ready", "ru": "Готово"},
- "no_file_selected": {"en": "No file selected", "ru": "Файл не выбран"},
- # --- fid_tab ---
- "btn_generate": {"en": "Generate", "ru": "Генерировать"},
- "btn_save_seq": {"en": "Save .seq", "ru": "Сохранить .seq"},
- "btn_load_seq_tab": {"en": "→ Load in Sequence Tab", "ru": "→ В Sequence Tab"},
- "grp_fid_params": {"en": "FID Parameters", "ru": "Параметры FID"},
- "grp_pulse_type": {"en": "Pulse type", "ru": "Тип импульса"},
- "grp_out_filename": {"en": "Output filename (no extension)", "ru": "Имя файла (без расширения)"},
- "fid_status_init": {
- "en": "Ready - set parameters and click Generate",
- "ru": "Готово - задайте параметры и нажмите Генерировать",
- },
- # --- scanning_tab ---
- "protocols_header": {"en": "Protocols", "ru": "Протоколы"},
- "tab_main": {"en": "Main", "ru": "Основные"},
- "tab_contrast": {"en": "Contrast", "ru": "Контраст"},
- "tab_resolution": {"en": "Resolution", "ru": "Разрешение"},
- "tab_system": {"en": "System", "ru": "Система"},
- "tab_geometry": {"en": "Geometry", "ru": "Геометрия"},
- "grp_orientation": {"en": "Orientation", "ru": "Ориентация"},
- "grp_rotation": {"en": "Rotation", "ru": "Поворот"},
- "grp_rot_matrix": {"en": "Rotation Matrix", "ru": "Матрица поворота"},
- "no_data": {"en": "No data", "ru": "Нет данных"},
- "btn_scan": {"en": "Scan", "ru": "Сканировать"},
- "btn_stop_scan": {"en": "Stop", "ru": "Стоп"},
- "scanning_status": {"en": "Scanning...", "ru": "Сканирование..."},
- "ready_to_scan": {"en": "Ready to scan", "ru": "Готово к сканированию"},
- "dlg_no_data_title": {"en": "No data", "ru": "Нет данных"},
- "dlg_no_seq_msg": {
- "en": "First load and export a sequence\nin the \"Sequence\" tab.",
- "ru": "Сначала загрузите и экспортируйте последовательность\nво вкладке \"Sequence\".",
- },
- "done_status": {"en": "Done", "ru": "Готово"},
- "error_prefix": {"en": "Error", "ru": "Ошибка"},
- # --- spectroscopy_tab ---
- "btn_load_json": {"en": "Load JSON...", "ru": "Загрузить JSON..."},
- "btn_analyze": {"en": "Analyze", "ru": "Анализировать"},
- "btn_batch": {"en": "Batch...", "ru": "Пакетный..."},
- "btn_export_sp": {"en": "Export...", "ru": "Экспорт..."},
- "btn_clear": {"en": "Clear", "ru": "Очистить"},
- "grp_nmr_params": {"en": "NMR Parameters", "ru": "Параметры ЯМР"},
- "grp_metadata": {"en": "Metadata", "ru": "Метаданные"},
- "grp_metrics": {"en": "Metrics", "ru": "Метрики"},
- "spec_status_init": {
- "en": "Click 'Load JSON...' to open a hardware JSON file and start NMR analysis",
- "ru": "Нажмите 'Загрузить JSON...' для открытия JSON-файла и запуска анализа ЯМР",
- },
- "batch_title": {"en": "Batch NMR Analysis", "ru": "ЯМР анализ набора данных"},
- "folder_label": {"en": "Folder:", "ru": "Папка:"},
- "btn_browse": {"en": "Browse...", "ru": "Обзор..."},
- "cb_use_current": {
- "en": "Use current parameters from the Spectroscopy tab",
- "ru": "Использовать текущие параметры вкладки Spectroscopy",
- },
- "btn_run_batch": {"en": "Run Batch", "ru": "Исследовать набор"},
- "btn_export_csv": {"en": "Export CSV...", "ru": "Экспорт CSV..."},
- "btn_close": {"en": "Close", "ru": "Закрыть"},
- "batch_status_init": {
- "en": "Select a folder and click Run Batch.",
- "ru": "Выберите папку и нажмите Исследовать набор.",
- },
- # --- mode selector ---
- "mode_plug": {"en": "PLUG", "ru": "ИМИТАТОР"},
- "mode_real": {"en": "HACK RF", "ru": "HACK RF"},
- "mode_confirm_title": {"en": "Switch Mode", "ru": "Смена режима"},
- "mode_confirm_to_real": {
- "en": "Switch connection to Hack RF mode?\n\nPhysical devices will be used for acquisition.",
- "ru": "Переключиться в режим подключения к спектрометру Hack RF?\n\nДля сбора данных будет использоваться реальное оборудование.",
- },
- "mode_confirm_to_plug": {
- "en": "Switch to PLUG (stub) mode?\n\nPhysical devices will NOT be used.",
- "ru": "Переключиться в режим имитатора?\n\nРеальное оборудование использоваться не будет.",
- },
- "mode_error": {"en": "Mode switch failed", "ru": "Ошибка смены режима"},
- # --- controls_panel ---
- "grp_hw_delays": {"en": "Hardware Delays / Rasters", "ru": "Задержки / Растры ЖО"},
- "btn_apply_rerun": {"en": "Apply && Rerun", "ru": "Применить && Перезапуск"},
- "btn_reset": {"en": "Reset", "ru": "Сброс"},
- "btn_reload_config": {"en": "Reload Config", "ru": "Перезагрузить конфиг"},
- "btn_save_hw": {"en": "Save HW Config", "ru": "Сохранить конфиг ЖО"},
- }
- _TAB_NAV_KEYS = [
- "tab_nav_sequence",
- "tab_nav_scanner",
- "tab_nav_fid",
- "tab_nav_scanning",
- "tab_nav_spectro",
- ]
- _PARAM_TABS_KEYS = [
- "tab_main",
- "tab_contrast",
- "tab_resolution",
- "tab_system",
- "tab_geometry",
- ]
- def tr(key: str) -> str:
- """Return translated string for *key* in the current language."""
- entry = _T.get(key)
- if entry is None:
- return key
- return entry.get(_lang, entry.get("en", key))
- def current_language() -> str:
- return _lang
- def set_language(lang: str) -> None:
- global _lang
- if lang in ("en", "ru"):
- _lang = lang
- for cb in list(_listeners):
- try:
- cb()
- except Exception:
- pass
|