from __future__ import annotations # --------------------------------------------------------------------------- # Localization module — English + Vietnamese # # Usage: # from gui.locales import t # label_text = t("dashboard.title") # label_text = t("dashboard.sel_count", n_sel=2, n_total=5) # # Tab names in ToolsView are NOT translated — they double as CTkTabview # lookup keys and cannot be renamed after creation. # Segmented-button values in ToolsView ("Status", "Link", "Unlink") are also # kept in English because they drive internal logic in _lm_on_change(). # --------------------------------------------------------------------------- _LANG: str = "en" _EN: dict[str, str] = { # ── App / sidebar ──────────────────────────────────────────────────────── "app.title": "Arma Mod Manager", "nav.dashboard": "Dashboard", "nav.mods": "Mods", "nav.tools": "Tools", "nav.logs": "Logs", "nav.settings": "Settings", # ── Pipeline step headers (printed to log) ─────────────────────────────── "pipeline.step1_name": "Parse presets", "pipeline.step2_name": "Compare presets", "pipeline.step3_name": "Download mods", "pipeline.step4_name": "Link mods", # ── app.py dialogs ──────────────────────────────────────────────────────── "app.dlg_presets_title": "Not enough presets selected", "app.dlg_presets_body": ( "Please select at least 2 preset files to compare.\n\n" "Use the checkboxes on the Dashboard to choose which presets to use." ), "app.dlg_setup_title": "Setup required", "app.dlg_setup_body": "Please complete Setup first.", # ── run_tool status lines (go to log) ──────────────────────────────────── "app.tool_done": "✓ Done", "app.tool_exit_code": "✗ Exited with code {code}", "app.tool_failed": "✗ Failed to start {script}: {e}", # ── Dashboard ──────────────────────────────────────────────────────────── "dashboard.title": "Dashboard", "dashboard.refresh_btn": "⟳ Refresh", "dashboard.preset_card_title": "Preset Files", "dashboard.preset_card_desc": "HTML exports from Arma 3 Launcher → Mods → Export to HTML", "dashboard.sel_count": "{n_sel} of {n_total} selected", "dashboard.btn_none": "None", "dashboard.btn_all": "All", "dashboard.btn_add": "+ Add Preset Files", "dashboard.no_config": "No config found. Complete Setup first.", "dashboard.folder_missing": "Folder missing:\n{path}", "dashboard.no_presets": "No preset files yet.\nUse the button below to add them.", "dashboard.file_dialog_title": "Select Arma 3 Launcher preset files", "dashboard.dlg_setup_title": "Setup required", "dashboard.dlg_setup_body": "Please complete Setup before adding presets.", "dashboard.pipeline_title": "Pipeline Status", "dashboard.step_parse": "Parse presets", "dashboard.step_compare": "Compare presets", "dashboard.step_download": "Download mods", "dashboard.step_link": "Link to Arma", "dashboard.stats": "{total} mods · {shared} shared", "dashboard.stats_missing": "\n{missing} missing from server", "dashboard.run_btn": "▶ Run Full Pipeline", "dashboard.running": "Running…", # ── Mods ───────────────────────────────────────────────────────────────── "mods.title": "Mods", "mods.refresh_btn": "⟳ Refresh", "mods.check_btn": "☁ Check Updates", "mods.check_btn_checking": "Checking…", "mods.search_label": "Search:", "mods.search_placeholder": "Filter mods in active tab…", "mods.no_config": "No config found. Complete Setup first.", "mods.no_data": ( "No mod data yet.\n" "Go to Dashboard, select your presets, then click Run Full Pipeline." ), "mods.read_error": "Error reading comparison.json: {e}", "mods.col_name": "Mod Name", "mods.col_downloaded": "Downloaded", "mods.col_linked": "Linked", "mods.col_server": "Server Status", "mods.status_ok": "✓ Up to date", "mods.status_stale": "⚠ {n} outdated", "mods.status_not_downloaded": "—", "mods.status_not_on_server": "Not on server", "mods.status_error": "✗ Error", "mods.status_checking": "Checking…", "mods.update_btn": "Update", # ── Logs ───────────────────────────────────────────────────────────────── "logs.title": "Logs", "logs.copy_btn": "Copy", "logs.clear_btn": "Clear", # ── Settings ───────────────────────────────────────────────────────────── "settings.title": "Settings", "settings.server_card_title": "Server & Path Configuration", "settings.server_card_desc": ( "Re-run the setup wizard to change your server URL, " "credentials, or Arma folder." ), "settings.wizard_btn": "Open Setup Wizard", "settings.appearance_title": "Appearance", "settings.language_title": "Language", "settings.config_title": "Current Configuration", # ── Wizard ─────────────────────────────────────────────────────────────── "wizard.title": "Setup — Arma Mod Manager", "wizard.step1_title": "Step 1 of 3 — Server Connection", "wizard.step1_desc": "Enter the details for your Caddy mod server.", "wizard.label_url": "Server URL", "wizard.label_user": "Username", "wizard.label_pw": "Password", "wizard.btn_next": "Next →", "wizard.btn_test": "Test Connection", "wizard.testing": "Testing…", "wizard.connected": "✓ Connected", "wizard.http_error": "✗ HTTP {code}", "wizard.conn_error": "✗ {e}", "wizard.step2_title": "Step 2 of 3 — Arma 3 Server Folder", "wizard.step2_desc": ( "Point to your Arma 3 Server installation. " "Links (junctions) will be created here." ), "wizard.label_arma": "Arma 3 Server folder", "wizard.btn_browse": "Browse", "wizard.step2_hint": ( "All other folders (downloads, presets) will be created " "automatically next to this tool." ), "wizard.btn_back": "← Back", "wizard.step3_title": "Step 3 of 3 — Review & Save", "wizard.step3_desc": "Check your settings, then click Save.", "wizard.not_set": "(not set)", "wizard.btn_save": "Save & Open", "wizard.browse_title": "Select Arma 3 Server folder", # ── Tools — shared ──────────────────────────────────────────────────────── "tools.title": "Tools", "tools.label_group": "Group:", "tools.label_options": "Options:", "tools.label_command": "Command:", "tools.all_groups": "All groups", "tools.no_groups": "(no groups found)", # ── Tools — Check Names ────────────────────────────────────────────────── "tools.cn_desc": ( "Scan mod folders and compare against the server. " "Reports naming mismatches (MISMATCH), unrecognised folders " "(NOT_ON_SERVER), and wrong Steam IDs in meta.cpp (ID_COLLISION)." ), "tools.cn_fix_chk": "Auto-fix folder name mismatches (--fix)", "tools.cn_fix_ids_chk": "Auto-fix wrong Steam IDs in meta.cpp (--fix-ids)", "tools.cn_warn": ( "⚠ --fix renames folders and updates junctions. " "--fix-ids rewrites meta.cpp files." ), "tools.cn_btn": "Run Check Names", # ── Tools — Update Mods ────────────────────────────────────────────────── "tools.um_desc": ( "Re-download mod files whose size on the server differs from " "your local copy. Use --force to re-download everything " "regardless of size." ), "tools.um_mod_label": "Mod folder:", "tools.um_mod_placeholder": "Optional — e.g. @ace", "tools.um_mod_hint": "(only when a specific group is selected)", "tools.um_force_chk": "Force re-download all files (--force)", "tools.um_warn": ( "⚠ --force re-downloads every file regardless of size. " "This may transfer a large amount of data." ), "tools.um_btn": "Run Update", # ── Tools — Link Mods ──────────────────────────────────────────────────── "tools.lm_desc": ( "Manage junction/symlink links between your downloads folder " "and the Arma 3 directory.\n" "Status — show what's linked. " "Link — create missing junctions. " "Unlink — remove junctions (mod files are NOT deleted)." ), "tools.lm_warn": ( "⚠ Unlink removes junction links from the Arma 3 directory. " "Mod files in downloads/ are NOT deleted." ), "tools.lm_show_status": "Show Status", "tools.lm_create_links": "Create Links", "tools.lm_remove_links": "Remove Links", "tools.lm_no_group_title": "No group selected", "tools.lm_no_group_body": "Please select a group from the dropdown.", "tools.lm_confirm_title": "Confirm Unlink", "tools.lm_confirm_body": ( "Remove junction links for group '{group}'?\n\n" "This removes links from the Arma 3 directory but does NOT delete " "mod files in downloads/." ), # ── Tools — Sync Missing ───────────────────────────────────────────────── "tools.sm_desc": ( "Retry downloading mods that were missing from the server " "when you last ran the pipeline. " "Checks the server again and downloads any that have since appeared." ), "tools.sm_btn": "Run Sync Missing", "tools.sm_count": "{count} mod(s) currently listed as missing.", "tools.sm_no_report": "No missing_report.json found — run the pipeline first.", # ── Tools — Report Missing ─────────────────────────────────────────────── "tools.rm_desc": ( "Check which mods from comparison.json are absent from the " "file server. Saves missing_report.json so you can track what " "still needs to be added to the server." ), "tools.rm_btn": "Generate Report", "tools.rm_last": "Last generated: {ts}", "tools.rm_none": "No report yet.", # ── Tools — Clean Orphans ──────────────────────────────────────────────── "tools.oc_desc": ( "Scan the downloads folder for mod folders that are no longer " "referenced in comparison.json. These orphans accumulate when you " "remove mods from your presets and re-run the pipeline. " "Select the ones you want to remove to free up disk space." ), "tools.oc_warn": ( "⚠ Deleting orphans permanently removes mod files from disk. " "This cannot be undone." ), "tools.oc_scan_btn": "Scan for Orphans", "tools.oc_scanning": "Scanning…", "tools.oc_no_config": "No config found. Complete Setup first.", "tools.oc_no_comparison": "No comparison.json found — run the pipeline first.", "tools.oc_none_found": "No orphans found. Your downloads folder is clean.", "tools.oc_found": "{count} orphan(s) found — {size} total", "tools.oc_sel_all": "Select All", "tools.oc_sel_none": "Deselect All", "tools.oc_delete_btn": "Delete Selected", "tools.oc_confirm_title": "Confirm Delete", "tools.oc_confirm_body": ( "Permanently delete {count} orphan folder(s) ({size})?\n\n" "This cannot be undone." ), "tools.oc_done": "Deleted {count} folder(s), freed {size}.", "tools.oc_error": "Error deleting {path}: {e}", "tools.oc_error_title": "Delete errors", "tools.oc_scan_error": "Scan error: {e}", } _VI: dict[str, str] = { # ── App / sidebar ──────────────────────────────────────────────────────── "app.title": "Arma Mod Manager", "nav.dashboard": "Tổng quan", "nav.mods": "Danh sách Mod", "nav.tools": "Công cụ", "nav.logs": "Nhật ký", "nav.settings": "Cài đặt", # ── Pipeline step headers ──────────────────────────────────────────────── "pipeline.step1_name": "Phân tích preset", "pipeline.step2_name": "So sánh preset", "pipeline.step3_name": "Tải mod", "pipeline.step4_name": "Liên kết mod", # ── app.py dialogs ──────────────────────────────────────────────────────── "app.dlg_presets_title": "Chưa chọn đủ preset", "app.dlg_presets_body": ( "Vui lòng chọn ít nhất 2 tệp preset để so sánh.\n\n" "Sử dụng các ô tick ở Tổng quan để chọn preset." ), "app.dlg_setup_title": "Cần thiết lập", "app.dlg_setup_body": "Vui lòng hoàn thành thiết lập trước.", # ── run_tool status lines ──────────────────────────────────────────────── "app.tool_done": "✓ Hoàn thành", "app.tool_exit_code": "✗ Thoát với mã lỗi {code}", "app.tool_failed": "✗ Không thể khởi động {script}: {e}", # ── Dashboard ──────────────────────────────────────────────────────────── "dashboard.title": "Tổng quan", "dashboard.refresh_btn": "⟳ Làm mới", "dashboard.preset_card_title": "Tệp Preset", "dashboard.preset_card_desc": "Xuất từ Arma 3 Launcher → Mods → Export to HTML", "dashboard.sel_count": "Đã chọn {n_sel} / {n_total}", "dashboard.btn_none": "Bỏ chọn", "dashboard.btn_all": "Tất cả", "dashboard.btn_add": "+ Thêm tệp Preset", "dashboard.no_config": "Chưa tìm thấy cấu hình. Vui lòng hoàn thành thiết lập.", "dashboard.folder_missing": "Thư mục không tồn tại:\n{path}", "dashboard.no_presets": "Chưa có tệp preset.\nDùng nút bên dưới để thêm.", "dashboard.file_dialog_title": "Chọn tệp preset Arma 3 Launcher", "dashboard.dlg_setup_title": "Cần thiết lập", "dashboard.dlg_setup_body": "Vui lòng hoàn thành thiết lập trước khi thêm preset.", "dashboard.pipeline_title": "Trạng thái Pipeline", "dashboard.step_parse": "Phân tích preset", "dashboard.step_compare": "So sánh preset", "dashboard.step_download": "Tải mod", "dashboard.step_link": "Liên kết với Arma", "dashboard.stats": "{total} mod · {shared} dùng chung", "dashboard.stats_missing": "\n{missing} mod thiếu trên máy chủ", "dashboard.run_btn": "▶ Chạy toàn bộ quy trình", "dashboard.running": "Đang chạy…", # ── Mods ───────────────────────────────────────────────────────────────── "mods.title": "Danh sách Mod", "mods.refresh_btn": "⟳ Làm mới", "mods.check_btn": "☁ Kiểm tra cập nhật", "mods.check_btn_checking": "Đang kiểm tra…", "mods.search_label": "Tìm kiếm:", "mods.search_placeholder": "Lọc mod trong tab hiện tại…", "mods.no_config": "Chưa tìm thấy cấu hình. Vui lòng hoàn thành thiết lập.", "mods.no_data": ( "Chưa có dữ liệu mod.\n" "Vào Tổng quan, chọn preset rồi nhấn Chạy toàn bộ quy trình." ), "mods.read_error": "Lỗi đọc comparison.json: {e}", "mods.col_name": "Tên Mod", "mods.col_downloaded": "Đã tải", "mods.col_linked": "Đã liên kết", "mods.col_server": "Trạng thái máy chủ", "mods.status_ok": "✓ Đã cập nhật", "mods.status_stale": "⚠ {n} tệp cũ", "mods.status_not_downloaded": "—", "mods.status_not_on_server": "Không có trên máy chủ", "mods.status_error": "✗ Lỗi", "mods.status_checking": "Đang kiểm tra…", "mods.update_btn": "Cập nhật", # ── Logs ───────────────────────────────────────────────────────────────── "logs.title": "Nhật ký", "logs.copy_btn": "Sao chép", "logs.clear_btn": "Xóa", # ── Settings ───────────────────────────────────────────────────────────── "settings.title": "Cài đặt", "settings.server_card_title": "Cấu hình máy chủ & đường dẫn", "settings.server_card_desc": ( "Mở lại trình hướng dẫn thiết lập để thay đổi URL máy chủ, " "thông tin đăng nhập hoặc thư mục Arma." ), "settings.wizard_btn": "Mở trình thiết lập", "settings.appearance_title": "Giao diện", "settings.language_title": "Ngôn ngữ", "settings.config_title": "Cấu hình hiện tại", # ── Wizard ─────────────────────────────────────────────────────────────── "wizard.title": "Thiết lập — Arma Mod Manager", "wizard.step1_title": "Bước 1 / 3 — Kết nối máy chủ", "wizard.step1_desc": "Nhập thông tin máy chủ Caddy của bạn.", "wizard.label_url": "URL máy chủ", "wizard.label_user": "Tên đăng nhập", "wizard.label_pw": "Mật khẩu", "wizard.btn_next": "Tiếp theo →", "wizard.btn_test": "Kiểm tra kết nối", "wizard.testing": "Đang kiểm tra…", "wizard.connected": "✓ Đã kết nối", "wizard.http_error": "✗ HTTP {code}", "wizard.conn_error": "✗ {e}", "wizard.step2_title": "Bước 2 / 3 — Thư mục Arma 3 Server", "wizard.step2_desc": ( "Trỏ tới thư mục cài đặt Arma 3 Server của bạn. " "Các liên kết (junction) sẽ được tạo tại đây." ), "wizard.label_arma": "Thư mục Arma 3 Server", "wizard.btn_browse": "Duyệt", "wizard.step2_hint": ( "Các thư mục khác (downloads, presets) sẽ được tạo tự động " "bên cạnh công cụ này." ), "wizard.btn_back": "← Quay lại", "wizard.step3_title": "Bước 3 / 3 — Xem lại & Lưu", "wizard.step3_desc": "Kiểm tra cài đặt rồi nhấn Lưu.", "wizard.not_set": "(chưa đặt)", "wizard.btn_save": "Lưu & Mở", "wizard.browse_title": "Chọn thư mục Arma 3 Server", # ── Tools — shared ──────────────────────────────────────────────────────── "tools.title": "Công cụ", "tools.label_group": "Nhóm:", "tools.label_options": "Tùy chọn:", "tools.label_command": "Lệnh:", "tools.all_groups": "Tất cả nhóm", "tools.no_groups": "(không tìm thấy nhóm)", # ── Tools — Check Names ────────────────────────────────────────────────── "tools.cn_desc": ( "Quét thư mục mod và so sánh với máy chủ. " "Báo cáo tên không khớp (MISMATCH), thư mục không nhận ra " "(NOT_ON_SERVER) và Steam ID sai trong meta.cpp (ID_COLLISION)." ), "tools.cn_fix_chk": "Tự động sửa tên thư mục không khớp (--fix)", "tools.cn_fix_ids_chk": "Tự động sửa Steam ID sai trong meta.cpp (--fix-ids)", "tools.cn_warn": ( "⚠ --fix đổi tên thư mục và cập nhật junction. " "--fix-ids ghi đè tệp meta.cpp." ), "tools.cn_btn": "Chạy kiểm tra tên", # ── Tools — Update Mods ────────────────────────────────────────────────── "tools.um_desc": ( "Tải lại tệp mod có kích thước khác với bản trên máy chủ. " "Dùng --force để tải lại tất cả bất kể kích thước." ), "tools.um_mod_label": "Thư mục mod:", "tools.um_mod_placeholder": "Không bắt buộc — ví dụ @ace", "tools.um_mod_hint": "(chỉ dùng khi chọn một nhóm cụ thể)", "tools.um_force_chk": "Buộc tải lại tất cả tệp (--force)", "tools.um_warn": ( "⚠ --force tải lại mọi tệp bất kể kích thước. " "Điều này có thể truyền một lượng dữ liệu lớn." ), "tools.um_btn": "Chạy cập nhật", # ── Tools — Link Mods ──────────────────────────────────────────────────── "tools.lm_desc": ( "Quản lý liên kết junction/symlink giữa thư mục downloads " "và thư mục Arma 3.\n" "Status — xem liên kết hiện có. " "Link — tạo junction còn thiếu. " "Unlink — xóa junction (tệp mod KHÔNG bị xóa)." ), "tools.lm_warn": ( "⚠ Unlink xóa liên kết junction khỏi thư mục Arma 3. " "Tệp mod trong downloads/ KHÔNG bị xóa." ), "tools.lm_show_status": "Xem trạng thái", "tools.lm_create_links": "Tạo liên kết", "tools.lm_remove_links": "Xóa liên kết", "tools.lm_no_group_title": "Chưa chọn nhóm", "tools.lm_no_group_body": "Vui lòng chọn một nhóm từ danh sách.", "tools.lm_confirm_title": "Xác nhận xóa liên kết", "tools.lm_confirm_body": ( "Xóa liên kết junction cho nhóm '{group}'?\n\n" "Thao tác này xóa liên kết khỏi thư mục Arma 3 " "nhưng KHÔNG xóa tệp mod trong downloads/." ), # ── Tools — Sync Missing ───────────────────────────────────────────────── "tools.sm_desc": ( "Thử tải lại các mod bị thiếu trên máy chủ khi chạy pipeline lần trước. " "Kiểm tra lại máy chủ và tải về nếu mod đã xuất hiện." ), "tools.sm_btn": "Chạy đồng bộ mod thiếu", "tools.sm_count": "{count} mod đang được liệt kê là thiếu.", "tools.sm_no_report": "Chưa có missing_report.json — hãy chạy pipeline trước.", # ── Tools — Report Missing ─────────────────────────────────────────────── "tools.rm_desc": ( "Kiểm tra mod nào trong comparison.json không có trên máy chủ. " "Lưu missing_report.json để theo dõi mod cần bổ sung." ), "tools.rm_btn": "Tạo báo cáo", "tools.rm_last": "Tạo lần cuối: {ts}", "tools.rm_none": "Chưa có báo cáo.", # ── Tools — Clean Orphans ──────────────────────────────────────────────── "tools.oc_desc": ( "Quét thư mục downloads để tìm các thư mục mod không còn được " "tham chiếu trong comparison.json. Các mod mồ côi này tích tụ khi " "bạn xóa mod khỏi preset và chạy lại pipeline. " "Chọn các thư mục muốn xóa để giải phóng dung lượng." ), "tools.oc_warn": ( "⚠ Xóa mod mồ côi sẽ xóa vĩnh viễn tệp mod khỏi ổ đĩa. " "Thao tác này không thể hoàn tác." ), "tools.oc_scan_btn": "Quét mod mồ côi", "tools.oc_scanning": "Đang quét…", "tools.oc_no_config": "Chưa tìm thấy cấu hình. Vui lòng hoàn thành thiết lập.", "tools.oc_no_comparison": "Chưa có comparison.json — hãy chạy pipeline trước.", "tools.oc_none_found": "Không tìm thấy mod mồ côi. Thư mục downloads sạch.", "tools.oc_found": "Tìm thấy {count} mod mồ côi — tổng {size}", "tools.oc_sel_all": "Chọn tất cả", "tools.oc_sel_none": "Bỏ chọn", "tools.oc_delete_btn": "Xóa đã chọn", "tools.oc_confirm_title": "Xác nhận xóa", "tools.oc_confirm_body": ( "Xóa vĩnh viễn {count} thư mục mồ côi ({size})?\n\n" "Thao tác này không thể hoàn tác." ), "tools.oc_done": "Đã xóa {count} thư mục, giải phóng {size}.", "tools.oc_error": "Lỗi khi xóa {path}: {e}", "tools.oc_error_title": "Lỗi xóa", "tools.oc_scan_error": "Lỗi quét: {e}", } # Guard: both dicts must have identical key sets assert set(_EN.keys()) == set(_VI.keys()), ( "EN/VI key mismatch: " + str(set(_EN.keys()) ^ set(_VI.keys())) ) _STRINGS: dict[str, dict[str, str]] = {"en": _EN, "vi": _VI} def set_language(lang: str) -> None: """Set the active language. Unknown codes fall back to English.""" global _LANG _LANG = lang if lang in _STRINGS else "en" def get_language() -> str: """Return the currently active language code.""" return _LANG def t(key: str, **kwargs: object) -> str: """Look up *key* in the active language, falling back to English then the key itself. Dynamic placeholders use str.format_map with keyword arguments:: t("dashboard.sel_count", n_sel=2, n_total=5) # dict entry: "{n_sel} of {n_total} selected" """ text = _STRINGS[_LANG].get(key) or _STRINGS["en"].get(key, key) if kwargs: try: return text.format_map(kwargs) except (KeyError, IndexError): return text return text