Skip to content

feat(local): translate the desktop app into Japanese and German, open without a broken chat model, and add a dev app identity - #1911

Merged
AnishSarkar22 merged 39 commits into
MODSetter:devfrom
AnishSarkar22:feat/translations
Sep 24, 2026
Merged

AnishSarkar22 merged 39 commits into
MODSetter:devfrom
AnishSarkar22:feat/translations

Conversation

@AnishSarkar22

@AnishSarkar22 AnishSarkar22 commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

What

  • Adds interface localization to the desktop app: English, Japanese and German, chosen from the OS languages or under Settings › General › Language, where each language is listed in its own name.
  • Uses FormatJS, the standard library for this: react-intl in the renderer and @formatjs/cli to extract, compile and verify. All three catalogs are precompiled and bundled, so nothing is fetched at run time and the build needs no network, which suits the air-gapped app.
  • English is written inline in each formatMessage call and formatjs extract generates en.json. ja.json and de.json are translated with the new translate skill, which holds the key naming, tone and glossary.
  • Messages are ICU MessageFormat: plurals with each language's categories, and numbers, sizes, percentages, dates and counts formatted per language through ICU skeletons.
  • Electron main owns the language (the saved choice, else the first supported OS language) and hands it to the window before first paint. A change reloads the window.
  • Translates every feature: chat, including its error kinds; settings; license; Studio; models and connections; onboarding; sources; network consent; and shared components.
  • Checks: FormatJS's ESLint rules (English present, placeholders given, id shape), and pre-commit hooks for extraction, formatjs verify and a project check for id prefixes, apostrophes and file layout. A test also checks each language's plural categories.
  • Adds the en-XA pseudo-locale for development only, to spot overflow and text that isn't in a message. Production builds don't contain it.
  • The application menu is now the same in dev and packaged builds and built only from Electron roles, so it follows the OS language. Main no longer depends on @formatjs/intl, and its bundle shrinks from about 349 KB to about 35 KB.
  • Fixes a startup dead end. A saved key the keychain can no longer decrypt, an unreachable provider, or egress turned off used to stop the app at "SurfSense could not start". The app now opens, and a notice on the composer names the reason and links to Settings. The backend's model listing returns a code for each failure.
  • Egress off keeps the selected model and holds the composer. The notice's Allow… opens the existing consent dialog, so sending a message never raises it.
  • Whether the saved chat model can be used is now decided in one place, shared by startup and the dashboard. A model that can't be used stays selected and is checked again when the model changes, when Settings closes and after Allow…, so fixing the cause, such as re-entering a key, clears the notice without a reload. Also fixes chat rows that screen readers read as the title run into the time, a dashboard crash on an unknown license state, a fast-refresh lint error, and two backend test files that pytest -m unit skipped.
  • Development runs as "SurfSense Dev" with a DEV app icon and its own keychain item, so it can't read or replace an installed SurfSense's key. Packaged and dev icons are kept apart in build/icons/.
  • Smaller fixes: the window is titled "SurfSense", the startup error screen matches the loader background, the model sections are labeled Text gen and Image gen, and Retry sits beside a failed model list.
  • Docs: architecture/localization.md, ADR 0029 (ICU catalogs) and ADR 0030 (FormatJS), plus updates to connections.md and packaging.md.

Why

Fixes #

How to test

@vercel

vercel Bot commented Sep 24, 2026

Copy link
Copy Markdown

@AnishSarkar22 is attempting to deploy a commit to the Rohan Verma's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 63f31869-66b1-4ba8-a69e-33a2b5854fd9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…y why

A saved key the keychain can no longer decrypt, an unreachable provider or
egress turned off used to stop startup at "SurfSense could not start", with
no way to reach Settings. Startup now opens without that model and a notice
attached to the composer names the reason and links to Settings.

- backend: drop ProviderConnection.api_key's dead InvalidToken catch so an
  unreadable key raises UnreadableSecretError (409 unreadable_secret), as
  documented; mark test_secrets as unit so it runs
- backend: code a failed connection model listing by what the endpoint did
  (provider_auth, provider_rate_limited, provider_unreachable, provider_error)
- chat: egress off keeps the model selected and holds the composer; the notice's
  Allow… opens the egress consent dialog, so a send never raises it
- chat: the notice follows the selected model (connection and name) and is
  checked again on model change, on closing Settings and after Allow
- chat: the notice sits behind the composer's top edge without moving it, and
  the scroll-to-bottom arrow rises clear of it
- models: show Retry beside a failed model list, not below it
- electron: run development as "SurfSense Dev" so its keychain item never
  touches the installed app's key
- i18n: new notice, placeholder and button strings in ja and de
- docs: connections.md describes the codes, the notice and the egress flow
@AnishSarkar22 AnishSarkar22 changed the title Feat/translations feat(local): translate the desktop app into Japanese and German, open without a broken chat model, and add a dev app identity Sep 24, 2026
…e once the cause is fixed

The saved chat model was checked twice, by startup and by the dashboard, with
different rules. Startup cleared the model to say it couldn't be used, which
left the dashboard nothing to check again, so a notice stayed until a reload
even after the key was re-entered in Settings.

- availability.ts: one check, used by startup and the dashboard, answering
  available, gone, needs-consent or unusable
- startup keeps the saved model whatever its status and passes that status on,
  so the dashboard doesn't check the same model twice
- the dashboard checks again when the model changes, when Settings closes and
  after Allow, so fixing the cause clears the notice without a reload
- the notice is derived from the status; the composer is held whenever a send
  couldn't go anywhere
- chat rows are named by their title, with the time read as a description,
  instead of "Q3 rollup2 weeks ago"
- an unknown license state shows as no license instead of crashing the
  dashboard
- the citation context moves to its own file so fast refresh keeps working
- the podcast brief and upload validation tests are marked unit so they run
- tests: recovery after Settings closes, the egress notice following the
  selected model's host, launch keeping the saved model; stale expectations
  for the source count, message failures and the new-chat placeholder updated
- docs: connections.md describes the single check, its statuses and when it
  runs again
@AnishSarkar22
AnishSarkar22 merged commit 6776aa4 into MODSetter:dev Sep 24, 2026
3 of 7 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