feat(i18n): carry the download page's language into the app - #124
Merged
Conversation
Summo picks its interface language from the operating system, and that is the right signal almost always — somebody on a Vietnamese Mac reading the English page still wants a Vietnamese app. It is wrong for exactly one person: whoever reads this page's language on a machine set to another one. The only evidence of them is the page they were standing on. Nothing about a downloaded file survives installation in a form the app can read. macOS records the origin URL on the .dmg and drops it when the app is dragged out; Windows keeps it on the .msi rather than on what it installs; and both language pages hand out the same binary from the same GitHub release. So the handoff is an explicit link instead: the download page offers `summo://lang/vi` (or `/en`) below the button, and the shell turns it into the language the interface opens in. `language_from` treats the URL as what it is — an input surface the OS hands this process from anywhere, including a page nobody here wrote. It answers `lang/<code>` and nothing else, checks the code is a short language tag, and is unit-tested against traversals, script, whitespace, invented verbs and a 64-character code. single-instance ships with it because the two are one feature: without it, clicking the link while Summo is running starts a second shell, with a second window and two processes racing for the same vault. Both arrival states are handled — `on_open_url` for a running app, `get_current` for the cold start where the link is what launched it, which is the case that would otherwise work only on the second click. The offer never overrides a choice. A saved locale means the user picked one, in settings or in setup, and a web page does not get to undo that — otherwise revisiting the download page silently re-languages an app somebody has used for a month. It applies only when nothing is saved, and an unshipped code is ignored rather than fallen back on, since falling back would put English on a first run whose OS locale had chosen better. Both halves were untested. `languages.mjs` set `summo.locale` before every load, so it proved each language renders and never touched the state every install passes through exactly once: a first run with nothing saved. It now covers five system locales including one Summo ships no catalogue for, and asserts detection does not *write* a choice — if it did, the handoff and a later change of system language would both be outranked by a preference nobody set.
Merged
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.
Asked for: download from the Vietnamese page → the app opens in Vietnamese, and the same for every other language.
Why it needs a link rather than something automatic
The app already picks its language from the OS, and that's the better signal almost always — somebody on a Vietnamese Mac reading the English page still wants a Vietnamese app. It's wrong for exactly one person: whoever reads this page's language on a machine set to a different one.
There is no automatic channel for that. I checked:
kMDItemWhereFroms) on the.dmg; draggingSummo.appout doesn't carry it.Zone.Identifieron the.msi, not on what it installs.localStorage, not~/.summo— deliberately, peri18n/context.tsx: it's per-machine and must be readable before the daemon handshake. So there's no settings file an installer could seed either.Hence an explicit handoff: the download page offers
summo://lang/vi(or/en) below the button, where it's meaningful because something has just been installed.Safety
language_fromtreats the URL as what it is — an input surface the OS hands this process from anywhere, including a page nobody here wrote. It answerslang/<code>and nothing else, and unit tests cover traversals, script, whitespace,summo://open/~/.summo/vault, and a 64-character code.single-instanceships with it because the two are one feature: without it, clicking the link while Summo runs starts a second shell — second window, two processes racing for the same vault. Both arrival states are handled:on_open_urlfor a running app,get_currentfor the cold start, which otherwise works only on the second click.Three rules, and the last two matter most
ko, which we don't shipThe gap this exposed
Both halves were untested.
languages.mjswrotesummo.localebefore every load, so it proved each language renders and never touched the state every install passes through exactly once: a first run with nothing saved. Nobody developing this ever sees it again after their first launch.It now covers five system locales — including
ko-KR, which has no catalogue and must land on English — and asserts detection does not write a choice. If it did, the handoff and any later change of system language would both be outranked by a preference nobody set.Not changed
Setup step 1 already asks for the interface language, first, pre-filled from the OS. It didn't need work, so I left it alone.
Verification
cargo fmt,clippy -D warnings,cargo test(5 pass) inapps/desktop/src-tauri— note that crate is a separate workspace, so the rootcargo fmt --alldoes not cover it and reported clean while it was not. Plustsc,lint,prettier --check, 441 unit tests, and the fulllanguages.mjssuite against a rebuilt binary.🤖 Generated with Claude Code