feat: add Russian localization and independent STT languages
CI / Typecheck, tests, lint (push) Canceled after 0s
CI / Standalone (mac-arm64) (push) Canceled after 0s
CI / Standalone (linux-x64) (push) Canceled after 0s
CI / Standalone (windows-x64) (push) Canceled after 0s

This commit is contained in:
2026-10-25 17:18:44 +03:00
parent b277ee3e0f
commit a50ca4b867
93 changed files with 3261 additions and 904 deletions
+54
View File
@@ -0,0 +1,54 @@
# Language support
LearnHarness supports English and Russian. Settings → General contains independent
interface and speech recognition language selectors. The microphone also has a
speech language selector. Each offers System default, English, and Русский.
System default uses the preferred browser language (the operating system's
preferred language in the desktop app); Russian locales select Russian and other
locales select English. Explicit choices persist in `settings.json` as
`general.language` and `general.sttLanguage`. Removing a field restores automatic
selection. Changing one preference preserves the other general settings.
The interface changes immediately, including accessibility labels and the desktop
menu. New AI teaching, questions, plans, practice, graph descriptions, research,
figures, and demos use the interface language. Existing conversations and imported
source material retain their authored text. A session retains the language of its
latest foreground turn so automatic follow-ups can use the same language after a
restart; an explicit app language preference takes precedence.
Speech recognition uses Deepgram Nova-3 with an explicit `language=en` or
`language=ru`. A recording captures the selected language when it starts and keeps
that language through reconnections. The selector is disabled while recording;
changes elsewhere take effect on the next recording. Audio capture, deferred
transcript insertion, stopping, and cancellation retain their existing behavior.
## Maintaining translations
The pure localization functions live in `src/core/i18n.ts` and are exported through
the core barrel. English and Russian catalogs are in `src/core/locales/en.json` and
`ru.json`. English source phrases are message keys. Add every new app-owned message
to both catalogs, use named interpolation parameters, and use plural entries with
`Intl.PluralRules` for counts. Russian entries may use `one`, `few`, `many`, and
`other` forms. Keep code, provider/model names, identifiers, and user-authored text
out of translation lookups.
React components use `useI18n()` from `components/i18n.tsx`. The root provider gets
server-resolved initial settings and locale to preserve hydration. General-setting
writes share a serialized read-modify-write path; other surfaces use the existing
settings-saved signal. Server and agent code receive an explicit language rather
than depending on a mutable global translation state.
Some existing protocol errors arrive preformatted in English. Localization can
recognize cataloged application templates at the display boundary, preserving the
canonical error values used for classification and any third-party diagnostics.
Raw technical instructions, code, and user overrides remain editable source data.
The desktop shell has its own small catalog in `electron/language.cjs`, packaged
with the shell. Its narrow `lh:language` channel accepts only `en` and `ru` from a
trusted application window. Russian prose uses Noto Sans because Source Sans 3
has no Cyrillic subset. Explicit font choices continue to take precedence.
`tests/i18n.test.tsx` covers preference validation, automatic selection, independent
STT requests, catalog parity and interpolation, Russian plural forms, SSR locale
isolation, translated dialogs and exports, agent instructions, and native labels.