Skip to content

The app in English, Portuguese and Spanish, with a language picker - #14

Merged
httpsphl merged 6 commits into
mainfrom
feat/languages
Sep 29, 2026
Merged

httpsphl merged 6 commits into
mainfrom
feat/languages

Conversation

@httpsphl

Copy link
Copy Markdown
Owner

The app now supports multiple languages: English, Português (Brasil) and Español. Stacked on #13, which is itself on #12. Once those merge with merge commits, this diff shrinks to the last commit.

How it works

  • Every string shown to the user lives in app/src/i18n/<language>/<area>.ts (common, shell, onboarding, updates, bots, chat, crews, messages). English is the reference, and its shape is the Messages type, so a string missing from (or extra in) another language fails to compile. A test also checks that no translation is empty. Strings that include values are functions, with plurals written out for each language.
  • Components use useT(). Code outside React uses t() when the text is needed: toasts, connection errors, date formatting.
  • Picker: a language button in the title bar, also shown on the setup screens. It offers "System language: …", English, Português (Brasil) and Español. "System language" follows Windows (pt-PT falls back to pt-BR, anything unsupported to English). The choice switches the UI immediately, is saved in localStorage, and sets <html lang>.
  • Dates, relative times ("in 5 min") and decimals ("4,2 s") follow the chosen language.
  • The installer also ships in the three languages and picks the one matching Windows.
  • Sidebar previews and chat notices are worded by the app from the kind/code added in Sign in to Claude from the app #13. User content stays as it arrives: names, messages, replies, tool output, the tool summaries the service writes, and the service's own error messages.
  • Tone: plain words, no jargon. Portuguese uses "você", Spanish uses "tú", and the glossary is in spec 15.6 (crew = equipe/equipo, role = função/rol…). "Turn" became "The bot couldn't finish this" in all three languages.

Tested

  • pnpm check: 61 tests pass. The new ones cover system-language detection, that every language has every string, switching languages (immediate and remembered), and dates in the chosen language. Existing English tests pass unchanged.
  • Browser preview: in Portuguese, the system language was picked automatically (Windows is in Portuguese), and the sidebar, crew page, chat, dates and composer all read naturally. Switching to Spanish updates everything immediately, the choice survives a reload, and durations use a decimal comma.

Not translated (known)

  • Error messages the service writes in English, for example form validation like "@scout is already used in this crew". They're shown as they arrive.
  • The few English words in tool summaries, such as "load send_message" and "Updated the plan".

The app checks the latest published GitHub release (latest.json) when it
opens and every six hours. A newer version shows "Update available" in
the title bar; one click downloads it with progress, runs the installer
passively and reopens Botloft, and the reopened app updates the daemon
because it is now outdated. Failures keep the current version, with the
error under "Details". Dev builds never offer to replace themselves.

Updates are signed with the Tauri updater key (public key in
tauri.conf.json, private key in the TAURI_SIGNING_PRIVATE_KEY secret),
and requireSignedVersion keeps a tampered feed from pushing an older
signed release back.

Pushing a tag vX.Y.Z runs release.yml: it checks the tag against the
workspace version, builds the signed installer and opens a draft release
with the installer, its signature and latest.json. The updater sees it
once the owner publishes it. The version now lives only in Cargo.toml.
… for the app

The daemon runs `claude auth status` (documented: JSON with loggedIn,
exit 0 when signed in, 1 when not; checked with 2.1.284) once Claude Code
is found, every 30 s while signed out, at once when a bot hits a sign-in
error, and when the app calls the new `system.refresh`. `system.status`
reports `claudeSignedIn` and `claudePath`. When the answer turns from
signed out to signed in, the bots a sign-in error stopped start again by
themselves. An account problem with a valid sign-in (billing, a blocked
organization) does not change the answer, so it cannot loop.

So the app can speak the owner's language, notices carry a code
(signed_out, usage_limit, turn_failed) next to the English text, and the
conversation-list line carries its kind with no wording of its own: the
app adds "You:" and "Waiting for approval".
Both pick a free port; run in parallel, one could take the port the other
had just freed and read it as taken.
"Sign in to Claude" opens Claude Code's own sign-in (`claude auth login`)
in a window of its own, launched by the app so it comes to the front,
then asks the daemon to check again and watches for the result. It shows
on the welcome screen as a "Claude account" check, as a banner when
signed out, and on a bot stopped by a sign-in error. The Tauri command
only runs an absolute path to an existing claude.exe, and strips the
Claude Code session variables an app started from a session would pass
on.
Every string the owner reads now lives in app/src/i18n/<locale>/, one
file per area. English is the reference: its shape is the Messages type,
so a text missing or extra in another language does not compile, and a
test checks that none is empty. Components read with useT(); code
outside React (toasts, connection errors, formatting) with t().

A language button in the title bar (also on the setup screens) picks
English, Português (Brasil) or Español, or follows Windows (the first
supported of the preferred languages, English otherwise). The choice is
kept in localStorage and sets <html lang>. Dates, relative times and
decimals follow it. The installer also comes in the three languages.

The daemon's notices and conversation-list lines are worded by the app
from their code and kind; names, messages, replies, tool output and the
daemon's own error texts stay as they come. "Turn" gave way to plain
words along the way.
@httpsphl
httpsphl merged commit d5e3e43 into main Sep 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant