Skip to content

Add Vietnamese language into selectable lang in UI (updated)#4241

Open
thanhtantran wants to merge 6 commits into
nesquena:masterfrom
thanhtantran:master
Open

Add Vietnamese language into selectable lang in UI (updated)#4241
thanhtantran wants to merge 6 commits into
nesquena:masterfrom
thanhtantran:master

Conversation

@thanhtantran

Copy link
Copy Markdown

Add Vietnamese language into selectable lang in UI (updated)

@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a full Vietnamese (vi) locale to static/i18n.js, covering ~1,300 translation keys across all UI areas (offline messages, voice mode, session management, onboarding, settings, MCP, cron jobs, kanban, and more). The previously reported issues (duplicate session keys with differing translations and full-width CJK punctuation in error-prefix strings) have been addressed in this updated version.

  • Adds the complete vi locale object with translations for every key present in the English fallback locale.
  • One duplicate untitled key remains (lines 18849 and 19052); both carry identical values so there is no visible runtime difference, but the redundant entry should be removed.
  • No CHANGELOG.md entry was included for this user-visible language addition, as required by AGENTS.md.

Confidence Score: 5/5

Safe to merge — the change is purely additive, touching only the translation file with no logic modifications.

The PR adds a new locale object without modifying any existing keys or runtime logic. The one duplicate key found carries an identical value in both occurrences, so there is no observable translation regression. The two blocking issues from the previous review round (differing duplicate session-management translations and full-width CJK punctuation in error strings) have been corrected.

static/i18n.js — the duplicate untitled key at lines 18849 and 19052 should be cleaned up before the next edit to that key.

Important Files Changed

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]
Loading
%%{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]
Loading

Reviews (4): Last reviewed commit: "Merge branch 'nesquena:master' into mast..." | Re-trigger Greptile

Comment thread static/i18n.js
Comment on lines +18330 to +18362
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.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Comment thread static/i18n.js Outdated
Comment on lines +19173 to +19213
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',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Comment thread static/i18n.js Outdated
Comment on lines +17869 to +17894
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…',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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!

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

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 en but not vi (they'll otherwise fall back to English):

discard, clear, create, remove, settings_heading_title, settings_heading_subtitle,
settings_section_conversation_title, settings_section_appearance_title, settings_section_appearance_meta,
settings_section_preferences_title, settings_section_preferences_meta, settings_section_system_title,
settings_section_system_meta, settings_check_now, settings_checking, settings_up_to_date,
settings_updates_available, settings_updates_disabled, settings_update_check_failed,
settings_label_workspace_panel_open, settings_desc_workspace_panel_open,
settings_label_workspace_todos_tab, settings_desc_workspace_todos_tab,
settings_label_session_jump_buttons, settings_desc_session_jump_buttons,
settings_label_terminal_auto_expand, settings_desc_terminal_auto_expand

(These are the searchable-settings section + a few generic action labels. Diffing the vi key set against en will surface any remaining drift.)

Two housekeeping notes:

Once the 27 keys are filled in, this is in good shape structurally. Leaving #4241 open for your update.

@nesquena-hermes nesquena-hermes added the changes-requested Maintainer left detailed feedback requesting changes; PR is waiting on author to address label Jun 15, 2026
thanhtantran and others added 2 commits June 16, 2026 14:07
…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
@thanhtantran

Copy link
Copy Markdown
Author

Hi @nesquena-hermes,

I have updated the translation, please check

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Thanks for the update @thanhtantran. Pulling the branch and diffing the vi block (static/i18n.js:18690-19988) against the current en block (static/i18n.js:7-1491) on this PR's HEAD:

So you were translating against a moving target. One more sync pass against current en should close it.

Missing keys by area (174 total)

75  settings_*        32  session_*       15  checkpoint_*
13  composer_*         9  excalidraw_*     5  yolo_*
 5  csv_*              4  media_*          4  insights_*
 3  outline_*          3  approval_*       + clear, create,
                                             remove, discard,
                                             cmd_yolo, model_show_all_models

Two gotchas when you add these

A handful of en values are not plain strings — copy the value shape exactly or the locale will break at runtime:

  • Placeholder style (static/i18n.js:526,531):
session_selected_count: '{0} selected',
session_batch_archive_confirm: 'Archive {0} conversations?',
  • Arrow-function style (static/i18n.js:1474,1478):
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 (arg) => \...${arg}...`` signature and just translate the surrounding text.

How to self-check before pushing

Diff the vi key set against en again after you fill these in — when the two key sets match (0 missing, 0 extra) it's structurally complete.

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.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Update — vi is structurally clean ✅ but still missing 201 keys (the gap grew as master added features)

@thanhtantran — re-ran the parity check on the current head against today's en block. Same story as my last pass, just refreshed numbers:

  • Structure is correctvi is a clean strict subset of en (0 extraneous/typo'd keys), good indentation, full-width chars fixed. The shape matches the other locales exactly, which is what we need.
  • ⚠️ 201 keys are still untranslated (vi has 1272, en has 1467) — these would silently fall back to English in the Vietnamese UI. The count went up from my earlier 174 because master keeps landing new English keys (checkpoints, composer mobile/queue/steer controls, approval flow, CSV export, etc.) while this branch sits — you're chasing a moving target, which isn't your fault.

This is the one thing blocking merge: a new selectable language should be reasonably complete, not 14% English-fallback. The CI locale-parity tests (test_quota_chip…, test_endless_scroll_i18n…, test_verdigris/zeus_skin…, +15 more) fail for exactly this reason — they assert every locale has the keys a feature introduced.

To get it over the line

  1. Rebase onto current master first (you're ~51 behind — translate against the current en, not a stale snapshot, so you don't immediately fall behind again).
  2. Add Vietnamese translations for the 201 missing keys. I've generated the exact list — the missing keys are everything in en (static/i18n.js) not present in your vi block; the big clusters are: checkpoint_* (~12), composer_* (mobile/queue/steer/send/stop/interrupt), approval_*, csv_*, cmd_yolo/commands, and assorted settings/preferences labels. Happy to paste the full 201-key list here if useful.
  3. Re-push — the locale-parity CI tests will go green once vi reaches key parity, and I'll run the full gate + ship it. Vietnamese is a welcome addition; it just needs to be complete.

No rush — take the time to do the translations well (machine-translating 201 UI strings tends to read poorly; native/careful translation is worth more than speed).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changes-requested Maintainer left detailed feedback requesting changes; PR is waiting on author to address

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants