Before step_fetch, scan all downloads/ subdirs and move any mod that
comparison.json now assigns to a different group. Matching uses steam_id
(via meta.cpp publishedid) first, normalized name as fallback.
Stale junctions in arma_dir are removed before the folder move so
step_link can re-create them pointing to the new location.
- New arma_modlist_tools/migrator.py: migrate_mod_groups()
- run.py: step_migrate(), --skip-migrate flag, wired into dispatch loop
- gui/app.py: step_migrate inserted as Step 3/5 between compare and fetch
- gui/locales.py: add step3/4/5 names (en + vi), renumber old 3->4, 4->5
- test_suite.py: 7 new migrator tests (158 total, 0 failed)
Three issues caused the Logs view to appear blank during a real pipeline run:
1. `from run import step_fetch, step_link` was outside the worker's
try/except/finally. An import failure silently killed the thread,
leaving _pipeline_done uncalled and the Run button stuck disabled
forever. Now wrapped in its own try/except that posts the error to
the log and resets the UI.
2. `build_server_index` makes N sequential HTTP requests (one per mod
folder's meta.cpp) with no output during the scan. Added an optional
`progress_fn(current, total, name)` callback; step_fetch wires it to
print progress every 25 folders so the log never goes silent.
3. No immediate feedback after clicking Start — the log was blank until
the worker thread started printing. Now posts a "Pipeline started"
banner from the main thread before the worker launches.
Introduces a two-language (EN/VI) i18n system with hot-swap support.
All ~160 user-facing strings are centralised in gui/locales.py; views
retranslate in-place on language switch without restarting the app.
- gui/locales.py: new file — _EN/_VI dicts, t() lookup, set_language(),
get_language(); assert guard ensures EN/VI key parity
- gui/app.py: switch_language(), _apply_startup_language(),
_save_language_pref(), _rebuild_nav_labels(); language stored in
config.json under ui.language; pipeline step headers and run_tool
status lines translated
- gui/views/settings.py: Language dropdown card (English / Tiếng Việt)
- gui/views/dashboard.py: all strings via t(); static header widgets
stored and retranslated in refresh()
- gui/views/mods.py: all strings via t(); _STATUS dict built at call
time so server status labels update on language switch
- gui/views/tools.py: all strings via _translatable registry; tab names
and segmented-button values kept in English (CTkTabview constraint)
- gui/views/logs.py: title + Copy/Clear buttons stored, retranslated
- gui/wizard.py: all 3 pages fully translated
- docs/huong-dan-su-dung.md: full Vietnamese user guide
- CLAUDE.md: documents localization architecture and constraints