fix(gui): populate engine dropdown without environment config
- Defer engine imports until translation starts - Add regression coverage for source downloads without environment settings
This commit is contained in:
parent
3b13575a74
commit
a371223189
2 changed files with 110 additions and 56 deletions
|
|
@ -19,6 +19,7 @@ import traceback
|
|||
import signal
|
||||
import multiprocessing
|
||||
import re
|
||||
from importlib import import_module
|
||||
from colorama import Fore
|
||||
from tqdm import tqdm
|
||||
from dotenv import load_dotenv
|
||||
|
|
@ -75,6 +76,39 @@ BATCH_COLLECT_LIVE_CHARGE_NOTE = (
|
|||
)
|
||||
|
||||
|
||||
TRANSLATION_MODULE_SPECS = (
|
||||
("RPG Maker MV/MZ", (".json",), "modules.rpgmakermvmz", "handleMVMZ"),
|
||||
("CSV", (".csv",), "modules.csv", "handleCSV"),
|
||||
("Tyrano", (".ks",), "modules.tyrano", "handleTyrano"),
|
||||
("Kirikiri", (".ks",), "modules.kirikiri", "handleKirikiri"),
|
||||
("JSON", (".json",), "modules.json", "handleJSON"),
|
||||
("Lune", (".l",), "modules.lune", "handleLune"),
|
||||
("Yuris", (".json",), "modules.yuris", "handleYuris"),
|
||||
("NScript", (".nscript",), "modules.nscript", "handleOnscripter"),
|
||||
("Wolf RPG (WolfDawn)", (".json",), "modules.wolfdawn", "handleWolfDawn"),
|
||||
("Wolf RPG", (".json",), "modules.wolf", "handleWOLF"),
|
||||
("Wolf RPG 2", (".txt",), "modules.wolf2", "handleWOLF2"),
|
||||
("Regex", (".txt", ".json", ".script", ".csv"), "modules.regex", "handleRegex"),
|
||||
("Text", (".txt", ".srt"), "modules.text", "handleText"),
|
||||
("RenPy", (".rpy",), "modules.renpy", "handleRenpy"),
|
||||
("Unity", (".unity",), "modules.unity", "handleUnity"),
|
||||
("Images", (".png", ".jpg", ".jpeg"), "modules.images", "handleImages"),
|
||||
("RPG Maker Plugin", (".js",), "modules.rpgmakerplugin", "handlePlugin"),
|
||||
("Aquedi4 Prepared JSON", (".json",), "modules.aquedi4", "handleAquedi4"),
|
||||
("SRPG Studio", (".json",), "modules.srpg", "handleSRPG"),
|
||||
)
|
||||
|
||||
|
||||
def _lazy_module_handler(module_name, handler_name):
|
||||
"""Return a handler that imports its engine only when translation starts."""
|
||||
|
||||
def run(*args, **kwargs):
|
||||
module = import_module(module_name)
|
||||
return getattr(module, handler_name)(*args, **kwargs)
|
||||
|
||||
return run
|
||||
|
||||
|
||||
class _ShimLabel:
|
||||
"""Plain stand-in for QLabel used by Files-tab row helpers (no real Qt widget)."""
|
||||
|
||||
|
|
@ -1647,49 +1681,19 @@ class TranslationTab(QWidget):
|
|||
|
||||
def setup_module_list(self):
|
||||
"""Set up the module selection list."""
|
||||
# Import modules to get the list
|
||||
try:
|
||||
sys.path.append(str(self.project_root))
|
||||
from modules.rpgmakermvmz import handleMVMZ
|
||||
from modules.csv import handleCSV
|
||||
from modules.tyrano import handleTyrano
|
||||
from modules.kirikiri import handleKirikiri
|
||||
from modules.json import handleJSON
|
||||
from modules.lune import handleLune
|
||||
from modules.yuris import handleYuris
|
||||
from modules.nscript import handleOnscripter
|
||||
from modules.wolf import handleWOLF
|
||||
from modules.wolf2 import handleWOLF2
|
||||
from modules.wolfdawn import handleWolfDawn
|
||||
from modules.regex import handleRegex
|
||||
from modules.text import handleText
|
||||
from modules.renpy import handleRenpy
|
||||
from modules.unity import handleUnity
|
||||
from modules.images import handleImages
|
||||
from modules.rpgmakerplugin import handlePlugin
|
||||
from modules.aquedi4 import handleAquedi4
|
||||
from modules.srpg import handleSRPG
|
||||
|
||||
# Engine modules read translation settings during import. A downloaded
|
||||
# source archive intentionally has no .env yet, so importing every
|
||||
# engine here used to abort discovery and leave only the fallback item.
|
||||
# Keep the UI registry independent and defer imports until a handler is
|
||||
# actually used.
|
||||
self.modules = [
|
||||
["RPG Maker MV/MZ", [".json"], handleMVMZ],
|
||||
["CSV", [".csv"], handleCSV],
|
||||
["Tyrano", [".ks"], handleTyrano],
|
||||
["Kirikiri", [".ks"], handleKirikiri],
|
||||
["JSON", [".json"], handleJSON],
|
||||
["Lune", [".l"], handleLune],
|
||||
["Yuris", [".json"], handleYuris],
|
||||
["NScript", [".nscript"], handleOnscripter],
|
||||
["Wolf RPG (WolfDawn)", [".json"], handleWolfDawn],
|
||||
["Wolf RPG", [".json"], handleWOLF],
|
||||
["Wolf RPG 2", [".txt"], handleWOLF2],
|
||||
["Regex", [".txt", ".json", ".script", ".csv"], handleRegex],
|
||||
["Text", [".txt", ".srt"], handleText],
|
||||
["RenPy", [".rpy"], handleRenpy],
|
||||
["Unity", [".unity"], handleUnity],
|
||||
["Images", [".png", ".jpg", ".jpeg"], handleImages],
|
||||
["RPG Maker Plugin", [".js"], handlePlugin],
|
||||
["Aquedi4 Prepared JSON", [".json"], handleAquedi4],
|
||||
["SRPG Studio", [".json"], handleSRPG],
|
||||
[
|
||||
display_name,
|
||||
list(extensions),
|
||||
_lazy_module_handler(module_name, handler_name),
|
||||
]
|
||||
for display_name, extensions, module_name, handler_name
|
||||
in TRANSLATION_MODULE_SPECS
|
||||
]
|
||||
|
||||
for module in self.modules:
|
||||
|
|
@ -1698,14 +1702,6 @@ class TranslationTab(QWidget):
|
|||
if self.module_combo.count():
|
||||
self._on_module_changed(self.module_combo.currentText())
|
||||
|
||||
except Exception as e:
|
||||
# Store error for later logging since log_display might not exist yet
|
||||
self.module_load_error = f"Warning: Could not load modules: {str(e)}"
|
||||
# Add a default option
|
||||
self.module_combo.addItem("RPG Maker MV/MZ (.json)")
|
||||
self.modules = [["RPG Maker MV/MZ", [".json"], None]]
|
||||
self._on_module_changed(self.module_combo.currentText())
|
||||
|
||||
def _on_module_changed(self, text: str):
|
||||
lowered = text.lower()
|
||||
if "wolf" in lowered and "wolf rpg 2" not in lowered:
|
||||
|
|
|
|||
58
tests/test_translation_engine_dropdown.py
Normal file
58
tests/test_translation_engine_dropdown.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
"""Regression tests for Translation-tab engine discovery."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
try:
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from gui.translation_tab import TRANSLATION_MODULE_SPECS, TranslationTab
|
||||
|
||||
_HAS_QT = True
|
||||
except Exception: # pragma: no cover - PyQt5 not installed
|
||||
_HAS_QT = False
|
||||
|
||||
|
||||
@unittest.skipUnless(_HAS_QT, "PyQt5 not available")
|
||||
class TranslationEngineDropdownTests(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
cls._app = QApplication.instance() or QApplication([])
|
||||
|
||||
def test_all_engines_are_listed_without_environment_configuration(self) -> None:
|
||||
required_settings = (
|
||||
"model",
|
||||
"language",
|
||||
"timeout",
|
||||
"width",
|
||||
"listWidth",
|
||||
"noteWidth",
|
||||
)
|
||||
without_settings = {
|
||||
key: value
|
||||
for key, value in os.environ.items()
|
||||
if key not in required_settings
|
||||
}
|
||||
|
||||
with patch.dict(os.environ, without_settings, clear=True):
|
||||
tab = TranslationTab()
|
||||
|
||||
expected = [
|
||||
f"{name} ({', '.join(extensions)})"
|
||||
for name, extensions, _module, _handler in TRANSLATION_MODULE_SPECS
|
||||
]
|
||||
actual = [
|
||||
tab.module_combo.itemText(index)
|
||||
for index in range(tab.module_combo.count())
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
self.assertTrue(all(callable(module[2]) for module in tab.modules))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Reference in a new issue