From 16a809594cdce8d4846c653a1bded9209096fd7c Mon Sep 17 00:00:00 2001 From: HuanLinOTO Date: Sun, 23 Aug 2026 02:31:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(i18n):=20better-locale=20=E6=8E=A5=E5=85=A5?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20activation-order-safe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ctx.get('betterLocale') 是非响应式读取(cordis 只对声明的 inject 依赖重新求值)。如果 better-locale 在 better-sidebar 之后激活, apply 顶层的 ctx.get 返回 undefined,attachBetterLocale(undefined) 使整个覆盖接入变成 no-op,即使用户后来在设置页选了日语, better-sidebar 的 t() 也不查询 override store。 改为在 ctx.effect 中订阅 ctx.locale.subscribe:better-locale 在 激活时(若有持久化的 override)和用户切换 override 时都会 bump locale revision,subscribe 回调触发 sync(),重新读取 ctx.get ('betterLocale')。store 可用后 attachBetterLocale(store) + register ja dict;store 不可用时 detach。 这解决了插件激活顺序不确定的问题:无论 better-locale 先于还是后于 better-sidebar 激活,override 接入都能正确建立。 --- src/client/index.tsx | 58 ++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/client/index.tsx b/src/client/index.tsx index 856fac3..a8fd047 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -67,28 +67,44 @@ export function apply(ctx: Context): void { // directly) also switches to the override language. We also register // the ja dict with the better-locale store so external callers of // ctx.locale.lookup('betterSidebar', key) get the override text too. - // Optional peer: ctx.get returns undefined when better-locale is absent, - // in which case the whole block is a no-op and the zh/en chain runs. - const betterLocale = ctx.get('betterLocale') as - | { - readonly active: string | undefined - getOverride(dshActive: string, ns: string, key: string): string | undefined - isOverrideActive(dshActive: string): boolean - register(ns: string, dicts: Record>): () => void - subscribe(listener: () => void): () => void + // + // Activation-order-safe: ctx.get('betterLocale') is a non-reactive read + // (cordis only re-evaluates declared `inject` deps). If better-locale + // activates after better-sidebar, the initial read returns undefined. + // We subscribe to the locale revision — better-locale bumps it on + // activation (when a persisted override exists) and on every override + // switch — and re-check ctx.get on each bump, attaching + registering + // the ja dict once the store becomes available. + ctx.effect(() => { + let dispose: (() => void) | undefined + const sync = (): void => { + dispose?.() + dispose = undefined + const store = ctx.get('betterLocale') as + | { + readonly active: string | undefined + getOverride(dshActive: string, ns: string, key: string): string | undefined + isOverrideActive(dshActive: string): boolean + register(ns: string, dicts: Record>): () => void + subscribe(listener: () => void): () => void + } + | undefined + attachBetterLocale(store) + if (store !== undefined) { + dispose = store.register(LOCALE_NS, { ja }) } - | undefined - attachBetterLocale(betterLocale) - if (betterLocale !== undefined) { - ctx.effect(() => { - // Register the ja dict for the betterSidebar namespace. Better-locale's - // patched lookup consults this when the user has selected the ja - // override; the dict stays owned by better-sidebar (the namespace - // owner), so a single source of truth covers both ctx.locale callers - // and the internal t(). - return betterLocale.register(LOCALE_NS, { ja }) - }, 'dsh-better-sidebar: better-locale ja dict') - } + } + // Initial check (picks up the store if better-locale activated first). + sync() + // Re-check on every locale revision bump (better-locale bumps when it + // activates with a persisted override, and when the user switches). + const unsubscribe = ctx.locale.subscribe(sync) + return () => { + unsubscribe() + dispose?.() + attachBetterLocale(undefined) + } + }, 'dsh-better-sidebar: better-locale lazy integration') // One store instance per activation: production code creates it only here, // then hands it to the mounted panel and closes over it in the slot // registrations (the official createXXXStore() factory rule — no