Skip to content

fix: UI language reverts to English after a second save in Settings#105

Merged
nashsu merged 1 commit intonashsu:mainfrom
yyyzl:fix/settings-ui-language-revert
May 1, 2026
Merged

fix: UI language reverts to English after a second save in Settings#105
nashsu merged 1 commit intonashsu:mainfrom
yyyzl:fix/settings-ui-language-revert

Conversation

@yyyzl
Copy link
Copy Markdown
Contributor

@yyyzl yyyzl commented Apr 30, 2026

Fixes #104.

Problem

After switching the UI language to Chinese in Settings → Interface and saving, opening another draft-bound section (Embedding / Multimodal / Web Search / Network / Output), editing any field, and clicking Save again silently flips the entire UI back to English and persists language=en to the store. Reproduces 100% on main (v0.4.5).

Root cause

SettingsView has a resync useEffect that rebuilds the whole draft whenever any of the store slices change, and re-reads i18n.language while doing so. handleSave calls multiple zustand setters before it finally calls i18n.changeLanguage at the end — and each await between setters lets React commit, which fires the resync effect mid-save. At that point i18n.language is still the OLD value, so the effect silently rewrites draft.uiLanguage back to the old language.

The first save still works because handleSave closes over its own snapshot of draft (with the new uiLanguage), so if (draft.uiLanguage !== i18n.language) still takes the change-language branch successfully. But after that save:

  • i18n.language === "zh"
  • but the component's draft.uiLanguage === "en" (clobbered by the mid-save resync)

The next save reads the stale draft.uiLanguage = "en", hits the same if condition with "en" !== "zh", and helpfully calls changeLanguage("en") + saveLanguage("en") — reverting and persisting the language flip the user never asked for.

Fix

One-line change in the resync effect: keep the current draft.uiLanguage via a functional setState instead of re-reading i18n.language. uiLanguage is only ever written by the Interface section's setDraft, never out-of-band, so preserving it across store-driven resyncs is correct.

 useEffect(() => {
-  setDraftState(
+  setDraftState((prev) =>
     initialDraft(
       llmConfig, searchApiConfig, embeddingConfig, multimodalConfig,
       outputLanguage, proxyConfig, maxHistoryMessages,
-      i18n.language,
+      prev.uiLanguage,
     ),
   );
 }, [...]);

Plus a code comment explaining why this is intentional, so the next person doesn't "fix" it back.

Verification

Manual repro on main:

  1. en → 中文 in Interface, Save → UI in Chinese ✅, draft.uiLanguage actually became "en" 🐛
  2. Open Embedding, edit endpoint, Save → UI flips to English 🐛

After this patch, step 2 keeps the UI in Chinese, and language=zh stays persisted.

The fix doesn't change any other section's behavior:

  • LLM section persists per-row independently (doesn't touch the shared draft Save)
  • About / Maintenance / Changelog have no draft-bound fields
  • Project switch → out-of-band store change → resync still happens, and prev.uiLanguage at that point is the value used to mount the panel (which equals i18n.language at mount time), so behavior is unchanged for that scenario.

handleSave calls multiple zustand setters (setLlmConfig,
setEmbeddingConfig, ...) BEFORE it calls i18n.changeLanguage at the end.
Each setter creates a new store object, which re-fires the resync
useEffect — and that effect was rebuilding the entire draft from
i18n.language, which is still the OLD language at this point.

Net effect: after the first save (e.g. en→zh) succeeded, the
component-state draft.uiLanguage had been silently rewritten back to
"en" by the mid-save resync. The next save (e.g. editing the Embedding
section) then sees draft.uiLanguage="en" vs i18n.language="zh", enters
the change-language branch, and reverts the UI to English.

Fix: when the resync effect rebuilds the draft, keep the current
draft.uiLanguage via a functional setState — uiLanguage only changes via
the Interface section's setDraft, never out-of-band, so preserving it
across store-driven resyncs is correct.
@nashsu nashsu merged commit 7a4e42f into nashsu:main May 1, 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.

[Bug] UI language reverts to English after saving Embedding (or other) settings

2 participants