Skip to content

feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 19 语言覆盖(ja/de/fr/...) - #339

Merged
HuanLinOTO merged 6 commits into
omdsh-dev:mainfrom
HuanLinOTO:feat/better-locale-ja
Aug 23, 2026
Merged

feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 19 语言覆盖(ja/de/fr/...)#339
HuanLinOTO merged 6 commits into
omdsh-dev:mainfrom
HuanLinOTO:feat/better-locale-ja

Conversation

@HuanLinOTO

@HuanLinOTO HuanLinOTO commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

概述

接入 @huanlin/dsh-plugin-better-locale(可选 peer)让 better-sidebar 支持 19 种第三语言覆盖:ja / de / fr / pt / ko / ar / hi / id / tr / vi / th / ru / it / nl / sv / pl / zh-HK / zh-TW / zh-MO。

better-locale 通过运行时 monkey-patch LocaleRuntime.prototype.lookup 注入第三语言,但 better-sidebar 的内置 t() 不走 ctx.locale(它有自己的 attachLocale + zh/en 词典),所以需要 better-sidebar 自己接入各语言词典才能翻译界面。

覆盖借用 DSH 的英文模式:只在 DSH active locale 为 en 时生效;DSH 在 zh 下覆盖完全惰性(保持原生 zh,界面不混语言)。

改动

  • src/client/locales-<lang>.ts(19 个新文件):各语言词典(每种约 340 keys),与 zh key-set 完全一致,typecheck 守护缺 key/多 key。

  • src/client/locales.ts

    • 新增 attachBetterLocale(store) —— 与 attachLocale 对称,注入 better-locale 的 override store。
    • 新增 19 语言 dict export(从 locales-<lang>.ts 导入,Record<keyof typeof zh, string> 类型注解编译期强制 key 集相等)。
    • t() 改为先查 store.getOverride(active, 'betterSidebar', key),命中则返回覆盖文本,否则走原 zh/en 链。
    • isZh() 改为 store.isOverrideActive(active) 语义(仅在 DSH=en 且 override 激活时返回 false;Chinese-specific selectors 切回)。
  • src/client/index.tsxapply() 里从 ctx.get('betterLocale') 取得 override store(optional,未安装时为 undefined),attachBetterLocale 注入 t();同时 store.register('betterSidebar', { ja, de, ... }) 把 19 语言词典注册进 better-locale 的 store,让外部 ctx.locale.lookup('betterSidebar', key) 调用者也能拿到覆盖文本。注册包在 ctx.effect 内,HMR 安全。

  • src/client/Sidebar.tsx:新增 BetterLocaleActive uSES 订阅 store.subscribe,覆盖切换时全树重新渲染。

  • tests/locales.spec.ts:新增 19 语言 override 测试(命中 / fallback / detach / interpolate / 各 dict key-set 完整性)。

  • package.json@huanlin/dsh-plugin-better-locale 加为 optional peer + devDep link。

  • AGENTS.md / README.md:文档化第三语言覆盖机制。

兼容性

未安装 better-locale 时,ctx.get('betterLocale') 返回 undefined,整段为 no-op;zh/en 行为完全不变。

better-locale 是 optional peer(peerDependenciesMeta.optional: true),不影响现有安装。

测试

  • pnpm typecheck
  • pnpm test -- tests/locales.spec.ts ✅(含 19 语言 dict key-set 完整性断言)

其他平台性失败测试(smoke/pty/sidebar-crash)为 Windows 预存问题(CRLF、pty spawning),与该 PR 无关(stash 验证 main 同样失败)。

关联

依赖插件 @huanlin/dsh-plugin-better-locale(v0.1.0 已发布):仓库 huanlinoto/dsh-plugin-better-locale,npm @huanlin/dsh-plugin-better-locale

设计文档:dsh-plugin-better-locale/docs/plans/2026-08-23-better-locale-design.md

鸣谢

俄语词典参考了 @Vladimir-Kryshchenko#302 的贡献。

新增 ja 词典(src/client/locales-ja.ts,326 keys,与 zh key-set-equal),
通过可选 peer @huanlin/dsh-plugin-better-locale 接入第三语言覆盖:

- locales.ts: 新增 attachBetterLocale(store) + ja dict export;t() 先查
  store.getOverride(active, 'betterSidebar', key),命中则返回覆盖文本,
  否则走原 zh/en 链;isZh() 在 override 激活时返回 false
- client/index.tsx: apply 中 ctx.get('betterLocale') 取得 override store
  (optional,未安装时为 undefined),attachBetterLocale 注入 t();同时
  register('betterSidebar', { ja }) 把 ja 词典注册进 better-locale 的
  store,让外部 ctx.locale.lookup 调用者也能拿到 ja 文本
- Sidebar.tsx: 新增 betterLocaleActive uSES 订阅 store.subscribe,覆盖
  切换时全树重渲染(DSH locale 的 active 字段不变,既有 localeRevision
  uSES 不会触发)
- package.json: @huanlin/dsh-plugin-better-locale 加为 optional peer
  + devDep link
- tests/locales.spec.ts: 新增 7 个 better-locale override 测试(命中/
  fallback/detach/interpolate/ja dict 完整性 smoke check)

未安装 better-locale 时 ctx.get 返回 undefined,整段为 no-op,zh/en
行为完全不变。
@HuanLinOTO
HuanLinOTO marked this pull request as draft August 22, 2026 17:23
@HuanLinOTO HuanLinOTO changed the title feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 ja 覆盖 [WIP]feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 ja 覆盖 Aug 22, 2026
better-locale 的覆盖本质上是借用 DSH 的英文槽位来渲染第三语言:
- store.getOverride(dshActive, ns, key) 改为只在 dshActive === 'en'
  时返回覆盖文本;DSH 在 zh 下覆盖完全惰性(保持原生 zh)
- 新增 store.isOverrideActive(dshActive):返回 active !== undefined
  && dshActive === 'en',供 better-sidebar 的 isZh() 判断
- isZh() 改用 isOverrideActive(dshActive) 判断(仅在 DSH=en 且有
  override 时返回 false),zh 下返回 true
- LanguageSwitcher 接收 dshActive prop,选了非 native 且 DSH 非 en
  时显示「请将 DSH 切换到英文以查看 [语言名]」提示
- 新增 switchToEnHint copy key(zh + en)
- 测试:better-locale +3 store tests(en-only / inert on zh /
  isOverrideActive),+1 patch test(inert on zh);better-sidebar
  +1 locales test(override inert while DSH on zh),FakeBetterLocale
  加 isOverrideActive 方法
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 接入都能正确建立。
@HuanLinOTO HuanLinOTO changed the title [WIP]feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 ja 覆盖 feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 19 语言覆盖(ja/de/fr/...) Aug 23, 2026
@HuanLinOTO HuanLinOTO added enhancement New feature or request P2 中:一般功能/优化 area/i18n Locale dictionaries, translation pipeline labels Aug 23, 2026
Sidebar.tsx read ctx.get('betterLocale') unconditionally. In real
cordis hosts ctx.get exists and returns undefined when better-locale
is absent — but the unit-test mocks build a minimal ctx without a
get method, throwing 'TypeError: ctx.get is not a function' inside
<Sidebar> render and taking down 7 tests
(bottom-auto-terminal + sidebar-crash suites).

Add a typeof guard so the optional contract holds even when ctx.get
is missing. The activation-order-safe ctx.effect path in index.tsx
is unaffected (real cordis Context, runs once).
@HuanLinOTO
HuanLinOTO marked this pull request as ready for review August 23, 2026 09:54
Comment thread package.json Outdated
@HuanLinOTO
HuanLinOTO merged commit d6d8db0 into omdsh-dev:main Aug 23, 2026
2 checks passed
Menghuan1918 added a commit that referenced this pull request Aug 24, 2026
- README / README_EN: add v0.16.0 changelog (free windows #354, model-driven
  sidebar opens #353, Markdown inline HTML + TOC #360, 19-language overlays
  #339, multi-repo git + linked worktrees #326/#285, browser loopback
  allowlist, Vue + 28 legacy languages #202, preview refresh trio #215/#228,
  local Markdown images #292, ego-browser catalog #340; fixes: market/cordis
  #338, ctx.get() reads #357, workspace boundary #345, panel-host layering
  #330/#278, layout-push hardening #310/#130/#180, width sharing #36, terminal
  cleanup #130, fs-search noise dirs #342, mermaid #341, Nerd Font #190,
  UTF-8 HTML #193, trust-fence #182, reveal-in-explorer #94, PowerShell #47,
  GET probe fallback #69, unrun devDep #336) and move v0.15.2 into history
- feature tour: free windows + Markdown inline HTML/TOC (EN), feature list
- plugin ecosystem: add ego-browser (24 tab plugins), install pin ^0.16.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/i18n Locale dictionaries, translation pipeline enhancement New feature or request P2 中:一般功能/优化

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants