feat: add Vietnamese localization to GUI
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
This commit is contained in:
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
|
||||
import customtkinter as ctk
|
||||
|
||||
from gui._constants import COLOR_ERROR
|
||||
from gui.locales import t
|
||||
from gui.views.base import BaseView
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -27,16 +28,19 @@ class LogsView(BaseView):
|
||||
# ── Header ────────────────────────────────────────────────────────────
|
||||
hdr = ctk.CTkFrame(self, fg_color="transparent")
|
||||
hdr.grid(row=0, column=0, sticky="ew", padx=24, pady=(20, 8))
|
||||
ctk.CTkLabel(hdr, text="Logs",
|
||||
font=ctk.CTkFont(size=22, weight="bold")).pack(side="left")
|
||||
self._title_lbl = ctk.CTkLabel(hdr, text=t("logs.title"),
|
||||
font=ctk.CTkFont(size=22, weight="bold"))
|
||||
self._title_lbl.pack(side="left")
|
||||
|
||||
btn_row = ctk.CTkFrame(hdr, fg_color="transparent")
|
||||
btn_row.pack(side="right")
|
||||
ctk.CTkButton(btn_row, text="Copy", width=72,
|
||||
command=self._copy).pack(side="left", padx=4)
|
||||
ctk.CTkButton(btn_row, text="Clear", width=72,
|
||||
fg_color=COLOR_ERROR, hover_color="#c62828",
|
||||
command=self._clear).pack(side="left")
|
||||
self._copy_btn = ctk.CTkButton(btn_row, text=t("logs.copy_btn"), width=72,
|
||||
command=self._copy)
|
||||
self._copy_btn.pack(side="left", padx=4)
|
||||
self._clear_btn = ctk.CTkButton(btn_row, text=t("logs.clear_btn"), width=72,
|
||||
fg_color=COLOR_ERROR, hover_color="#c62828",
|
||||
command=self._clear)
|
||||
self._clear_btn.pack(side="left")
|
||||
|
||||
# ── Log textbox (persistent) ──────────────────────────────────────────
|
||||
self._log_box = ctk.CTkTextbox(
|
||||
@@ -44,6 +48,12 @@ class LogsView(BaseView):
|
||||
font=ctk.CTkFont(family="Consolas", size=12))
|
||||
self._log_box.grid(row=1, column=0, sticky="nsew", padx=24, pady=(0, 12))
|
||||
|
||||
def refresh(self) -> None:
|
||||
# Retranslate header widgets (log content intentionally preserved)
|
||||
self._title_lbl.configure(text=t("logs.title"))
|
||||
self._copy_btn.configure(text=t("logs.copy_btn"))
|
||||
self._clear_btn.configure(text=t("logs.clear_btn"))
|
||||
|
||||
def append(self, text: str) -> None:
|
||||
"""Thread-safe-ish: called from the app's after() poll loop (main thread)."""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user