Files
cowork-local/agent/knowledge/i18n_rules.md
T
anhtnm1andClaude Opus 5 7bd2b95a57 docs(agent): thư viện instruction cho việc sửa bug UI/UX
Bộ 7 role chuyên biệt (triage → specialist → implementer → reviewer) cùng
lớp dùng chung: guardrail, tri thức về repo, checklist, và contract đầu ra.

Vì sao có: bug UI/UX được báo bằng lời kể triệu chứng, và người sửa hay bỏ
qua ba thứ mà repo này rất dễ vi phạm — luật "không file nào ngoài theme/
được đặt tên một màu", trần LOC theo bánh cóc, và việc ui/ với presentation/
cùng tồn tại nên sửa nhầm file là "đã fix mà vẫn thấy lỗi".

knowledge/qt_pitfalls.md chép lại 20 nguyên nhân gốc hay gặp của bug PySide6;
examples/bad_fix.md có hai ca CÓ THẬT, gồm ca chính bản vá trong nhánh này
từng mắc (compare_digest trên str ngoài ASCII) và lọt qua vòng review đầu.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 19:55:02 +09:00

3.9 KiB

i18n — luật chuỗi hiển thị

Nguồn: docstring i18n/__init__.py.


1. Ba ngôn ngữ, mặc định tiếng Việt

LANGUAGES = {"en": "English", "ja": "日本語", "vi": "Tiếng Việt"}
LANGUAGE_SHORT = {"en": "EN", "ja": "JP", "vi": "VN"}   # switcher gọn ở top bar
DEFAULT_LANGUAGE = "vi"

tr(key, **kwargs) trả chuỗi theo ngôn ngữ hiện tại, fallback lần lượt: ngôn ngữ hiện tại → en → chính cái key. Nghĩa là thiếu entry thì UI hiện ra workspace.tab_folder chứ không crash — nếu người dùng chụp màn hình có chuỗi dạng a.b_c thì đó chính là triệu chứng thiếu key.

.format(**kwargs) được áp dụng khi có placeholder: tr("composer.attachments", n=3).

2. Widget nào phải đăng ký callback

Loại widget Cách xử lý
Sống lâu — chrome cửa sổ chính, tab, sidebar, composer Đăng ký on_language_changed(cb); cb áp lại tr() cho chính widget đó. Callback chạy ngay một lần và mỗi lần đổi ngôn ngữ
Tạm thời — Settings, Skills, Flow, Permission dialog Dựng lại từ đầu mỗi lần mở, nên chỉ cần gọi tr() lúc construct, không đăng ký

Quy ước đặt tên hàm callback trong repo: _retranslate() / _apply_i18n() — xem ui/workspace_tab.py:484 trở đi làm mẫu chuẩn.

Bug điển hình: "Đổi ngôn ngữ nhưng nhãn X không đổi" → widget sống lâu mà quên đăng ký, hoặc có đăng ký nhưng callback bỏ sót đúng nhãn đó. Không sửa bằng cách gọi tr() lại ở chỗ khác — sửa trong callback.

3. File từ điển

i18n/ chia theo màn hình, không phải một file khổng lồ:

i18n/login_dialog.py      i18n/sidebar.py        i18n/composer.py
i18n/cowork_tab.py        i18n/settings_dialog.py i18n/skills_dialog.py
i18n/libreoffice_view.py  i18n/agents_admin_tab.py i18n/monitoring_overview.py
i18n/hint.py

Mỗi file export dict key -> {"en":..., "ja":..., "vi":...}, được i18n/__init__.py import và gộp lại. Thêm key mới:

  1. Chọn đúng file theo màn hình (không nhét đại vào login_dialog.py chỉ vì nó lớn nhất).
  2. Điền đủ 3 ngôn ngữ. Thiếu ja là lỗi hay gặp nhất và chỉ lộ ra khi khách Nhật dùng.
  3. Đặt key theo <màn>.<thành_phần> — workspace.tab_folder, app.nav.recents.

4. Rủi ro riêng của tiếng Nhật và tiếng Việt

Rủi ro Triệu chứng Cách xử lý
Tiếng Nhật ngắn hơn, tiếng Việt dài hơn tiếng Anh Nút vừa với EN, tràn với VI; label bị ... với JA Không setFixedWidth theo chuỗi tiếng Anh. Dùng sizeHint + minimumWidth, hoặc cho phép wrap
Dấu tiếng Việt bị cắt phần trên/dưới Ắ, ộ mất dấu ở nhãn cao cố định Không đặt setFixedHeight cho label theo pixel; để layout tự tính
Font mặc định thiếu glyph Nhật Ô vuông tofu □□□ trên máy chưa cài font Kiểm tra _FONT trong theme/palettes.py, khai báo fallback
Sắp xếp / so sánh chuỗi Danh sách project sắp sai với tên có dấu Dùng locale-aware sort, không sorted() thô
Chiều dài chuỗi tính bằng ký tự ≠ chiều rộng hiển thị Elide sai với chữ Nhật Đo bằng QFontMetrics.horizontalAdvance, không len()

5. Checklist sửa bug i18n

  • Key mới có đủ en / ja / vi?
  • Đã thử đổi qua cả 3 ngôn ngữ trong lúc app đang chạy (không phải restart)?
  • Widget sống lâu đã đăng ký on_language_changed?
  • Không còn chuỗi hardcode nào trong bản vá?
  • Layout còn đúng với chuỗi dài nhất trong 3 ngôn ngữ?
  • Không dùng len() để đo bề rộng chữ?