mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(studio): pass the system language to the kernel host so the tray follows it
This commit is contained in:
1 parent
7bdaae2025
commit
d3d3cba4ea
6 files changed
+97
-9
No files matched your search
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"reasonix/internal/base/i18n"
|
||||
"reasonix/internal/contract/config"
|
||||
)
|
||||
|
||||
func TestKernelLanguageWithShellSystemLanguage(t *testing.T) {
|
||||
before := i18n.CurrentLanguage()
|
||||
t.Cleanup(func() { i18n.DetectLanguage(before) })
|
||||
for _, key := range []string{"REASONIX_LANG", "LC_ALL", "LC_MESSAGES", "LANG"} {
|
||||
t.Setenv(key, "")
|
||||
}
|
||||
t.Setenv("REASONIX_SYSTEM_LANG", "zh-CN")
|
||||
for _, tt := range []struct{ setting, env, want string }{
|
||||
{"auto", "", "zh"},
|
||||
{"en", "", "en"},
|
||||
{"auto", "en", "en"},
|
||||
{"zh-TW", "en", "zh-TW"},
|
||||
} {
|
||||
t.Setenv("REASONIX_LANG", tt.env)
|
||||
if got := resolveKernelLanguage(&config.Config{Language: tt.setting}); got != tt.want {
|
||||
t.Errorf("setting %q, env %q: language = %q, want %q", tt.setting, tt.env, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,10 @@ class HostStartError extends Error {
|
||||
// the only channel pointing this way. stdin is the lease: the kernel drains
|
||||
// when this end closes, which is what stops it outliving a parent that exited
|
||||
// without being asked to.
|
||||
function start(binary, args, { onStderr, onExit, onAct, onSlow, timeoutMs = HANDSHAKE_TIMEOUT_MS } = {}) {
|
||||
const child = spawn(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
|
||||
function start(binary, args, { onStderr, onExit, onAct, onSlow, systemLanguage, timeoutMs = HANDSHAKE_TIMEOUT_MS } = {}) {
|
||||
const env = { ...process.env };
|
||||
if (systemLanguage) env.REASONIX_SYSTEM_LANG = systemLanguage;
|
||||
const child = spawn(binary, args, { stdio: ["pipe", "pipe", "pipe"], env });
|
||||
child.stderr.setEncoding("utf8");
|
||||
if (onStderr) child.stderr.on("data", onStderr);
|
||||
if (onExit) child.on("exit", onExit);
|
||||
|
||||
@@ -94,6 +94,7 @@ async function launchKernel(args) {
|
||||
const began = Date.now();
|
||||
logs.shell.line(`host: starting ${hostBinary} (attempt ${attempt})`);
|
||||
kernel = start(hostBinary, args, {
|
||||
systemLanguage: app.getPreferredSystemLanguages()[0] ?? app.getLocale(),
|
||||
timeoutMs: handshakeTimeout(),
|
||||
onSlow: () => {
|
||||
logs.shell.line("host: no handshake yet; showing the starting window");
|
||||
|
||||
@@ -6,7 +6,7 @@ import os from "node:os";
|
||||
import fs from "node:fs";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { parse, readActs } = require("../src/host.js");
|
||||
const { parse, readActs, start } = require("../src/host.js");
|
||||
const { contextTemplate, editMenuTemplate, applicationMenuTemplate, menuInstaller } = require("../src/editmenu.js");
|
||||
const { uiLanguage } = require("../src/uilang.js");
|
||||
const { externalTarget } = require("../src/links.js");
|
||||
@@ -17,6 +17,23 @@ const { pick, loadPrefs, savePrefs, registerPrefs } = require("../src/prefs.js")
|
||||
const TOKEN = "a".repeat(64);
|
||||
const line = (over) => JSON.stringify({ version: 1, origin: "http://127.0.0.1:8080", token: TOKEN, ...over });
|
||||
|
||||
test("the spawned host receives the system language and inherits explicit locale overrides", async () => {
|
||||
const script = `console.log(${JSON.stringify(line())});
|
||||
console.log(JSON.stringify({act: JSON.stringify({system: process.env.REASONIX_SYSTEM_LANG,
|
||||
explicit: process.env.REASONIX_LANG})}));`;
|
||||
const before = process.env.REASONIX_SYSTEM_LANG;
|
||||
const explicit = process.env.REASONIX_LANG;
|
||||
const state = new Promise((resolve) => {
|
||||
const host = start(process.execPath, ["-e", script], {
|
||||
systemLanguage: "zh-Hant-TW",
|
||||
onAct: (act) => resolve(JSON.parse(act)),
|
||||
});
|
||||
host.ready.catch(resolve);
|
||||
});
|
||||
assert.deepEqual(await state, { system: "zh-Hant-TW", ...(explicit === undefined ? {} : { explicit }) });
|
||||
assert.equal(process.env.REASONIX_SYSTEM_LANG, before);
|
||||
});
|
||||
|
||||
test("the handshake is accepted only when every field accounts for itself", () => {
|
||||
assert.deepEqual(parse(line()), { origin: "http://127.0.0.1:8080", token: TOKEN });
|
||||
|
||||
|
||||
@@ -5,11 +5,9 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DetectLanguage selects a catalogue from override (e.g. cfg.Language) or the
|
||||
// environment and installs it as M. Returns the resolved tag ("en", "zh") so
|
||||
// callers can log or expose it.
|
||||
//
|
||||
// Priority: override > REASONIX_LANG > LC_ALL > LC_MESSAGES > LANG > "en".
|
||||
// DetectLanguage installs the catalogue for an explicit setting or locale.
|
||||
// Explicit settings and locale variables take priority over the shell's system
|
||||
// language and the native OS probe; unresolved locales fall back to English.
|
||||
func DetectLanguage(override string) string {
|
||||
for _, c := range append([]string{override}, envCandidates()...) {
|
||||
if tag := normalize(c); tag != "" {
|
||||
@@ -24,7 +22,7 @@ func DetectLanguage(override string) string {
|
||||
var osLanguage = detectOSLanguage
|
||||
|
||||
func envCandidates() []string {
|
||||
keys := []string{"REASONIX_LANG", "LC_ALL", "LC_MESSAGES", "LANG"}
|
||||
keys := []string{"REASONIX_LANG", "LC_ALL", "LC_MESSAGES", "LANG", "REASONIX_SYSTEM_LANG"}
|
||||
out := make([]string, 0, len(keys)+1)
|
||||
for _, k := range keys {
|
||||
out = append(out, os.Getenv(k))
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package i18n
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestShellSystemLanguagePriority(t *testing.T) {
|
||||
probe := osLanguage
|
||||
osLanguage = func() string { return "en-US" }
|
||||
t.Cleanup(func() { osLanguage = probe })
|
||||
before := CurrentLanguage()
|
||||
t.Cleanup(func() { setLanguage(before) })
|
||||
for _, tt := range []struct {
|
||||
name, override, key, value, system, want string
|
||||
}{
|
||||
{"auto", "auto", "", "", "zh-CN", "zh"},
|
||||
{"traditional", "auto", "", "", "zh-Hant-TW", "zh-TW"},
|
||||
{"setting", "en", "", "", "zh-CN", "en"},
|
||||
{"reasonix", "auto", "REASONIX_LANG", "en", "zh-CN", "en"},
|
||||
{"all", "auto", "LC_ALL", "en_US.UTF-8", "zh-CN", "en"},
|
||||
{"messages", "auto", "LC_MESSAGES", "en_US.UTF-8", "zh-CN", "en"},
|
||||
{"lang", "auto", "LANG", "en_US.UTF-8", "zh-CN", "en"},
|
||||
{"unsupported", "auto", "", "", "fr-FR", "en"},
|
||||
{"absent", "auto", "", "", "", "en"},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for _, key := range []string{"REASONIX_LANG", "LC_ALL", "LC_MESSAGES", "LANG"} {
|
||||
t.Setenv(key, "")
|
||||
}
|
||||
t.Setenv("REASONIX_SYSTEM_LANG", tt.system)
|
||||
if tt.key != "" {
|
||||
t.Setenv(tt.key, tt.value)
|
||||
}
|
||||
if got := DetectLanguage(tt.override); got != tt.want {
|
||||
t.Fatalf("language = %q, want %q", got, tt.want)
|
||||
}
|
||||
say := CatalogFor(tt.override)
|
||||
want := Catalog(tt.want)
|
||||
if say.TrayOpen != want.TrayOpen || say.TrayCloseToTray != want.TrayCloseToTray || say.TrayQuit != want.TrayQuit || say.TrayIdle != want.TrayIdle {
|
||||
t.Fatal("tray catalogue does not follow the resolved language")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user