Repository navigation
feat(app): translate the desktop menu - #322
Merged
Merged
Conversation
The app menu was the last English-only surface in a translated UI: its labels were literals in DESKTOP_MENU, which is shared by the macOS native menu bar (built in the desktop main process) and the Windows/Linux titlebar menu (built in the renderer). Labels are now dictionary keys, typed against the English dict so a missing key fails typecheck. windows-app-menu.tsx resolves them through useLanguage().t. The macOS bar resolves them in main/menu-i18n.ts, which dynamic-imports the selected locale's dict and reads the locale from the same codev.global.dat record the renderer's i18n reads — so Windows and Linux, which never build a native menu, load no dictionary at all. createMenu is async as a result, and ipc.ts's store-set handler calls refreshMenuLanguage() so a language change rebuilds the bar rather than leaving it a launch behind. 45 menu.* keys added to en/es/vi, following .codev/glossary/vi.md — the Vietnamese keeps session, terminal, webview and developer tools in English. A parity test asserts every labelKey resolves in all three locales, and pickMenuLocale is unit-tested against the record shape the app actually persists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Closes #
Type of change
What does this PR do?
The app menu stayed English no matter what language the rest of the UI was in — File, Edit, View, Go, Window, Help and every item inside them. Reported with a Vietnamese app showing an English menu next to a Vietnamese sidebar.
The cause:
DESKTOP_MENUinpackages/app/src/desktop-menu.tscarried literal English labels, and it is read by two renderers — the macOS native menu bar, built in the desktop main process, and the Windows/Linux titlebar menu, built in the renderer. The renderer half could have translated all along; the main-process half has no i18n at all, which is presumably why neither did.Labels are now dictionary keys.
label→labelKey, typed askeyof typeof dictagainst the English dict, so a key that does not exist fails typecheck and a new item cannot be added with a literal.windows-app-menu.tsxresolves them throughuseLanguage().t.packages/desktop/src/main/menu-i18n.ts, which dynamic-imports only the selected locale's dict (@opencode-ai/app/i18n/*, a new export mirroring how@opencode-ai/uialready exports its own).createMenureturns before that on Windows and Linux, so those builds load no dictionary at all — confirmed in the build output, where en/es/vi land in separate ~70 KB lazy chunks.codev.global.datlanguagerecord the renderer's own i18n reads, so the bar and the UI cannot disagree. Parsing lives in the electron-freemain/menu-locale.tsso it is unit-testable.menu.tsremembers its lastDepssorefreshMenuLanguage()can rebuild the bar;ipc.ts'sstore-sethandler calls it when that record is written, so switching language updates the menu instead of leaving it a launch behind.45
menu.*keys added to en/es/vi. The Vietnamese follows.codev/glossary/vi.md: sentence casing, and developer terms stay English —Tải session logs lên,Bật/tắt terminal,Bật/tắt developer tools,Tải lại webview,Session mới.DIVERGENCES.mdgets a row for the localized menu;CHANGELOG.mdgets an entry under Language.How did you verify your code works?
desktop-menu.test.tsasserts everylabelKeyin the table resolves in all three locales — deliberately the tripwire for an upstream menu item arriving with a literal label. Checked that it actually fails by deleting a key.menu-locale.test.tscoverspickMenuLocaleagainst the record shape the app really persists (verified against a livecodev.global.dat:"language": "{\"locale\":\"vi\"…"), a bare locale string, a parsed object, theapp.getLocale()fallback, and a locale with no dictionary.bun typecheckclean inpackages/appandpackages/desktop;bun testgreen (706 app, 159 desktop).electron-vite buildclean, with the dicts code-split out of the main entry as intended.Not verified visually: I have no screenshot tooling on this box to open the menu and photograph it, and the macOS bar I cannot run at all. The renderer path is the same
language.tevery other label in the app uses, and the parity test covers the keys — but a glance at the menu on macOS before merge would be worth it.Screenshots / recordings
None — see the note above.
Checklist
🤖 Generated with Claude Code