Add Vietnamese language into selectable lang in UI (updated)#4241
Add Vietnamese language into selectable lang in UI (updated)#4241thanhtantran wants to merge 6 commits into
Conversation
|
| Filename | Overview |
|---|---|
| static/i18n.js | Adds the Vietnamese (vi) locale with ~1,300 keys. One duplicate untitled key (lines 18849 and 19052) exists with identical values; the previous duplicate-key issues (differing translations, full-width punctuation) from the prior review round have been resolved. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[User selects language in Settings] --> B{Chosen locale}
B -->|vi - new in this PR| C[loadLocale vi]
B -->|en or other| D[loadLocale lang]
C --> E[LOCALES.vi - 1300 keys]
D --> F[Existing locale object]
E --> G[_locale set to vi]
F --> G
G --> H[t key returns Vietnamese string]
H --> I[UI renders in Vietnamese]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[User selects language in Settings] --> B{Chosen locale}
B -->|vi - new in this PR| C[loadLocale vi]
B -->|en or other| D[loadLocale lang]
C --> E[LOCALES.vi - 1300 keys]
D --> F[Existing locale object]
E --> G[_locale set to vi]
F --> G
G --> H[t key returns Vietnamese string]
H --> I[UI renders in Vietnamese]
Reviews (4): Last reviewed commit: "Merge branch 'nesquena:master' into mast..." | Re-trigger Greptile
| session_pin: 'Ghim cuộc trò chuyện', | ||
| session_unpin: 'Bỏ ghim cuộc trò chuyện', | ||
| session_pin_desc: 'Giữ cuộc trò chuyện này ở đầu danh sách', | ||
| session_unpin_desc: 'Bỏ khỏi danh sách ghim', | ||
| session_pin_failed: 'Ghim thất bại: ', | ||
| session_move_project: 'Chuyển sang project', | ||
| session_move_project_desc_has: 'Đổi project cho cuộc trò chuyện này', | ||
| session_move_project_desc_none: 'Gán project cho cuộc trò chuyện này', | ||
| session_archive: 'Lưu trữ cuộc trò chuyện', | ||
| session_hide_external: 'Ẩn khỏi danh sách', | ||
| session_restore: 'Khôi phục cuộc trò chuyện', | ||
| session_archive_desc: 'Ẩn cuộc trò chuyện này cho tới khi hiện archived', | ||
| session_archive_worktree_desc: 'Ẩn cuộc trò chuyện này; giữ worktree trên đĩa', | ||
| session_hide_external_desc: 'Ẩn phiên import này khỏi WebUI mà không xóa lịch sử nguồn.', | ||
| session_restore_desc: 'Đưa cuộc trò chuyện này trở lại danh sách chính', | ||
| session_archived: 'Đã lưu trữ phiên', | ||
| session_archived_worktree: 'Đã lưu trữ phiên. Worktree vẫn còn trên đĩa.', | ||
| session_hidden: 'Đã ẩn phiên khỏi danh sách', | ||
| session_restored: 'Đã khôi phục phiên', | ||
| session_archive_failed: 'Lưu trữ thất bại: ', | ||
| session_duplicate: 'Nhân bản cuộc trò chuyện', | ||
| session_duplicate_desc: 'Tạo bản sao với cùng workspace và model', | ||
| session_duplicated: 'Đã nhân bản phiên', | ||
| session_duplicate_failed: 'Nhân bản thất bại: ', | ||
| session_stop_response: 'Dừng phản hồi', | ||
| session_stop_response_desc: 'Hủy phản hồi đang chạy cho cuộc trò chuyện này', | ||
| session_delete: 'Xóa cuộc trò chuyện', | ||
| session_delete_desc: 'Xóa vĩnh viễn cuộc trò chuyện này', | ||
| session_delete_confirm: 'Xóa cuộc trò chuyện này?', | ||
| session_delete_worktree_desc: 'Chỉ xóa cuộc trò chuyện WebUI; giữ worktree trên đĩa', | ||
| session_delete_worktree_confirm: (path) => `Xóa cuộc trò chuyện này? Worktree tại ${path} sẽ vẫn còn trên đĩa.`, | ||
| session_deleted: 'Đã xóa cuộc trò chuyện', | ||
| session_deleted_worktree: 'Đã xóa cuộc trò chuyện. Worktree vẫn còn trên đĩa.', |
There was a problem hiding this comment.
Duplicate session keys silently discard "cuộc trò chuyện" translations
This entire block (lines 18330–18362) defines keys that are redefined again starting at line 19168 under the comment // Session management and settings keys (en fallback — pending translation). In a JavaScript object literal, duplicate keys cause the later value to silently overwrite the earlier one, so every translation here that uses "cuộc trò chuyện" — including session_pin, session_archive, session_restore, session_delete, session_duplicate, session_move_project_desc_has/none, and session_stop_response_desc — will never be seen at runtime. The block at line 19168 wins and uses "phiên" throughout. Either remove the first block or the second, keeping only one consistent set.
| session_archive_failed: 'Lưu trữ thất bại:', | ||
| session_archived: 'Đã lưu trữ phiên', | ||
| session_archived_worktree: 'Đã lưu trữ phiên. Worktree vẫn còn trên đĩa.', | ||
| session_hidden: 'Đã ẩn phiên khỏi danh sách', | ||
| session_delete: 'Xóa phiên', | ||
| session_delete_desc: 'Xóa vĩnh viễn phiên này', | ||
| session_delete_confirm: 'Xóa phiên này?', | ||
| session_delete_worktree_desc: 'Chỉ xóa phiên WebUI; giữ worktree trên đĩa', | ||
| session_delete_worktree_confirm: (path) => `Xóa phiên này? Worktree tại ${path} sẽ vẫn còn trên đĩa。`, | ||
| session_deleted: 'Đã xóa phiên', | ||
| session_deleted_worktree: 'Đã xóa phiên. Worktree vẫn còn trên đĩa.', | ||
| session_worktree_remove: 'Xóa worktree', | ||
| session_worktree_remove_desc: (path) => `Xóa git worktree tại ${path}`, | ||
| session_worktree_remove_confirm: (path) => `Xác nhận xóa git worktree khỏi đĩa?\n\nĐường dẫn:${path}\n\nToàn bộ thư mục worktree sẽ bị xóa, nhưng dữ liệu phiên trong WebUI vẫn được giữ lại。`, | ||
| session_worktree_remove_not_exists: (path) => `Worktree tại ${path} không còn tồn tại trên đĩa。`, | ||
| session_worktree_remove_confirm_label: 'Xóa', | ||
| session_worktree_removed: 'Đã xóa worktree。', | ||
| session_worktree_remove_failed: 'Xóa worktree thất bại:', | ||
| session_worktree_remove_status_failed: 'Đọc trạng thái worktree thất bại:', | ||
| session_worktree_remove_locked_by_stream: 'Không thể xóa — có phiên streaming đang hoạt động sử dụng worktree này。', | ||
| session_worktree_remove_locked_by_terminal: 'Không thể xóa — có phiên terminal đang hoạt động sử dụng worktree này。', | ||
| session_worktree_remove_unsafe_blocked: 'Hãy xử lý thay đổi local hoặc commit chưa push trước khi xóa worktree này。', | ||
| session_worktree_remove_dirty_warning: '⚠️ Worktree này có thay đổi chưa commit và sẽ bị xóa vĩnh viễn。', | ||
| session_worktree_remove_untracked_warning: (count) => `${count} file chưa theo dõi sẽ bị xóa vĩnh viễn。`, | ||
| session_worktree_remove_ahead_warning: (ahead) => `${ahead} commit chưa push sẽ bị mất。`, | ||
| session_duplicate: 'Nhân bản phiên', | ||
| session_duplicate_desc: 'Tạo bản sao với cùng workspace và model', | ||
| session_duplicate_failed: 'Nhân bản thất bại:', | ||
| session_stop_response: 'Dừng phản hồi', | ||
| session_stop_response_desc: 'Hủy phản hồi đang chạy của phiên này', | ||
| session_duplicated: 'Đã nhân bản phiên', | ||
| session_move_project: 'Chuyển sang project', | ||
| session_move_project_desc_has: 'Đổi project của phiên này', | ||
| session_move_project_desc_none: 'Gán project cho phiên này', | ||
| session_pin: 'Ghim phiên', | ||
| session_pin_desc: 'Giữ phiên này ở đầu danh sách', | ||
| session_pin_failed: 'Ghim thất bại:', | ||
| session_restore: 'Khôi phục phiên', | ||
| session_restore_desc: 'Khôi phục phiên về danh sách chính', | ||
| session_restored: 'Đã khôi phục phiên', | ||
| session_unpin: 'Bỏ ghim', |
There was a problem hiding this comment.
Full-width colon
: and period 。 in error-prefix and status strings
Several strings in this second session-management block use the Unicode full-width colon : (U+FF1A) and full-width period 。 (U+3002) instead of their ASCII equivalents. Affected keys include session_archive_failed (line 19173), session_worktree_remove_failed (19190), session_worktree_remove_status_failed (19191), session_duplicate_failed (19200), and session_pin_failed (19209). The English pattern for these keys ends with ': ' (ASCII colon + space) because the string is concatenated directly with an error message at runtime — e.g., t('session_archive_failed') + err. Using 'Lưu trữ thất bại:' produces Lưu trữ thất bại:<error text> with no space and an unexpected CJK character. Similarly, session_worktree_removed and several session_worktree_remove_* strings end with 。 rather than ., producing inconsistent sentence-final punctuation.
| vi: { | ||
| offline_title: 'Mất kết nối', | ||
| offline_browser_detail: 'Trình duyệt báo rằng thiết bị này đang ngoại tuyến.', | ||
| offline_network_detail: 'Không thể kết nối tới Hermes từ trình duyệt lúc này.', | ||
| offline_autorefresh: 'Trang sẽ tự động làm mới khi Hermes khả dụng trở lại.', | ||
| offline_check_now: 'Kiểm tra ngay', | ||
| offline_checking: 'Đang kiểm tra…', | ||
| offline_stream_waiting: 'Mất kết nối. Đang chờ làm mới…', | ||
| _lang: 'vi', | ||
| _label: 'Tiếng Việt', | ||
| _speech: 'vi-VN', | ||
| // boot.js | ||
| cancelling: 'Đang hủy…', | ||
| cancel_failed: 'Hủy thất bại: ', | ||
| mic_denied: 'Quyền truy cập microphone bị từ chối. Kiểm tra quyền trình duyệt.', | ||
| mic_no_speech: 'Không phát hiện giọng nói. Hãy thử lại.', | ||
| mic_network: 'Nhận diện giọng nói không khả dụng.', | ||
| mic_error: 'Lỗi nhập giọng nói: ', | ||
| // Voice | ||
| voice_dictate: 'Đọc chính tả', | ||
| voice_dictate_active: 'Dừng đọc chính tả', | ||
| voice_recording_active: 'Dừng ghi âm', | ||
| voice_mode_toggle: 'Chế độ giọng nói', | ||
| voice_mode_toggle_active: 'Thoát chế độ giọng nói', | ||
| // Turn-based voice mode (#1333) | ||
| voice_listening: 'Đang nghe…', |
There was a problem hiding this comment.
Inconsistent indentation in the
vi locale block
The rest of the file (including the English locale at line 7) uses 4-space indentation consistently throughout. The vi block mixes hard tabs, 2-space, 3-space, and 4-space indentation across different sections. Some sections (e.g., offline_title at line 17870) use hard tabs, while others (e.g., the pdf_loading group) use 4 spaces, and keys around slash_skill_badge and cmd_background use 2 or 3 spaces. This is a style-only issue and won't affect runtime, but it makes the block harder to maintain and review.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Thanks for the update @thanhtantran — this revision (now in #4241) is closer: down from 50 missing keys to 27. To finish it, please add Vietnamese translations for the remaining keys that exist in (These are the searchable-settings section + a few generic action labels. Diffing the Two housekeeping notes:
Once the 27 keys are filled in, this is in good shape structurally. Leaving #4241 open for your update. |
…entation - Remove duplicate 'Session management and settings keys' section (~123 lines) - Replace full-width Unicode : (U+FF1A) with ASCII : - Replace full-width Unicode 。 (U+3002) with ASCII . - Normalize indentation: tabs -> 4 spaces, fix 2/3-space indents Fixes issues reported by Greptile bot on PR nesquena#4241
|
Hi @nesquena-hermes, I have updated the translation, please check |
|
Thanks for the update @thanhtantran. Pulling the branch and diffing the
So you were translating against a moving target. One more sync pass against current Missing keys by area (174 total)Two gotchas when you add theseA handful of
session_selected_count: '{0} selected',
session_batch_archive_confirm: 'Archive {0} conversations?',
checkpoint_restore_confirm_message: (ckpt) => `Restore workspace to checkpoint "${ckpt}"? ...`,
checkpoint_diff_files_changed: (n) => `${n} file${n === 1 ? '' : 's'} changed`,For the function ones, keep the How to self-check before pushingDiff the Same two gates as before still apply: adding a new selectable UI language is a product decision, and the Vietnamese wording itself needs a Vietnamese-speaker / maintainer (@nesquena) sign-off. Leaving #4241 open for the next sync pass. |
Update —
|
Add Vietnamese language into selectable lang in UI (updated)