Skip to content

fix(web/i18n): persist Desktop language from config.yaml on startup (#26665) - #26680

Closed
xxxigm wants to merge 3 commits into
NousResearch:mainfrom
xxxigm:fix/desktop-language-persist-26665
Closed

xxxigm wants to merge 3 commits into
NousResearch:mainfrom
xxxigm:fix/desktop-language-persist-26665

Conversation

@xxxigm

@xxxigm xxxigm commented May 16, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes #26665. Hermes Desktop ignored display.language from ~/.hermes/config.yaml on startup, so non-English users (e.g. pt-BR) saw the UI in English on every restart even though the value was correctly saved on disk.

The React I18nProvider (web/src/i18n/context.tsx) only seeded the active locale from localStorage. Users who set the language via the CLI setup wizard or by editing config.yaml directly never populated localStorage, so the provider always fell back to English. The same symptom appears in browsers that wipe site data on quit even when the user used the dropdown.

This PR teaches the provider to fall back to display.language from /api/config when localStorage has no entry, mirrors the Python alias map (pt-BR → pt, zh-CN → zh, etc.) on the TypeScript side so the same string resolves identically across Desktop / CLI / gateway, and pins a regression test for the specific Portuguese aliases called out in the bug report. An explicit dropdown choice still wins, so the existing in-browser UX is unchanged. The fix is read-only on the config side — it never mutates config.yaml.

Related Issue

Fixes #26665 — Desktop language setting resets to English on restart despite config.yaml having pt-BR saved.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • web/src/i18n/context.tsx (+74/-4):
    • New LOCALE_ALIASES map covering BCP-47 regional tags and a few endonym/English aliases — mirrors _LANGUAGE_ALIASES in agent/i18n.py so Desktop, CLI, and gateway resolve user input identically.
    • New normalizeLocale(value) helper that accepts the alias map plus a regional-suffix strip (zh-XX → zh) and returns null for unknown input.
    • Refactored getInitialLocale() to route stored values through normalizeLocale.
    • New fetchConfigLocale() helper that calls api.getConfig() and reads display.language.
    • New useEffect in I18nProvider that, only when localStorage has no entry, applies the config-derived locale on mount. An explicit dropdown choice — saved to localStorage — always wins.
  • tests/agent/test_i18n.py (+5):
    • Adds pt-BR, pt-PT, and brazilian cases to test_normalize_lang_accepts_aliases so future refactors of the Python alias map can't silently route Portuguese users back to English.

No backend code or config-write path touched.

How to Test

  1. Check out the branch and ensure .venv is set up:
    python3 -m venv .venv && source .venv/bin/activate && pip install -e ".[all,dev]"
    
  2. Run the i18n tests on their own to confirm the pinned aliases:
    scripts/run_tests.sh tests/agent/test_i18n.py -v
    
    Expected: 43 passed.
  3. Manual repro (matches the issue's steps to reproduce):
    • Set display.language: pt-BR in ~/.hermes/config.yaml.
    • Open Hermes Desktop in a fresh browser profile (no hermes-locale key in localStorage).
    • Expected: the UI renders in Portuguese without touching the dropdown.
  4. Manual regression checks:
    • Pick Français from the dropdown, reload the page → UI stays French even though config.yaml still says pt-BR (localStorage wins).
    • Clear localStorage, leave config.yaml without display.language → UI stays English.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (feat(web/i18n): ..., fix(web/i18n): ..., test(i18n): ...)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix (no unrelated commits)
  • I've run scripts/run_tests.sh tests/agent/test_i18n.py and all tests pass
  • I've added tests for my changes (regression case for pt-BR / pt-PT / brazilian)
  • I've tested on my platform: macOS 15.2 (Darwin 24.6.0), Python 3.11

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — N/A; behavior matches existing display.language docs, only the surface that honors it changed
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A, no new keys
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — frontend-only TS change, runs in the browser; backend is untouched
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Screenshots / Logs

$ scripts/run_tests.sh tests/agent/test_i18n.py -v
============================== 43 passed in 2.08s ==============================

$ git diff --stat main..HEAD
 tests/agent/test_i18n.py |  5 ++++
 web/src/i18n/context.tsx | 77 +++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 78 insertions(+), 4 deletions(-)

$ git log --oneline main..HEAD
6e68ef4ee test(i18n): pin pt-BR/pt-PT/brazilian to pt catalog (#26665)
909964b65 fix(web/i18n): seed UI language from config.yaml display.language (#26665)
073f60284 feat(web/i18n): normalize regional locale tags (pt-BR, zh-CN, etc.)

xxxigm added 3 commits May 16, 2026 07:45
Mirrors the alias map in agent/i18n.py so user-supplied values from
config.yaml or legacy localStorage entries route to the right catalog
instead of silently falling back to English.

Refs NousResearch#26665
…usResearch#26665)

The Desktop only read the locale from localStorage, so users who set
`display.language` in `~/.hermes/config.yaml` directly (e.g. via the
CLI setup wizard) saw English on every restart even though the value
was saved.  Fetch `display.language` from `/api/config` on mount and
apply it when the user has not explicitly chosen a language via the
in-browser dropdown — an explicit dropdown choice still wins.

Fixes NousResearch#26665
Adds regression coverage for the specific aliases called out in the
bug report so future refactors of the alias map can't silently route
Portuguese users back to English.
@xxxigm
xxxigm force-pushed the fix/desktop-language-persist-26665 branch from e2e8497 to 6e68ef4 Compare May 16, 2026 00:51
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/config Config system, migrations, profiles javascript labels May 16, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing the dashboard fallback and reusing the existing /api/config API.

Problems

  • The reported issue is for native Desktop, but this diff changes the separate web dashboard surface. apps/desktop/AGENTS.md:11-24 distinguishes them; native Desktop already loads display.language in apps/desktop/src/i18n/context.tsx:107-112, while its locale type does not include Portuguese (apps/desktop/src/i18n/types.ts:8). This dashboard-only change therefore cannot by itself fix the reported pt-BR Desktop behavior.
  • The regression coverage is in tests/agent/test_i18n.py, not the changed web provider. It does not verify the new /api/config fallback or the localStorage-precedence contract in web/src/i18n/context.tsx.

Suggested changes

  • Confirm and state the intended surface: re-scope to apps/desktop for #26665, or present this as a dashboard-parity fix.
  • Add web-provider tests for config pt-BR with no stored locale and for an explicit stored locale taking precedence.

Automated hermes-sweeper review.

Comment thread tests/agent/test_i18n.py
assert i18n._normalize_lang("türkçe") == "tr"
# Regional Portuguese tags should both land on the shared `pt` catalog
# rather than silently falling back to English (#26665).
assert i18n._normalize_lang("pt-BR") == "pt"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions cover the Python alias map, but the behavior added by this PR is in web/src/i18n/context.tsx. Please add a web-provider test that verifies /api/config display.language: "pt-BR" is applied only when no stored locale exists.

@teknium1

teknium1 commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Closing: this patched web/src/i18n/context.tsx (the browser dashboard), while the reporter's surface in #26665 is the Electron Desktop, whose provider lives in apps/desktop/src/i18n/context.tsx and already seeded the locale from display.language via /api/config. The actual Desktop symptom was a startup read race, fixed on main via #106476 (140eaf9e036). The alias map idea (pt-BR → pt) is covered by normalizeLocale on the Desktop side. Thanks for the report-driven diagnosis; the dashboard's language handling is a separate surface and not the source of #26665.

@teknium1 teknium1 closed this Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles area/i18n Localization, locales, translations comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Desktop language setting resets to English on restart despite config.yaml having pt-BR saved

3 participants