Summary
apps/memos-local-plugin/viewer/src/stores/i18n.ts computes the module-level default locale at import time:
function detectDefault(): Locale {
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved === "en" || saved === "zh") return saved;
} catch {
// ignore
}
const nav = (typeof navigator !== "undefined" && navigator.language) || "en";
return nav.toLowerCase().startsWith("zh") ? "zh" : "en";
}
export const locale = signal<Locale>(detectDefault());
Under Node (Vitest / SSR / CI without jsdom), navigator is undefined and localStorage throws (swallowed by the try/catch), so locale is always "en". Any unit test that needs to assert zh-CN strings (e.g. "已连接") can never pass through the real detection path — tests must pin locale.value = "zh" by reaching into the store internals.
Proposal
Make the default injectable / SSR-safe so tests (and SSR hosts) can exercise the detection logic without mutating the exported signal, e.g.:
export function detectDefault(opts?: { navLanguage?: string; storageKey?: string }): Locale
or lazy-evaluate detectDefault so a test can stub globalThis.navigator before first read.
Either keeps the browser behavior identical while letting tests cover the zh-* → zh branch that currently ships untested.
Happy to send a small PR if you'd like — we have one ready downstream.
Summary
apps/memos-local-plugin/viewer/src/stores/i18n.tscomputes the module-level default locale at import time:Under Node (Vitest / SSR / CI without jsdom),
navigatoris undefined andlocalStoragethrows (swallowed by the try/catch), solocaleis always"en". Any unit test that needs to assert zh-CN strings (e.g."已连接") can never pass through the real detection path — tests must pinlocale.value = "zh"by reaching into the store internals.Proposal
Make the default injectable / SSR-safe so tests (and SSR hosts) can exercise the detection logic without mutating the exported signal, e.g.:
or lazy-evaluate
detectDefaultso a test can stubglobalThis.navigatorbefore first read.Either keeps the browser behavior identical while letting tests cover the
zh-*→zhbranch that currently ships untested.Happy to send a small PR if you'd like — we have one ready downstream.