fix(i18n): đổi ngôn ngữ áp lại toàn bộ chữ trên màn hình

Trước đây rất nhiều widget gọi setText(tr(...)) một lần lúc dựng, nên sau khi
đổi ngôn ngữ một nửa màn hình vẫn giữ tiếng cũ. Thêm họ bind_text/bind_tip/
bind_placeholder/bind_items/bind_dynamic trong i18n: khoá dịch được gắn thẳng
vào widget (giữ tham chiếu yếu) và tự áp lại mỗi lần set_language.

- co4e sidebar, node property panel, run control, routing toggle, nav rail,
  top bar, filter scaffold, usage chart: chuyển sang binding hoặc tự đăng ký
  on_language_changed thay vì trông chờ nơi nhúng.
- RoutingToggle/AutoRunToggle tự đăng ký retranslate và dùng
  AdjustToContents để bản dịch dài không bị cắt.
- BusyOverlay mới: che cửa sổ trong lúc set_language chạy đồng bộ trên GUI
  thread, tránh cảm giác treo app.
- co4e sidebar chỉ đọc thư viện skill một lần (_skill_prefix_lookup) thay vì
  quét đĩa cho từng skill — trước đó mỗi lần reload tốn ~3.8s đứng GUI.
- Bổ sung bản dịch ja/vi còn để nguyên tiếng Anh; sửa chiều cao hàng
  Settings ở nav rail.
- Thêm 4 bộ test UI cho các thay đổi trên.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-10 01:35:43 +09:00
committed by thanhnv
co-authored by Claude Opus 5
parent 58a2a4507d
commit 9459dbe197
31 changed files with 1625 additions and 131 deletions
@@ -121,6 +121,13 @@ class UsageChartWidget(QWidget):
def retranslate(self) -> None:
"""Áp lại chữ theo ngôn ngữ đang chọn cho nhãn và tooltip."""
# setItemText, chứ không clear()+addItem(): cột data của hai combo này
# là thứ quyết định kỳ và chỉ số đang xem, dựng lại danh sách sẽ reset cả
# hai. Khoá dịch suy ra từ chính cột data nên không phải chép lại danh
# sách giá trị ở hai nơi.
for combo, prefix in ((self.gran_combo, "gran"), (self.metric_combo, "metric")):
for i in range(combo.count()):
combo.setItemText(i, tr(f"dashboard.{prefix}_{combo.itemData(i)}"))
self.currency_lbl.setText(tr("monitoring.overview_currency"))
self.currency_combo.setToolTip(tr("dashboard.currency_tooltip"))
self._chart_title.setText(tr("dashboard.chart_title"))