Repository navigation
Description
Problem
MonoCode only speaks English. Every string sits inline in the component that renders it, so there is no seam to translate at, and the app cannot follow a language the OS is already set to. For anyone who does not work in English that is the first thing they hit, and it is not something they can work around.
Proposed solution
A small translation layer, then Simplified Chinese for the surfaces people read most.
Design points worth agreeing on before more of the app is covered:
- The table is gettext-style: the English source string is the key.
t()returns the key when a translation is missing, so a partly translated app degrades to English instead of showing placeholders, and coverage can grow one screen at a time with no big-bang migration. - Language defaults to
autoand follows the OS, the way the app already treats the color scheme. It persists to localStorage and fires a change event like the other appearance settings, so someone who never opens Settings still sees their own language. - The macOS menu bar lives outside the webview and cannot read the preference, so the resolved tag is pushed across to Rust, which rebuilds the app menu and the dock menu from its own label table. Windows and Linux draw their menu bar in the webview, so the same command is a no-op there.
- Settings search matches whichever language you type in. A row stays reachable by its English name while the interface is Chinese.
scripts/check-i18n-keys.mjsfails on at()key with no translation, or a duplicate key, so coverage cannot silently regress as the app grows.
I have a branch that does this for the window frame and Settings, and opened a PR against it.
Alternatives considered
- Leave strings inline and maintain a Chinese fork. No merge path back to upstream; every release would need the strings ported again.
- Keep a second set of components per language. Doubles the surface to keep in sync.
- A heavier i18n dependency (i18next, react-intl). The app is deliberately light on dependencies and a table plus one hook covers what is needed here.
Additional context
Covered so far: title bar and tab strip, the in-app menu bar, sidebar, composer, settings navigation, every Settings section, the delete-project dialog, and the macOS native menu.
Not covered, and I would rather land the layer than one enormous diff: the transcript, the Inbox and Notes views, the file tree, other dialogs, and UI added after v0.1.47 — the per-project notification categories being the obvious example.
Activity
king20300 commentedon Oct 1, 2026
@hardbeat920 There are now two open i18n PRs with different designs, and it'd help to know which direction you want before more translation work piles up on either:
auto | en | zh, and it also localizes the macOS native menu through Rusten.ts/zh-CN.tsdictionaries,system | en | zh-CN, with broader coverage in one PRWhichever you pick, I'd like to add Traditional Chinese (
zh-Hant) as well. Right now both map Taiwan, Hong Kong and Macau systems to Simplified. I'm happy to contribute the locale split and a full zh-Hant table reviewed by a native speaker, so it would be good to settle the language-tag shape (zh-Hans/zh-Hantvszh-CN/zh-TW) early, before a stored value ships.cc @gzx19990101 @wangdada8208
jeremy-prt commentedon Oct 7, 2026
+1, as discussed on X. Would love the UI to follow the macOS system language by default, with a setting to override it. French would be great for me.
SeniorHan commentedon Oct 11, 2026
I ran into the same fork-in-the-road here and, rather than add a third design, tried to answer the question with something runnable. Posting what I found; happy to throw it away if you'd rather go a different way.
What I did: took the gettext design from #244 (English source string as the key) and re-implemented it cleanly on current
main, since #244 is now 68 commits behind and conflicting. Scope is deliberately small: the framework plus only the language row, so the design is reviewable without a giant translation diff.Draft PR, not a request to merge: SeniorHan#1
It's on my fork, drafted against my own
main. All 12 CI jobs pass, includingcheck (macos-latest).One deliberate difference from #244: the tag is
zh-Hans/zh-Hant, not a barezh. #244'sLang::from_tagmaps everyzh*to Simplified, so a Traditional reader gets Simplified — the bug @king20300 reported. Since a stored value already encodes which script, better to fix the tag now than re-tag later. French (worth noting @jeremy-prt's +1) would then befrwith no new machinery.What CI caught that I couldn't see on Linux: porting the menu code dropped a
#[tauri::command]attribute from a macOS-gated function. Linux compiled fine — the registration iscfg(target_os = "macos"), so the generated symbol was never referenced — and macOS failed withcannot find __cmd__keybindings_set_overrides in menu. Fixing it was trivial, but it's a reminder the macOS job is the one that matters for menu work. That fix is in the second commit.Status / caveats. Only the language row is translated; everything else stays English on purpose until there's a direction. No
.appwas built or launched — menu code is compile/clippy/test-verified only, not run.The actual question for @hardbeat920 (same as @king20300's): which design do you want, and do you want this at all? If gettext is the shape, I'm happy to keep going one surface at a time; if you'd prefer #530's nested-key approach, that's a better answer today than more translation piled onto either PR.
No action needed from me on this unless you want the branch pushed somewhere else or the design changed.