@deepseek-ai/dsh-client-locale 0.1.1-rc.2 → 0.1.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +125 -3
- package/README.zh.md +126 -4
- package/lib/client.js +224 -75
- package/lib/index.js +4 -3
- package/lib/invariant.js +3 -4
- package/lib/types/client/index.d.ts +66 -27
- package/lib/types/client/settings-store.d.ts +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/locale-settings.d.ts +6 -2
- package/lib/types/locales/en.d.ts +15 -0
- package/lib/types/locales/zh.d.ts +15 -0
- package/package.json +16 -21
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/locale/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 7c2264bb59165c7c4e213516b4337fd5bc854bf8
|
|
6
|
+
README.zh.md: 1693f7fce4b3aab33e79d1fe20943050ff992188
|
package/README.md
CHANGED
|
@@ -1,12 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Localization for the web GUI: the zh/en preference, browser-derived fallback, typed namespace dictionaries, and the framework translation seat, for users and plugin authors."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-client-locale
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`dsh-client-locale` localizes the web GUI: users choose from the registered languages in Settings → General, and the UI copy switches immediately. The package ships `zh` and `en`, while external client plugins can add languages and their namespace dictionaries. On a loopback page, the choice persists as `locale.preference` in `$DSH_HOME/settings.yaml`; a non-loopback page keeps its selection process-local even though Connection authenticates every API method. A fresh browser starts provisionally in the first registered language requested by `navigator` until an allowed Host preference arrives and replaces it live. Plugin authors receive full type checking for the built-in dictionary form and translate through the framework `t` seat; copy rendered through slots follows language switches without a reload.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Use it wherever the web GUI needs a language switch or translated copy: the shipped settings row covers users, and plugin authors register their own dictionaries. Nothing needs configuration to mount — the package activates with the client tree.
|
|
29
|
+
|
|
30
|
+
### Choosing a language
|
|
31
|
+
|
|
32
|
+
Open Settings → General and select a registered language. The active locale is applied immediately: the UI copy switches, `<html lang>` points at the external id or built-in document tag, and the choice is written to the durable settings section. A browser without an explicit Host preference selects the first registered language that matches `navigator` by full tag and then primary subtag, falling back to English. A stored external locale waits for its definition to register instead of becoming active while unavailable.
|
|
33
|
+
|
|
34
|
+
### Registering a dictionary
|
|
35
|
+
|
|
36
|
+
Call `ctx.locale.register(ns, { zh, en })` with a namespace merged into `LocaleNamespaceMap`; the compiler checks every key against the namespace's typed key union and requires both shipped locales. Consumers translate through `ctx.locale.bind(ns)` or the framework-injected `t` seat. A dictionary registered after the UI is already mounted is picked up without a remount.
|
|
37
|
+
|
|
38
|
+
### Registering a language pack
|
|
39
|
+
|
|
40
|
+
An external client plugin registers the language definition and each translated namespace as owned effects; definitions and dictionaries may register in either order:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
export const inject = ['locale']
|
|
44
|
+
|
|
45
|
+
export function apply(ctx) {
|
|
46
|
+
ctx.effect(
|
|
47
|
+
() => ctx.locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' }),
|
|
48
|
+
'my-locale: language',
|
|
49
|
+
)
|
|
50
|
+
ctx.effect(
|
|
51
|
+
() => ctx.locale.register('common', 'ja', {
|
|
52
|
+
cancel: 'キャンセル',
|
|
53
|
+
close: '閉じる',
|
|
54
|
+
}),
|
|
55
|
+
'my-locale: common dictionary',
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
An external id is a non-empty ASCII BCP 47-style tag. Its fallback must already be registered, and the chain must terminate at `en`; unknown targets, duplicate ids, and cycles fail at registration. Lookup walks the fallback chain in the requested namespace, repeats it in `common`, then displays the key. Unloading a definition removes it from the selector and returns an active selection to the available browser/default locale.
|
|
61
|
+
|
|
62
|
+
### What the Host half does
|
|
63
|
+
|
|
64
|
+
The Host persists the preference through the settings service on loopback pages. The Client deliberately withholds that settings scope on non-loopback pages, so their locale selection remains process-local even though Connection authenticates every API method.
|
|
65
|
+
|
|
66
|
+
-----
|
|
67
|
+
|
|
68
|
+
<a id="understand-the-implementation"></a>
|
|
69
|
+
## Understand the implementation
|
|
70
|
+
|
|
71
|
+
<details>
|
|
72
|
+
<summary>Implementation internals — click to expand</summary>
|
|
73
|
+
|
|
74
|
+
This section explains how the locale service is built; observable behavior is covered in [Use this package](#use-this-package).
|
|
6
75
|
|
|
76
|
+
### Design concept
|
|
77
|
+
|
|
78
|
+
One `LocaleRuntime` owns the preference and the dictionary registry, and is itself the slot system's `LocaleFace`: `getSnapshot`/`subscribe` back the framework-injected `t` seat through `ctx.slots.installLocale`. The immutable snapshot carries the active locale, the selectable locales, and a monotonic revision; dictionary registration and locale switches both advance the revision, but only a switch emits the `locale/change` event. Product-authored Client UI text must enter through these typed dictionaries or an already-localized primitive prop; `verify-client-ui-i18n` enforces that source ownership ([decision](../../../.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.md)).
|
|
79
|
+
|
|
80
|
+
### Preference resolution
|
|
81
|
+
|
|
82
|
+
The provisional locale comes from the browser (`navigator.languages` matched by full tag and then primary subtag, English as the fallback), standing in until the allowed Host-backed settings scope delivers its stored preference. The Host read runs after plugin activation so an unavailable or withheld settings scope cannot block the page, and the result replaces the provisional value live. A stored external locale waits for its definition to register. `setLocale` is the only write entry; it persists even when the id already matches the active locale, because the active value may be provisional and must survive a different browser sharing the same home.
|
|
83
|
+
|
|
84
|
+
### Dictionary lookup
|
|
85
|
+
|
|
86
|
+
The typed object form requires complete dictionaries for both built-in locales. The per-locale form lets language packs register each namespace independently. For each key, lookup walks the active language's declared fallback chain in the requested namespace, repeats that chain in `common`, then displays the key itself. Bound translate functions retain stable identity per namespace so they can ride inject surfaces without breaking memoization.
|
|
87
|
+
|
|
88
|
+
### Source map
|
|
89
|
+
|
|
90
|
+
| File | Role |
|
|
91
|
+
|---|---|
|
|
92
|
+
| [`src/client/index.ts`](src/client/index.ts) | `LocaleRuntime`, dictionary registry, Language row registration, `locale/change` event |
|
|
93
|
+
| [`src/index.ts`](src/index.ts) | Node half: registers the `locale` settings namespace |
|
|
94
|
+
| [`src/locale-settings.ts`](src/locale-settings.ts) | The durable schema for `locale.preference` |
|
|
95
|
+
| [`src/locales/`](src/locales/) | The shipped `zh`/`en` dictionaries |
|
|
96
|
+
|
|
97
|
+
</details>
|
|
98
|
+
|
|
99
|
+
-----
|
|
100
|
+
|
|
101
|
+
<a id="further-exploration"></a>
|
|
102
|
+
## Further Exploration
|
|
103
|
+
|
|
104
|
+
Read these when the locale contract is not enough: the slot face it implements, the settings surface it rides, and the persistence decision behind the preference.
|
|
105
|
+
|
|
106
|
+
- [Client slot system](../ui-slots/README.md) — the slot model and the `LocaleFace` seat this package implements.
|
|
107
|
+
- [Host-backed preferences decision](../../../.agents/notes/implemented/bug-fix/2026-08-06-host-backed-web-preferences.md) — why the preference persists in Host settings instead of the browser.
|
|
108
|
+
- [Settings group map](../../settings/README.md) — the settings service that stores the preference.
|
|
109
|
+
- [Client group map](../README.md) — the browser half this package belongs to.
|
|
110
|
+
|
|
111
|
+
-----
|
|
112
|
+
|
|
113
|
+
<a id="model-experience"></a>
|
|
7
114
|
## Model Experience
|
|
8
115
|
|
|
9
|
-
None, as the locale
|
|
116
|
+
None, as the locale service is a browser-side UI plugin layer that registers nothing model-facing.
|
|
10
117
|
|
|
11
118
|
#### KV Cache effect
|
|
12
119
|
|
|
@@ -14,5 +121,20 @@ None; this package neither assembles nor sends a provider request.
|
|
|
14
121
|
|
|
15
122
|
## Known Limitations and Deferred Work
|
|
16
123
|
|
|
17
|
-
-
|
|
124
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
These limits define where localization is incomplete or frozen at registration time. They are current package constraints, not a task backlog.
|
|
128
|
+
|
|
18
129
|
- **Registry-held text reads its translation once** — copy captured at registration time outside the slot render path (e.g. the `/model` command description in the command registry) keeps the language it was registered under until re-registration; slot-rendered copy follows switches live.
|
|
130
|
+
- **Language packs own language-specific behavior** — the registry supplies selection, persistence, browser matching, key fallback, and `<html lang>`; it does not add plural rules or bidirectional layout.
|
|
131
|
+
|
|
132
|
+
<a id="dev-note"></a>
|
|
133
|
+
### Dev Note
|
|
134
|
+
|
|
135
|
+
<details>
|
|
136
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
137
|
+
|
|
138
|
+
None.
|
|
139
|
+
|
|
140
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,18 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "面向用户与插件作者的 web GUI 本地化说明:zh/en 偏好、浏览器派生回退、类型化命名空间词典与框架翻译席位。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-client-locale
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-client-locale` 为 web GUI 提供本地化:用户在“设置 → 常规”中从已注册语言中选择,UI 文案会立即切换。本包内置 `zh` 与 `en`,外部 client 插件可以增加语言及其命名空间字典。在 loopback 页面上,该选择以 `locale.preference` 存储在 `$DSH_HOME/settings.yaml` 中;非 loopback 页面即使由 Connection 认证所有 API 方法,也只在进程内保留选择。全新浏览器会先临时使用 `navigator` 请求的第一个已注册语言,直到允许读取的 Host 偏好到达并实时替换。插件作者使用内置字典形式时会获得完整类型检查,并通过框架 `t` 席位翻译;经 slot 渲染的文案会随语言切换即时更新。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
只要 web GUI 需要语言切换或翻译文案就使用它:已发布的设置行覆盖用户侧,插件作者则注册自己的词典。挂载无需任何配置——本包随客户端树一起激活。
|
|
29
|
+
|
|
30
|
+
### 选择语言
|
|
31
|
+
|
|
32
|
+
打开“设置 → 常规”并选择一种已注册语言。生效中的 locale 会立即应用:UI 文案切换、`<html lang>` 指向外部 id 或内置语言的文档标签,选择写入持久设置分区。没有显式 Host 偏好的浏览器会按完整标签、再按主子标签选择 `navigator` 请求的第一个已注册语言,无法匹配时回退到 English。已存储的外部 locale 会等待其定义注册,不会在不可用时生效。
|
|
33
|
+
|
|
34
|
+
### 注册词典
|
|
35
|
+
|
|
36
|
+
用已合并进 `LocaleNamespaceMap` 的命名空间调用 `ctx.locale.register(ns, { zh, en })`;编译器会对照该命名空间的类型化键并集检查每个键,并要求两个内置 locale 齐全。消费方通过 `ctx.locale.bind(ns)` 或框架注入的 `t` 席位翻译。UI 已挂载后再注册的词典无需重新挂载即可生效。
|
|
37
|
+
|
|
38
|
+
### 注册语言包
|
|
39
|
+
|
|
40
|
+
外部 client 插件把语言定义和每个已翻译命名空间注册为自身拥有的 effect;定义与字典可以按任意顺序注册:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
export const inject = ['locale']
|
|
44
|
+
|
|
45
|
+
export function apply(ctx) {
|
|
46
|
+
ctx.effect(
|
|
47
|
+
() => ctx.locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' }),
|
|
48
|
+
'my-locale: language',
|
|
49
|
+
)
|
|
50
|
+
ctx.effect(
|
|
51
|
+
() => ctx.locale.register('common', 'ja', {
|
|
52
|
+
cancel: 'キャンセル',
|
|
53
|
+
close: '閉じる',
|
|
54
|
+
}),
|
|
55
|
+
'my-locale: common dictionary',
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
外部 id 必须是非空的 ASCII BCP 47 风格标签。它的 fallback 必须已经注册,且整条链必须终止于 `en`;未知目标、重复 id 与循环会在注册时失败。查找时先在请求命名空间内遍历生效语言的 fallback 链,再在 `common` 中遍历该链,最后显示键本身。卸载语言定义会将其从选择器移除,并让生效中的选择回落到可用的浏览器语言或默认语言。
|
|
61
|
+
|
|
62
|
+
### Host 半侧做什么
|
|
63
|
+
|
|
64
|
+
Host 通过 settings 服务为 loopback 页面持久化偏好。Client 会刻意拒绝非 loopback 页面使用该 settings scope,因此即使 Connection 认证所有 API 方法,它们的 locale 选择仍只存在于进程内。
|
|
65
|
+
|
|
66
|
+
-----
|
|
67
|
+
|
|
68
|
+
<a id="understand-the-implementation"></a>
|
|
69
|
+
## 理解实现
|
|
70
|
+
|
|
71
|
+
<details>
|
|
72
|
+
<summary>实现细节——点击展开</summary>
|
|
73
|
+
|
|
74
|
+
本节解释 locale 服务的构建方式;可观察行为已在[使用本包](#use-this-package)中说明。
|
|
6
75
|
|
|
76
|
+
### 设计理念
|
|
77
|
+
|
|
78
|
+
一个 `LocaleRuntime` 同时拥有偏好与词典注册表,并且自身就是 slot 系统的 `LocaleFace`:`getSnapshot`/`subscribe` 通过 `ctx.slots.installLocale` 支撑框架注入的 `t` 席位。不可变快照携带生效中的 locale、可选择的 locale 列表与单调 revision;词典注册与 locale 切换都会推进 revision,但只有切换会发出 `locale/change` 事件。产品编写的 Client UI 文本必须来自这些带类型的字典,或来自已经本地化的 primitive prop;`verify-client-ui-i18n` 强制执行该源码归属(见[决策](../../../.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.zh.md))。
|
|
79
|
+
|
|
80
|
+
### 偏好解析
|
|
81
|
+
|
|
82
|
+
临时 locale 来自浏览器(`navigator.languages` 先按完整标签、再按主子标签匹配,以 English 作为回退),在允许使用的 Host-backed settings scope 送达其存储偏好之前生效。Host 读取在插件激活后运行,因此 settings scope 不可用或被拒绝都不会阻塞页面,结果会实时替换临时值。已存储的外部 locale 会等待其定义注册。`setLocale` 是唯一写入入口;即使 id 已与生效中的 locale 匹配也会持久化,因为生效中的值可能是临时的,必须能在共享同一 home 的其他浏览器上存活。
|
|
83
|
+
|
|
84
|
+
### 词典查找
|
|
85
|
+
|
|
86
|
+
带类型的对象形式要求两个内置 locale 都有完整字典;逐 locale 形式允许语言包独立注册每个命名空间。逐键查找会先在请求命名空间中沿生效语言声明的 fallback 链查找,再在 `common` 中重复该链,最后显示键本身。绑定的翻译函数按命名空间保持稳定身份,因此可以挂在 inject 表面上而不破坏 memoization。
|
|
87
|
+
|
|
88
|
+
### 源码地图
|
|
89
|
+
|
|
90
|
+
| 文件 | 职责 |
|
|
91
|
+
|---|---|
|
|
92
|
+
| [`src/client/index.ts`](src/client/index.ts) | `LocaleRuntime`、词典注册表、Language 行注册、`locale/change` 事件 |
|
|
93
|
+
| [`src/index.ts`](src/index.ts) | node 半侧:注册 `locale` 设置命名空间 |
|
|
94
|
+
| [`src/locale-settings.ts`](src/locale-settings.ts) | `locale.preference` 的持久 schema |
|
|
95
|
+
| [`src/locales/`](src/locales/) | 已发布的 `zh`/`en` 词典 |
|
|
96
|
+
|
|
97
|
+
</details>
|
|
98
|
+
|
|
99
|
+
-----
|
|
100
|
+
|
|
101
|
+
<a id="further-exploration"></a>
|
|
102
|
+
## 进一步探索
|
|
103
|
+
|
|
104
|
+
当 locale 约定不够用时阅读以下页面:它所实现的 slot 面孔、它所依托的设置界面,以及偏好背后的持久化决策。
|
|
105
|
+
|
|
106
|
+
- [客户端 slot 系统](../ui-slots/README.zh.md)——本包实现的 slot 模型与 `LocaleFace` 席位。
|
|
107
|
+
- [Host 支撑偏好决策](../../../.agents/notes/implemented/bug-fix/2026-08-06-host-backed-web-preferences.zh.md)——偏好为何持久化在 Host 设置中而非浏览器里。
|
|
108
|
+
- [设置组地图](../../settings/README.zh.md)——存储该偏好的设置服务。
|
|
109
|
+
- [客户端组地图](../README.zh.md)——本包所属的浏览器半侧。
|
|
110
|
+
|
|
111
|
+
-----
|
|
112
|
+
|
|
113
|
+
<a id="model-experience"></a>
|
|
7
114
|
## 模型体验
|
|
8
115
|
|
|
9
|
-
无。locale
|
|
116
|
+
无。locale 服务属于浏览器侧 UI 插件层,不注册任何面向模型的内容。
|
|
10
117
|
|
|
11
118
|
#### KV Cache 影响
|
|
12
119
|
|
|
13
120
|
无;该包既不组装也不发送提供方请求。
|
|
14
121
|
|
|
15
|
-
##
|
|
122
|
+
## 已知限制与延期工作
|
|
123
|
+
|
|
124
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
这些限制说明本地化在哪些地方不完整,或在注册时被冻结。它们是当前包约束,不是任务积压。
|
|
16
128
|
|
|
17
|
-
- **部分界面仍保留内联文案**——设置行、侧边栏、问题作答器和模型选择使用 locale seat;其他包仍直接拥有静态文本。
|
|
18
129
|
- **注册表持有的文本只读取一次翻译**——在 slot 渲染路径之外于注册时捕获的文案(例如 command 注册表中的 `/model` 命令描述)在重新注册前保持注册时的语言;slot 渲染的文案随切换实时更新。
|
|
130
|
+
- **语言包负责语言特有行为**——注册表提供选择、持久化、浏览器匹配、逐 key 回退和 `<html lang>`;它不增加复数规则或双向布局。
|
|
131
|
+
|
|
132
|
+
<a id="dev-note"></a>
|
|
133
|
+
### 开发备注
|
|
134
|
+
|
|
135
|
+
<details>
|
|
136
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
137
|
+
|
|
138
|
+
无。
|
|
139
|
+
|
|
140
|
+
</details>
|
package/lib/client.js
CHANGED
|
@@ -7,7 +7,7 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
|
-
let
|
|
10
|
+
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
11
11
|
//#region ../../../vendor/cosmokit/src/misc.ts
|
|
12
12
|
/** Return true when a value is `null` or `undefined`. */
|
|
13
13
|
function isNullable(value) {
|
|
@@ -806,9 +806,11 @@ window.__ModuleLoader__.load({
|
|
|
806
806
|
const LOCALE_SETTINGS_NAMESPACE = "locale";
|
|
807
807
|
/** Field carrying an explicit locale selection; absence delegates to the browser. */
|
|
808
808
|
const LOCALE_PREFERENCE_FIELD = "preference";
|
|
809
|
+
/** Accepted BCP 47-style language ids. */
|
|
810
|
+
const LOCALE_ID_PATTERN = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/u;
|
|
809
811
|
/** Locale identifiers shipped by the browser client. */
|
|
810
812
|
const LOCALE_IDS = ["zh", "en"];
|
|
811
|
-
Schema.object({ [LOCALE_PREFERENCE_FIELD]: Schema.
|
|
813
|
+
Schema.object({ [LOCALE_PREFERENCE_FIELD]: Schema.string().pattern(LOCALE_ID_PATTERN).required(false) });
|
|
812
814
|
//#endregion
|
|
813
815
|
//#region lib/types/locales/zh.js
|
|
814
816
|
/** zh base dictionary for the common namespace: cross-feature standard words. */
|
|
@@ -818,6 +820,13 @@ window.__ModuleLoader__.load({
|
|
|
818
820
|
"close": "关闭",
|
|
819
821
|
"copy": "复制",
|
|
820
822
|
"copied": "复制成功",
|
|
823
|
+
"copy.failed": "复制失败",
|
|
824
|
+
"copy.value": "复制值",
|
|
825
|
+
"copy.json": "复制 JSON",
|
|
826
|
+
"copy.path": "复制属性路径",
|
|
827
|
+
"copy.prettyJson": "复制格式化 JSON",
|
|
828
|
+
"copy.compactJson": "复制紧凑 JSON",
|
|
829
|
+
"copy.optionsHint": "{action};右键点击可选择复制方式",
|
|
821
830
|
"retry": "重试",
|
|
822
831
|
"loading": "加载中…",
|
|
823
832
|
"load.failed": "加载失败",
|
|
@@ -834,9 +843,17 @@ window.__ModuleLoader__.load({
|
|
|
834
843
|
"collapse": "收起",
|
|
835
844
|
"expand": "展开",
|
|
836
845
|
"back": "返回",
|
|
846
|
+
"brand.localBuild": "DSH 本地构建",
|
|
837
847
|
"unknown": "未知",
|
|
838
848
|
"none": "无",
|
|
839
|
-
"truncated": "已截断"
|
|
849
|
+
"truncated": "已截断",
|
|
850
|
+
"json.collapseNode": "收起 JSON 节点",
|
|
851
|
+
"json.expandNode": "展开 JSON 节点",
|
|
852
|
+
"json.label": "JSON",
|
|
853
|
+
"markdown.footnotes": "脚注",
|
|
854
|
+
"markdown.truncatedCharacters": "… 已截断,共 {total} 字符",
|
|
855
|
+
"number.thousand": "{value}K",
|
|
856
|
+
"number.million": "{value}M"
|
|
840
857
|
};
|
|
841
858
|
//#endregion
|
|
842
859
|
//#region lib/types/locales/en.js
|
|
@@ -847,6 +864,13 @@ window.__ModuleLoader__.load({
|
|
|
847
864
|
"close": "Close",
|
|
848
865
|
"copy": "Copy",
|
|
849
866
|
"copied": "Copied",
|
|
867
|
+
"copy.failed": "Copy failed",
|
|
868
|
+
"copy.value": "Copy value",
|
|
869
|
+
"copy.json": "Copy JSON",
|
|
870
|
+
"copy.path": "Copy property path",
|
|
871
|
+
"copy.prettyJson": "Copy pretty JSON",
|
|
872
|
+
"copy.compactJson": "Copy compact JSON",
|
|
873
|
+
"copy.optionsHint": "{action}; right-click for copy options",
|
|
850
874
|
"retry": "Retry",
|
|
851
875
|
"loading": "Loading…",
|
|
852
876
|
"load.failed": "Failed to load",
|
|
@@ -863,9 +887,17 @@ window.__ModuleLoader__.load({
|
|
|
863
887
|
"collapse": "Collapse",
|
|
864
888
|
"expand": "Expand",
|
|
865
889
|
"back": "Back",
|
|
890
|
+
"brand.localBuild": "DSH Local Build",
|
|
866
891
|
"unknown": "Unknown",
|
|
867
892
|
"none": "None",
|
|
868
|
-
"truncated": "Truncated"
|
|
893
|
+
"truncated": "Truncated",
|
|
894
|
+
"json.collapseNode": "Collapse JSON node",
|
|
895
|
+
"json.expandNode": "Expand JSON node",
|
|
896
|
+
"json.label": "JSON",
|
|
897
|
+
"markdown.footnotes": "Footnotes",
|
|
898
|
+
"markdown.truncatedCharacters": "… truncated at {total} characters",
|
|
899
|
+
"number.thousand": "{value}K",
|
|
900
|
+
"number.million": "{value}M"
|
|
869
901
|
};
|
|
870
902
|
//#endregion
|
|
871
903
|
//#region lib/types/locales/settings.js
|
|
@@ -959,7 +991,7 @@ window.__ModuleLoader__.load({
|
|
|
959
991
|
* @returns the store handle.
|
|
960
992
|
*/
|
|
961
993
|
function createLanguageRowStore() {
|
|
962
|
-
return (0,
|
|
994
|
+
return (0, _deepseek_ai_dsh_client_store.defineStore)({
|
|
963
995
|
init: () => ({
|
|
964
996
|
active: "",
|
|
965
997
|
options: [],
|
|
@@ -976,12 +1008,12 @@ window.__ModuleLoader__.load({
|
|
|
976
1008
|
//#endregion
|
|
977
1009
|
//#region lib/types/client/index.js
|
|
978
1010
|
/**
|
|
979
|
-
* English is both the locale the UI opens in when the browser names no
|
|
1011
|
+
* English is both the locale the UI opens in when the browser names no registered
|
|
980
1012
|
* language (and for non-browser runs), and the dictionary consulted after the
|
|
981
1013
|
* active locale misses a key. One constant serves both because the shipped
|
|
982
1014
|
* `zh`/`en` dictionaries carry identical key sets, so neither direction can
|
|
983
1015
|
* leave a key unresolved; the residual case points at English rather than
|
|
984
|
-
* zh because a browser naming
|
|
1016
|
+
* zh because a browser naming no registered language is the reader least
|
|
985
1017
|
* likely to read Chinese.
|
|
986
1018
|
*/
|
|
987
1019
|
const FALLBACK_LOCALE = "en";
|
|
@@ -989,55 +1021,65 @@ window.__ModuleLoader__.load({
|
|
|
989
1021
|
const COMMON_NS = "common";
|
|
990
1022
|
/** Namespace owning this feature's settings-row copy. */
|
|
991
1023
|
const SETTINGS_NS = "settings.locale";
|
|
992
|
-
/** The two shipped
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
label: "English"
|
|
999
|
-
}]);
|
|
1000
|
-
/**
|
|
1001
|
-
* `<html lang>` tag per shipped locale. The locale id is the app's own
|
|
1002
|
-
* vocabulary (primary subtag); the document attribute wants a BCP 47 tag,
|
|
1003
|
-
* which assistive technology and browser features (pronunciation rules,
|
|
1004
|
-
* translation offers, font fallback, spell check) read to pick their own
|
|
1005
|
-
* behavior. `zh` alone leaves the script ambiguous, so the shipped Chinese
|
|
1006
|
-
* copy names the variant it actually is.
|
|
1007
|
-
*/
|
|
1008
|
-
const DOCUMENT_LANGUAGE = {
|
|
1009
|
-
zh: "zh-CN",
|
|
1010
|
-
en: "en"
|
|
1024
|
+
/** The two locales and dictionaries shipped by this package. */
|
|
1025
|
+
const BUILT_IN_LOCALE_METADATA = {
|
|
1026
|
+
zh: {
|
|
1027
|
+
label: "中文",
|
|
1028
|
+
fallback: "en"
|
|
1029
|
+
},
|
|
1030
|
+
en: { label: "English" }
|
|
1011
1031
|
};
|
|
1032
|
+
const BUILT_IN_LOCALES = Object.freeze(LOCALE_IDS.map((id) => Object.freeze({
|
|
1033
|
+
id,
|
|
1034
|
+
...BUILT_IN_LOCALE_METADATA[id]
|
|
1035
|
+
})));
|
|
1036
|
+
/** Case-insensitive key for BCP 47-style ids. */
|
|
1037
|
+
function localeKey(value) {
|
|
1038
|
+
return value.toLowerCase();
|
|
1039
|
+
}
|
|
1040
|
+
/** Validate and detach a language-pack contribution from its mutable input. */
|
|
1041
|
+
function normalizeLanguage(input) {
|
|
1042
|
+
if (!LOCALE_ID_PATTERN.test(input.id)) throw new Error(`locale id "${input.id}" is not a BCP 47-style tag`);
|
|
1043
|
+
if (input.label.trim() === "") throw new Error("locale label must not be empty");
|
|
1044
|
+
if (!LOCALE_ID_PATTERN.test(input.fallback)) throw new Error(`locale fallback "${input.fallback}" is not a BCP 47-style tag`);
|
|
1045
|
+
return Object.freeze({
|
|
1046
|
+
id: input.id,
|
|
1047
|
+
label: input.label,
|
|
1048
|
+
fallback: input.fallback
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1012
1051
|
/**
|
|
1013
|
-
* Point `<html lang>` at the active locale
|
|
1014
|
-
*
|
|
1015
|
-
*
|
|
1016
|
-
* @param active - the active locale id.
|
|
1052
|
+
* Point `<html lang>` at the active locale, keeping the served document in
|
|
1053
|
+
* sync with locale snapshot changes.
|
|
1054
|
+
* @param snapshot - current locale state, including the active definition.
|
|
1017
1055
|
*/
|
|
1018
|
-
function syncDocumentLanguage(
|
|
1056
|
+
function syncDocumentLanguage(snapshot) {
|
|
1019
1057
|
if (typeof document === "undefined") return;
|
|
1020
|
-
document.documentElement.lang =
|
|
1058
|
+
document.documentElement.lang = snapshot.active === "zh" ? "zh-CN" : snapshot.active;
|
|
1021
1059
|
}
|
|
1022
1060
|
/**
|
|
1023
|
-
* Dictionary registry plus locale preference. Lookup
|
|
1024
|
-
*
|
|
1025
|
-
* the shared common namespace
|
|
1026
|
-
*
|
|
1027
|
-
*
|
|
1028
|
-
*
|
|
1029
|
-
* LocaleFace getSnapshot/subscribe pair
|
|
1030
|
-
*
|
|
1061
|
+
* Dictionary registry plus locale preference. Lookup walks the active
|
|
1062
|
+
* language's declared fallback chain in the entry namespace, then repeats it
|
|
1063
|
+
* in the shared common namespace before showing the key itself. Reads go
|
|
1064
|
+
* through {@link getLocale}; preferences change only through
|
|
1065
|
+
* {@link setLocale}, while language packs extend the catalog through
|
|
1066
|
+
* {@link addLanguage}. Continuous sync uses the `locale/change` event or
|
|
1067
|
+
* the LocaleFace getSnapshot/subscribe pair installed through
|
|
1068
|
+
* `ctx.slots.installLocale`.
|
|
1031
1069
|
*/
|
|
1032
1070
|
var LocaleRuntime = class {
|
|
1033
1071
|
dicts = /* @__PURE__ */ new Map();
|
|
1034
1072
|
bound = /* @__PURE__ */ new Map();
|
|
1073
|
+
catalog = /* @__PURE__ */ new Map();
|
|
1074
|
+
fallbackChains = /* @__PURE__ */ new Map();
|
|
1035
1075
|
snapshot;
|
|
1036
1076
|
listeners = /* @__PURE__ */ new Set();
|
|
1037
1077
|
ctx;
|
|
1038
1078
|
host;
|
|
1039
1079
|
/** Browser-derived locale standing wherever no explicit Host selection does. */
|
|
1040
1080
|
provisional;
|
|
1081
|
+
/** Last explicit selection, including one awaiting an external registration. */
|
|
1082
|
+
preference;
|
|
1041
1083
|
/**
|
|
1042
1084
|
* @param ctx - owning context (change events are emitted on it; the scope
|
|
1043
1085
|
* listener is released through ctx.effect on dispose).
|
|
@@ -1047,10 +1089,12 @@ window.__ModuleLoader__.load({
|
|
|
1047
1089
|
constructor(ctx, host) {
|
|
1048
1090
|
this.ctx = ctx;
|
|
1049
1091
|
this.host = host;
|
|
1050
|
-
this.
|
|
1092
|
+
for (const locale of BUILT_IN_LOCALES) this.catalog.set(localeKey(locale.id), locale);
|
|
1093
|
+
const locales = this.localeList();
|
|
1094
|
+
this.provisional = resolveInitialLocale(locales);
|
|
1051
1095
|
this.snapshot = Object.freeze({
|
|
1052
1096
|
active: this.provisional,
|
|
1053
|
-
locales
|
|
1097
|
+
locales,
|
|
1054
1098
|
revision: 0
|
|
1055
1099
|
});
|
|
1056
1100
|
if (host !== void 0) {
|
|
@@ -1078,7 +1122,7 @@ window.__ModuleLoader__.load({
|
|
|
1078
1122
|
/**
|
|
1079
1123
|
* LocaleFace subscribe: notified on every snapshot change (locale switch
|
|
1080
1124
|
* or dictionary registration — registrations bump the revision so already
|
|
1081
|
-
* rendered outlets pick up late-arriving dictionaries).
|
|
1125
|
+
* rendered outlets pick up late-arriving dictionaries and locale definitions).
|
|
1082
1126
|
* @param fn - change callback.
|
|
1083
1127
|
* @returns unsubscribe.
|
|
1084
1128
|
*/
|
|
@@ -1101,12 +1145,49 @@ window.__ModuleLoader__.load({
|
|
|
1101
1145
|
* @param id - a registered locale id; unknown ids throw.
|
|
1102
1146
|
*/
|
|
1103
1147
|
setLocale(id) {
|
|
1104
|
-
const match = this.
|
|
1148
|
+
const match = this.catalog.get(localeKey(id));
|
|
1105
1149
|
if (match === void 0) throw new Error(`locale "${id}" is not registered`);
|
|
1150
|
+
this.preference = match.id;
|
|
1106
1151
|
if (this.snapshot.active !== match.id) this.publish(match.id, true);
|
|
1107
1152
|
this.host?.set(LOCALE_PREFERENCE_FIELD, match.id);
|
|
1108
1153
|
}
|
|
1109
1154
|
/**
|
|
1155
|
+
* Add one selectable language to the shared catalog. Its fallback must
|
|
1156
|
+
* already be registered, and following fallback definitions must terminate
|
|
1157
|
+
* at English. Dictionaries may register before or after this definition.
|
|
1158
|
+
* Registration rechecks an unresolved Host preference and the browser's
|
|
1159
|
+
* ordered language list. The caller owns the returned disposer; removing an
|
|
1160
|
+
* active language falls back without clearing the stored id.
|
|
1161
|
+
* @param input - stable id, self-described label, and fallback language id.
|
|
1162
|
+
* @returns idempotent disposer removing this exact definition.
|
|
1163
|
+
* @throws when fields are malformed, the id is occupied, or the fallback
|
|
1164
|
+
* target is unknown or creates a cycle.
|
|
1165
|
+
*/
|
|
1166
|
+
addLanguage(input) {
|
|
1167
|
+
const candidate = normalizeLanguage(input);
|
|
1168
|
+
const key = localeKey(candidate.id);
|
|
1169
|
+
if (this.catalog.has(key)) throw new Error(`locale "${candidate.id}" is already registered`);
|
|
1170
|
+
const fallback = this.catalog.get(localeKey(candidate.fallback));
|
|
1171
|
+
if (fallback === void 0) throw new Error(`locale fallback "${candidate.fallback}" is not registered`);
|
|
1172
|
+
const language = Object.freeze({
|
|
1173
|
+
...candidate,
|
|
1174
|
+
fallback: fallback.id
|
|
1175
|
+
});
|
|
1176
|
+
this.catalog.set(key, language);
|
|
1177
|
+
try {
|
|
1178
|
+
this.assertFallbackChain(language.id);
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
this.catalog.delete(key);
|
|
1181
|
+
throw error;
|
|
1182
|
+
}
|
|
1183
|
+
this.publishCatalog();
|
|
1184
|
+
return () => {
|
|
1185
|
+
if (this.catalog.get(key) !== language) return;
|
|
1186
|
+
this.catalog.delete(key);
|
|
1187
|
+
this.publishCatalog();
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1110
1191
|
* Adopt the scope's accepted durable selection without writing it back; an
|
|
1111
1192
|
* absent selection returns to the browser-derived locale.
|
|
1112
1193
|
* @param host - the constructor-narrowed scope driving this adoption.
|
|
@@ -1114,19 +1195,74 @@ window.__ModuleLoader__.load({
|
|
|
1114
1195
|
adopt(host) {
|
|
1115
1196
|
const section = host.getSnapshot().value;
|
|
1116
1197
|
if (section === void 0) return;
|
|
1117
|
-
|
|
1198
|
+
this.preference = section.preference;
|
|
1199
|
+
const target = this.resolveActive();
|
|
1118
1200
|
if (this.snapshot.active === target) return;
|
|
1119
1201
|
this.publish(target, true);
|
|
1120
1202
|
}
|
|
1203
|
+
/** Recompute browser fallback and publish the current catalog. */
|
|
1204
|
+
publishCatalog() {
|
|
1205
|
+
this.fallbackChains.clear();
|
|
1206
|
+
const locales = this.localeList();
|
|
1207
|
+
this.provisional = resolveInitialLocale(locales);
|
|
1208
|
+
const active = this.resolveActive();
|
|
1209
|
+
this.publish(active, active !== this.snapshot.active, locales);
|
|
1210
|
+
}
|
|
1211
|
+
/** Resolve an explicit preference only while its definition is available. */
|
|
1212
|
+
resolveActive() {
|
|
1213
|
+
if (this.preference === void 0) return this.provisional;
|
|
1214
|
+
return this.catalog.get(localeKey(this.preference))?.id ?? this.provisional;
|
|
1215
|
+
}
|
|
1216
|
+
/** Snapshot the catalog in registration order. */
|
|
1217
|
+
localeList() {
|
|
1218
|
+
return Object.freeze([...this.catalog.values()]);
|
|
1219
|
+
}
|
|
1220
|
+
/** Fail a new definition whose complete fallback path does not reach English. */
|
|
1221
|
+
assertFallbackChain(start) {
|
|
1222
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1223
|
+
let current = this.catalog.get(localeKey(start));
|
|
1224
|
+
while (current !== void 0) {
|
|
1225
|
+
const key = localeKey(current.id);
|
|
1226
|
+
if (seen.has(key)) throw new Error(`locale fallback cycle includes "${current.id}"`);
|
|
1227
|
+
seen.add(key);
|
|
1228
|
+
if (key === localeKey("en")) return;
|
|
1229
|
+
/* v8 ignore next -- English is the only built-in terminal and every
|
|
1230
|
+
* language accepted by addLanguage has a required fallback. */
|
|
1231
|
+
if (current.fallback === void 0) throw new Error(`locale "${current.id}" fallback chain does not reach "en"`);
|
|
1232
|
+
const next = this.catalog.get(localeKey(current.fallback));
|
|
1233
|
+
if (next === void 0) throw new Error(`locale fallback "${current.fallback}" is not registered`);
|
|
1234
|
+
current = next;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
/** Resolve a lookup chain, falling directly to English across an unload gap. */
|
|
1238
|
+
fallbackChain(start) {
|
|
1239
|
+
const startKey = localeKey(start);
|
|
1240
|
+
const cached = this.fallbackChains.get(startKey);
|
|
1241
|
+
if (cached !== void 0) return cached;
|
|
1242
|
+
const chain = [];
|
|
1243
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1244
|
+
let current = this.catalog.get(startKey);
|
|
1245
|
+
while (current !== void 0 && !seen.has(localeKey(current.id))) {
|
|
1246
|
+
const key = localeKey(current.id);
|
|
1247
|
+
seen.add(key);
|
|
1248
|
+
chain.push(current.id);
|
|
1249
|
+
current = current.fallback === void 0 ? void 0 : this.catalog.get(localeKey(current.fallback));
|
|
1250
|
+
}
|
|
1251
|
+
if (!seen.has(localeKey("en"))) chain.push("en");
|
|
1252
|
+
const resolved = Object.freeze(chain);
|
|
1253
|
+
this.fallbackChains.set(startKey, resolved);
|
|
1254
|
+
return resolved;
|
|
1255
|
+
}
|
|
1121
1256
|
register(ns, localeOrDicts, dict) {
|
|
1122
1257
|
const pairs = typeof localeOrDicts === "string" ? [[localeOrDicts, dict]] : Object.entries(localeOrDicts);
|
|
1258
|
+
for (const [locale] of pairs) if (!LOCALE_ID_PATTERN.test(locale)) throw new Error(`locale id "${locale}" is not a BCP 47-style tag`);
|
|
1123
1259
|
let locales = this.dicts.get(ns);
|
|
1124
1260
|
if (!locales) {
|
|
1125
1261
|
locales = /* @__PURE__ */ new Map();
|
|
1126
1262
|
this.dicts.set(ns, locales);
|
|
1127
1263
|
}
|
|
1128
|
-
for (const [locale] of pairs) if (locales.has(locale)) throw new Error(`locale namespace "${ns}" already has locale "${locale}"`);
|
|
1129
|
-
for (const [locale, entries] of pairs) locales.set(locale, entries);
|
|
1264
|
+
for (const [locale] of pairs) if (locales.has(localeKey(locale))) throw new Error(`locale namespace "${ns}" already has locale "${locale}"`);
|
|
1265
|
+
for (const [locale, entries] of pairs) locales.set(localeKey(locale), entries);
|
|
1130
1266
|
this.publish(this.snapshot.active, false);
|
|
1131
1267
|
return () => {
|
|
1132
1268
|
const owner = this.dicts.get(ns);
|
|
@@ -1134,9 +1270,12 @@ window.__ModuleLoader__.load({
|
|
|
1134
1270
|
* first register and never removed, so the disposer always finds it. */
|
|
1135
1271
|
if (!owner) return;
|
|
1136
1272
|
let removed = false;
|
|
1137
|
-
for (const [locale, entries] of pairs)
|
|
1138
|
-
|
|
1139
|
-
|
|
1273
|
+
for (const [locale, entries] of pairs) {
|
|
1274
|
+
const key = localeKey(locale);
|
|
1275
|
+
if (owner.get(key) === entries) {
|
|
1276
|
+
owner.delete(key);
|
|
1277
|
+
removed = true;
|
|
1278
|
+
}
|
|
1140
1279
|
}
|
|
1141
1280
|
if (removed) this.publish(this.snapshot.active, false);
|
|
1142
1281
|
};
|
|
@@ -1151,13 +1290,17 @@ window.__ModuleLoader__.load({
|
|
|
1151
1290
|
return t;
|
|
1152
1291
|
}
|
|
1153
1292
|
translate(ns, key, params) {
|
|
1154
|
-
const
|
|
1293
|
+
const chain = this.fallbackChain(this.snapshot.active);
|
|
1294
|
+
const template = this.lookup(ns, key, chain) ?? (ns !== "common" ? this.lookup("common", key, chain) : void 0) ?? key;
|
|
1155
1295
|
if (!params) return template;
|
|
1156
1296
|
return template.replace(/\{(\w+)\}/g, (match, name) => name in params ? String(params[name]) : match);
|
|
1157
1297
|
}
|
|
1158
|
-
lookup(ns, key) {
|
|
1298
|
+
lookup(ns, key, chain) {
|
|
1159
1299
|
const locales = this.dicts.get(ns);
|
|
1160
|
-
|
|
1300
|
+
for (const locale of chain) {
|
|
1301
|
+
const value = locales?.get(localeKey(locale))?.[key];
|
|
1302
|
+
if (value !== void 0) return value;
|
|
1303
|
+
}
|
|
1161
1304
|
}
|
|
1162
1305
|
/**
|
|
1163
1306
|
* Advance the snapshot revision and notify LocaleFace subscribers (render
|
|
@@ -1166,10 +1309,10 @@ window.__ModuleLoader__.load({
|
|
|
1166
1309
|
* registration-heavy boot cannot storm event listeners (which may
|
|
1167
1310
|
* re-register slots in response).
|
|
1168
1311
|
*/
|
|
1169
|
-
publish(active, localeChanged) {
|
|
1312
|
+
publish(active, localeChanged, locales = this.snapshot.locales) {
|
|
1170
1313
|
this.snapshot = Object.freeze({
|
|
1171
1314
|
active,
|
|
1172
|
-
locales
|
|
1315
|
+
locales,
|
|
1173
1316
|
revision: this.snapshot.revision + 1
|
|
1174
1317
|
});
|
|
1175
1318
|
if (localeChanged) this.ctx.emit("locale/change", this.snapshot);
|
|
@@ -1184,30 +1327,35 @@ window.__ModuleLoader__.load({
|
|
|
1184
1327
|
* The browser's own language wins over {@link FALLBACK_LOCALE}; an explicit
|
|
1185
1328
|
* Host preference may replace this provisional value after plugin activation.
|
|
1186
1329
|
*/
|
|
1187
|
-
function resolveInitialLocale() {
|
|
1188
|
-
return detectBrowserLocale() ?? "en";
|
|
1330
|
+
function resolveInitialLocale(locales) {
|
|
1331
|
+
return detectBrowserLocale(locales) ?? "en";
|
|
1189
1332
|
}
|
|
1190
1333
|
/**
|
|
1191
|
-
* The first
|
|
1192
|
-
*
|
|
1193
|
-
*
|
|
1194
|
-
*
|
|
1195
|
-
*
|
|
1196
|
-
*
|
|
1197
|
-
*
|
|
1334
|
+
* The first registered locale the browser asks for. Each browser tag first
|
|
1335
|
+
* matches a locale id exactly, then its primary subtag, so an exact regional
|
|
1336
|
+
* registration wins before a language-wide fallback.
|
|
1337
|
+
* `window` is the browser test, not `navigator`: Node exposes a global
|
|
1338
|
+
* `navigator` reporting the machine's own language, which must not decide the
|
|
1339
|
+
* locale for non-browser runs. `navigator.language` trails the ordered
|
|
1340
|
+
* `languages` list and covers hosts exposing only the single tag.
|
|
1341
|
+
* @param locales - definitions currently available to the browser.
|
|
1342
|
+
* @returns the first matching locale id, or undefined.
|
|
1198
1343
|
*/
|
|
1199
|
-
function detectBrowserLocale() {
|
|
1344
|
+
function detectBrowserLocale(locales) {
|
|
1200
1345
|
if (typeof window === "undefined") return void 0;
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
const
|
|
1204
|
-
|
|
1346
|
+
const languages = navigator.languages;
|
|
1347
|
+
for (const tag of [...languages ?? [], navigator.language]) {
|
|
1348
|
+
const requested = localeKey(tag);
|
|
1349
|
+
const exact = locales.find((locale) => localeKey(locale.id) === requested);
|
|
1350
|
+
if (exact !== void 0) return exact.id;
|
|
1351
|
+
const primary = requested.split("-")[0];
|
|
1352
|
+
const match = locales.find((locale) => localeKey(locale.id).split("-")[0] === primary);
|
|
1353
|
+
if (match !== void 0) return match.id;
|
|
1205
1354
|
}
|
|
1206
1355
|
}
|
|
1207
1356
|
/** Required services: slot registration plus the settings transport. */
|
|
1208
1357
|
const inject = [
|
|
1209
1358
|
"slots",
|
|
1210
|
-
"connection",
|
|
1211
1359
|
"remote",
|
|
1212
1360
|
"settingsScope"
|
|
1213
1361
|
];
|
|
@@ -1231,18 +1379,19 @@ window.__ModuleLoader__.load({
|
|
|
1231
1379
|
ctx.slots.installLocale(locale);
|
|
1232
1380
|
const store = createLanguageRowStore();
|
|
1233
1381
|
let bound;
|
|
1234
|
-
const sync = (
|
|
1235
|
-
|
|
1382
|
+
const sync = () => {
|
|
1383
|
+
const snapshot = locale.getSnapshot();
|
|
1384
|
+
syncDocumentLanguage(snapshot);
|
|
1236
1385
|
bound?.sync(snapshot.active, snapshot.locales.map((l) => ({
|
|
1237
1386
|
id: l.id,
|
|
1238
1387
|
label: l.label
|
|
1239
1388
|
})), snapshot.revision);
|
|
1240
1389
|
};
|
|
1241
|
-
ctx.
|
|
1242
|
-
|
|
1390
|
+
ctx.effect(() => locale.subscribe(sync), "locale: language row and document synchronization");
|
|
1391
|
+
sync();
|
|
1243
1392
|
const injected = (actions) => {
|
|
1244
1393
|
bound = actions;
|
|
1245
|
-
sync(
|
|
1394
|
+
sync();
|
|
1246
1395
|
return { setLocale: (id) => {
|
|
1247
1396
|
locale.setLocale(id);
|
|
1248
1397
|
} };
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
2
1
|
import z from "@deepseek-ai/schemastery";
|
|
3
2
|
//#region lib/types/locale-settings.js
|
|
4
3
|
/** Locale preference stored in the Host user-settings document. */
|
|
@@ -6,10 +5,12 @@ import z from "@deepseek-ai/schemastery";
|
|
|
6
5
|
const LOCALE_SETTINGS_NAMESPACE = "locale";
|
|
7
6
|
/** Field carrying an explicit locale selection; absence delegates to the browser. */
|
|
8
7
|
const LOCALE_PREFERENCE_FIELD = "preference";
|
|
8
|
+
/** Accepted BCP 47-style language ids. */
|
|
9
|
+
const LOCALE_ID_PATTERN = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/u;
|
|
9
10
|
/** Locale identifiers shipped by the browser client. */
|
|
10
11
|
const LOCALE_IDS = ["zh", "en"];
|
|
11
12
|
/** Durable locale schema; also the wire envelope the browser scope validates against. */
|
|
12
|
-
const LocaleSettingsSchema = z.object({ [LOCALE_PREFERENCE_FIELD]: z.
|
|
13
|
+
const LocaleSettingsSchema = z.object({ [LOCALE_PREFERENCE_FIELD]: z.string().pattern(LOCALE_ID_PATTERN).required(false) });
|
|
13
14
|
//#endregion
|
|
14
15
|
//#region lib/types/index.js
|
|
15
16
|
/** Host registration for the browser locale preference. */
|
|
@@ -19,7 +20,7 @@ const LocaleSettingsSchema = z.object({ [LOCALE_PREFERENCE_FIELD]: z.union([...L
|
|
|
19
20
|
*/
|
|
20
21
|
function apply(ctx) {
|
|
21
22
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
22
|
-
settingsCtx.settings.register(
|
|
23
|
+
settingsCtx.settings.register(LOCALE_SETTINGS_NAMESPACE, LocaleSettingsSchema);
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
//#endregion
|
package/lib/invariant.js
CHANGED
|
@@ -9,10 +9,9 @@ const name = "client-locale-invariant";
|
|
|
9
9
|
/** Service required before the companion can reserve package ownership. */
|
|
10
10
|
const inject = ["invariants"];
|
|
11
11
|
/**
|
|
12
|
-
* No runtime invariant:
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* asserted directly by this package's behavior specs.
|
|
12
|
+
* No runtime invariant: the locale catalog and dictionaries have no
|
|
13
|
+
* independent runtime source to compare against; registration disposal,
|
|
14
|
+
* preference resolution, and fallback lookup are asserted by behavior specs.
|
|
16
15
|
*/
|
|
17
16
|
const install = () => {};
|
|
18
17
|
/**
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
* preference row into the settings General section — the locale feature owns
|
|
5
5
|
* its own settings surface.
|
|
6
6
|
*/
|
|
7
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
8
8
|
import { type LocaleDictOf, type LocaleNamespaceMap, type Translate, type TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
|
|
9
|
-
import type {
|
|
10
|
-
import { type LocaleId, type LocaleSettings } from '../locale-settings.ts';
|
|
9
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
10
|
+
import { type BuiltInLocaleId, type LocaleId, type LocaleSettings } from '../locale-settings.ts';
|
|
11
11
|
import { type CommonKey } from '../locales/index.ts';
|
|
12
12
|
import { type SettingsLocaleKey } from '../locales/settings.ts';
|
|
13
13
|
export type { LanguageRowComponentProps, LanguageRowInjected } from './LanguageRow.tsx';
|
|
14
14
|
export type { LanguageOptionRow, LanguageRowState } from './settings-store.ts';
|
|
15
15
|
export type { CommonKey } from '../locales/index.ts';
|
|
16
|
-
export type { LocaleId, LocaleSettings } from '../locale-settings.ts';
|
|
16
|
+
export type { BuiltInLocaleId, LocaleId, LocaleSettings } from '../locale-settings.ts';
|
|
17
17
|
export type { Translate, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
|
|
18
18
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
19
19
|
interface LocaleNamespaceMap {
|
|
@@ -25,12 +25,23 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
25
25
|
}
|
|
26
26
|
/** Locale dictionary: flat key to template string ({name} placeholders). */
|
|
27
27
|
export type LocaleDict = Record<string, string>;
|
|
28
|
-
/**
|
|
29
|
-
export interface
|
|
30
|
-
/**
|
|
28
|
+
/** Input accepted when a language-pack plugin adds a selectable language. */
|
|
29
|
+
export interface LanguageRegistration {
|
|
30
|
+
/** Stable BCP 47-style id stored as the locale preference. */
|
|
31
31
|
id: LocaleId;
|
|
32
|
-
/** Display name in
|
|
32
|
+
/** Display name written in the represented language. */
|
|
33
33
|
label: string;
|
|
34
|
+
/** Registered language consulted when this language lacks a dictionary key. */
|
|
35
|
+
fallback: LocaleId;
|
|
36
|
+
}
|
|
37
|
+
/** One normalized selectable locale published in snapshots. */
|
|
38
|
+
export interface LocaleDefinition {
|
|
39
|
+
/** Stable id persisted by {@link LocaleRuntime.setLocale}. */
|
|
40
|
+
readonly id: LocaleId;
|
|
41
|
+
/** Display name written in the represented language. */
|
|
42
|
+
readonly label: string;
|
|
43
|
+
/** Next language in the per-key fallback chain; absent only for English. */
|
|
44
|
+
readonly fallback?: LocaleId;
|
|
34
45
|
}
|
|
35
46
|
/** Immutable locale state published on every change. */
|
|
36
47
|
export interface LocaleSnapshot {
|
|
@@ -58,45 +69,49 @@ declare module '@deepseek-ai/cordis' {
|
|
|
58
69
|
}
|
|
59
70
|
}
|
|
60
71
|
/**
|
|
61
|
-
* English is both the locale the UI opens in when the browser names no
|
|
72
|
+
* English is both the locale the UI opens in when the browser names no registered
|
|
62
73
|
* language (and for non-browser runs), and the dictionary consulted after the
|
|
63
74
|
* active locale misses a key. One constant serves both because the shipped
|
|
64
75
|
* `zh`/`en` dictionaries carry identical key sets, so neither direction can
|
|
65
76
|
* leave a key unresolved; the residual case points at English rather than
|
|
66
|
-
* zh because a browser naming
|
|
77
|
+
* zh because a browser naming no registered language is the reader least
|
|
67
78
|
* likely to read Chinese.
|
|
68
79
|
*/
|
|
69
|
-
export declare const FALLBACK_LOCALE:
|
|
80
|
+
export declare const FALLBACK_LOCALE: BuiltInLocaleId;
|
|
70
81
|
/** Shared namespace for shell-level texts. */
|
|
71
82
|
export declare const COMMON_NS = "common";
|
|
72
83
|
/** Namespace owning this feature's settings-row copy. */
|
|
73
84
|
export declare const SETTINGS_NS = "settings.locale";
|
|
74
85
|
/**
|
|
75
|
-
* Dictionary registry plus locale preference. Lookup
|
|
76
|
-
*
|
|
77
|
-
* the shared common namespace
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* LocaleFace getSnapshot/subscribe pair
|
|
82
|
-
*
|
|
86
|
+
* Dictionary registry plus locale preference. Lookup walks the active
|
|
87
|
+
* language's declared fallback chain in the entry namespace, then repeats it
|
|
88
|
+
* in the shared common namespace before showing the key itself. Reads go
|
|
89
|
+
* through {@link getLocale}; preferences change only through
|
|
90
|
+
* {@link setLocale}, while language packs extend the catalog through
|
|
91
|
+
* {@link addLanguage}. Continuous sync uses the `locale/change` event or
|
|
92
|
+
* the LocaleFace getSnapshot/subscribe pair installed through
|
|
93
|
+
* `ctx.slots.installLocale`.
|
|
83
94
|
*/
|
|
84
95
|
export declare class LocaleRuntime {
|
|
85
96
|
private dicts;
|
|
86
97
|
private bound;
|
|
98
|
+
private catalog;
|
|
99
|
+
private fallbackChains;
|
|
87
100
|
private snapshot;
|
|
88
101
|
private listeners;
|
|
89
102
|
private readonly ctx;
|
|
90
103
|
private readonly host;
|
|
91
104
|
/** Browser-derived locale standing wherever no explicit Host selection does. */
|
|
92
|
-
private
|
|
105
|
+
private provisional;
|
|
106
|
+
/** Last explicit selection, including one awaiting an external registration. */
|
|
107
|
+
private preference;
|
|
93
108
|
/**
|
|
94
109
|
* @param ctx - owning context (change events are emitted on it; the scope
|
|
95
110
|
* listener is released through ctx.effect on dispose).
|
|
96
111
|
* @param host - durable preference scope owned by the providing plugin;
|
|
97
112
|
* absent compositions (standalone dictionary registries) stay process-local.
|
|
98
113
|
*/
|
|
99
|
-
constructor(ctx:
|
|
114
|
+
constructor(ctx: ClientContext, host?: SettingsScope<LocaleSettings>);
|
|
100
115
|
/**
|
|
101
116
|
* Read the current immutable locale snapshot.
|
|
102
117
|
* @returns the current snapshot (stable reference until the next change).
|
|
@@ -111,7 +126,7 @@ export declare class LocaleRuntime {
|
|
|
111
126
|
/**
|
|
112
127
|
* LocaleFace subscribe: notified on every snapshot change (locale switch
|
|
113
128
|
* or dictionary registration — registrations bump the revision so already
|
|
114
|
-
* rendered outlets pick up late-arriving dictionaries).
|
|
129
|
+
* rendered outlets pick up late-arriving dictionaries and locale definitions).
|
|
115
130
|
* @param fn - change callback.
|
|
116
131
|
* @returns unsubscribe.
|
|
117
132
|
*/
|
|
@@ -129,12 +144,35 @@ export declare class LocaleRuntime {
|
|
|
129
144
|
* @param id - a registered locale id; unknown ids throw.
|
|
130
145
|
*/
|
|
131
146
|
setLocale(id: string): void;
|
|
147
|
+
/**
|
|
148
|
+
* Add one selectable language to the shared catalog. Its fallback must
|
|
149
|
+
* already be registered, and following fallback definitions must terminate
|
|
150
|
+
* at English. Dictionaries may register before or after this definition.
|
|
151
|
+
* Registration rechecks an unresolved Host preference and the browser's
|
|
152
|
+
* ordered language list. The caller owns the returned disposer; removing an
|
|
153
|
+
* active language falls back without clearing the stored id.
|
|
154
|
+
* @param input - stable id, self-described label, and fallback language id.
|
|
155
|
+
* @returns idempotent disposer removing this exact definition.
|
|
156
|
+
* @throws when fields are malformed, the id is occupied, or the fallback
|
|
157
|
+
* target is unknown or creates a cycle.
|
|
158
|
+
*/
|
|
159
|
+
addLanguage(input: LanguageRegistration): () => void;
|
|
132
160
|
/**
|
|
133
161
|
* Adopt the scope's accepted durable selection without writing it back; an
|
|
134
162
|
* absent selection returns to the browser-derived locale.
|
|
135
163
|
* @param host - the constructor-narrowed scope driving this adoption.
|
|
136
164
|
*/
|
|
137
165
|
private adopt;
|
|
166
|
+
/** Recompute browser fallback and publish the current catalog. */
|
|
167
|
+
private publishCatalog;
|
|
168
|
+
/** Resolve an explicit preference only while its definition is available. */
|
|
169
|
+
private resolveActive;
|
|
170
|
+
/** Snapshot the catalog in registration order. */
|
|
171
|
+
private localeList;
|
|
172
|
+
/** Fail a new definition whose complete fallback path does not reach English. */
|
|
173
|
+
private assertFallbackChain;
|
|
174
|
+
/** Resolve a lookup chain, falling directly to English across an unload gap. */
|
|
175
|
+
private fallbackChain;
|
|
138
176
|
/**
|
|
139
177
|
* Register a declared namespace's dictionaries, all locales in one call —
|
|
140
178
|
* the typed form: each dictionary is checked against the namespace's
|
|
@@ -144,17 +182,18 @@ export declare class LocaleRuntime {
|
|
|
144
182
|
* namespace's texts have one owner). Registration bumps the revision so
|
|
145
183
|
* mounted outlets pick up late-arriving dictionaries.
|
|
146
184
|
* @param ns - a namespace merged into LocaleNamespaceMap.
|
|
147
|
-
* @param dicts - complete dictionaries keyed by locale id.
|
|
185
|
+
* @param dicts - complete dictionaries keyed by built-in locale id.
|
|
148
186
|
* @returns disposer removing every locale registered by this call (idempotent).
|
|
149
187
|
*/
|
|
150
|
-
register<N extends keyof LocaleNamespaceMap
|
|
188
|
+
register<N extends Extract<keyof LocaleNamespaceMap, string>>(ns: N, dicts: Record<BuiltInLocaleId, LocaleDictOf<N>>): () => void;
|
|
151
189
|
/**
|
|
152
|
-
* Single-locale untyped form for
|
|
153
|
-
*
|
|
190
|
+
* Single-locale untyped form for language-pack contributions and namespaces
|
|
191
|
+
* outside the merge table.
|
|
154
192
|
* @param ns - namespace.
|
|
155
193
|
* @param locale - locale tag.
|
|
156
194
|
* @param dict - dictionary.
|
|
157
195
|
* @returns disposer (idempotent).
|
|
196
|
+
* @throws when locale is not a BCP 47-style tag.
|
|
158
197
|
*/
|
|
159
198
|
register(ns: string, locale: string, dict: LocaleDict): () => void;
|
|
160
199
|
/**
|
|
@@ -166,7 +205,7 @@ export declare class LocaleRuntime {
|
|
|
166
205
|
* @param ns - a namespace merged into LocaleNamespaceMap.
|
|
167
206
|
* @returns the typed translate function (reads the active locale at call time).
|
|
168
207
|
*/
|
|
169
|
-
bind<N extends keyof LocaleNamespaceMap
|
|
208
|
+
bind<N extends Extract<keyof LocaleNamespaceMap, string>>(ns: N): TranslateNS<N>;
|
|
170
209
|
/**
|
|
171
210
|
* Untyped form for namespaces outside the merge table (dynamic
|
|
172
211
|
* composition, tests).
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* plugin's apply-world change listener is the only writer; the row component
|
|
4
4
|
* reads via props.useStore.
|
|
5
5
|
*/
|
|
6
|
-
import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-
|
|
6
|
+
import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-store';
|
|
7
7
|
/** One selectable locale row (id + self-described label). */
|
|
8
8
|
export interface LanguageOptionRow {
|
|
9
9
|
/** Locale id (the setLocale argument). */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Host registration for the browser locale preference. */
|
|
2
2
|
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
-
export { LOCALE_IDS, LOCALE_PREFERENCE_FIELD, LOCALE_SETTINGS_NAMESPACE, type LocaleId, type LocaleSettings, } from './locale-settings.ts';
|
|
3
|
+
export { LOCALE_IDS, LOCALE_PREFERENCE_FIELD, LOCALE_SETTINGS_NAMESPACE, type BuiltInLocaleId, type LocaleId, type LocaleSettings, } from './locale-settings.ts';
|
|
4
4
|
/**
|
|
5
5
|
* Register the durable locale section when a settings provider exists.
|
|
6
6
|
* @param ctx - Host context whose optional settings service owns the section.
|
|
@@ -4,10 +4,14 @@ import z from '@deepseek-ai/schemastery';
|
|
|
4
4
|
export declare const LOCALE_SETTINGS_NAMESPACE = "locale";
|
|
5
5
|
/** Field carrying an explicit locale selection; absence delegates to the browser. */
|
|
6
6
|
export declare const LOCALE_PREFERENCE_FIELD = "preference";
|
|
7
|
+
/** Accepted BCP 47-style language ids. */
|
|
8
|
+
export declare const LOCALE_ID_PATTERN: RegExp;
|
|
7
9
|
/** Locale identifiers shipped by the browser client. */
|
|
8
10
|
export declare const LOCALE_IDS: readonly ["zh", "en"];
|
|
9
|
-
/**
|
|
10
|
-
export type
|
|
11
|
+
/** Locale identifier shipped by the browser client. */
|
|
12
|
+
export type BuiltInLocaleId = typeof LOCALE_IDS[number];
|
|
13
|
+
/** Open locale identifier accepted from language-pack plugins. */
|
|
14
|
+
export type LocaleId = string;
|
|
11
15
|
/** Durable locale section shared by the Host schema and the browser scope. */
|
|
12
16
|
export interface LocaleSettings {
|
|
13
17
|
/** Explicit locale selection; absence delegates to the browser. */
|
|
@@ -5,6 +5,13 @@ export declare const en: {
|
|
|
5
5
|
close: string;
|
|
6
6
|
copy: string;
|
|
7
7
|
copied: string;
|
|
8
|
+
'copy.failed': string;
|
|
9
|
+
'copy.value': string;
|
|
10
|
+
'copy.json': string;
|
|
11
|
+
'copy.path': string;
|
|
12
|
+
'copy.prettyJson': string;
|
|
13
|
+
'copy.compactJson': string;
|
|
14
|
+
'copy.optionsHint': string;
|
|
8
15
|
retry: string;
|
|
9
16
|
loading: string;
|
|
10
17
|
'load.failed': string;
|
|
@@ -21,8 +28,16 @@ export declare const en: {
|
|
|
21
28
|
collapse: string;
|
|
22
29
|
expand: string;
|
|
23
30
|
back: string;
|
|
31
|
+
'brand.localBuild': string;
|
|
24
32
|
unknown: string;
|
|
25
33
|
none: string;
|
|
26
34
|
truncated: string;
|
|
35
|
+
'json.collapseNode': string;
|
|
36
|
+
'json.expandNode': string;
|
|
37
|
+
'json.label': string;
|
|
38
|
+
'markdown.footnotes': string;
|
|
39
|
+
'markdown.truncatedCharacters': string;
|
|
40
|
+
'number.thousand': string;
|
|
41
|
+
'number.million': string;
|
|
27
42
|
};
|
|
28
43
|
//# sourceMappingURL=en.d.ts.map
|
|
@@ -5,6 +5,13 @@ export declare const zh: {
|
|
|
5
5
|
close: string;
|
|
6
6
|
copy: string;
|
|
7
7
|
copied: string;
|
|
8
|
+
'copy.failed': string;
|
|
9
|
+
'copy.value': string;
|
|
10
|
+
'copy.json': string;
|
|
11
|
+
'copy.path': string;
|
|
12
|
+
'copy.prettyJson': string;
|
|
13
|
+
'copy.compactJson': string;
|
|
14
|
+
'copy.optionsHint': string;
|
|
8
15
|
retry: string;
|
|
9
16
|
loading: string;
|
|
10
17
|
'load.failed': string;
|
|
@@ -21,9 +28,17 @@ export declare const zh: {
|
|
|
21
28
|
collapse: string;
|
|
22
29
|
expand: string;
|
|
23
30
|
back: string;
|
|
31
|
+
'brand.localBuild': string;
|
|
24
32
|
unknown: string;
|
|
25
33
|
none: string;
|
|
26
34
|
truncated: string;
|
|
35
|
+
'json.collapseNode': string;
|
|
36
|
+
'json.expandNode': string;
|
|
37
|
+
'json.label': string;
|
|
38
|
+
'markdown.footnotes': string;
|
|
39
|
+
'markdown.truncatedCharacters': string;
|
|
40
|
+
'number.thousand': string;
|
|
41
|
+
'number.million': string;
|
|
27
42
|
};
|
|
28
43
|
/** The common vocabulary key union (zh is the key-set source of truth). */
|
|
29
44
|
export type CommonKey = keyof typeof zh;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-locale",
|
|
3
|
-
"description": "Locale plugin: Host-backed
|
|
4
|
-
"version": "0.1.
|
|
3
|
+
"description": "Locale plugin: Host-backed preference, extensible language catalog, browser fallback, and typed built-in dictionaries",
|
|
4
|
+
"version": "0.1.2-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"client": {
|
|
34
34
|
"inject": [
|
|
35
35
|
"@deepseek-ai/dsh-client-connection",
|
|
36
|
-
"@deepseek-ai/dsh-client-
|
|
36
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
37
37
|
"@deepseek-ai/dsh-client-ui-settings",
|
|
38
38
|
"@deepseek-ai/dsh-api-remotes"
|
|
39
39
|
],
|
|
@@ -43,30 +43,25 @@
|
|
|
43
43
|
},
|
|
44
44
|
"license": "MIT",
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
47
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.1-rc.2",
|
|
48
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
|
|
49
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
|
|
50
|
-
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
51
|
-
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
52
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.1-rc.2"
|
|
46
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
53
47
|
},
|
|
54
48
|
"devDependencies": {
|
|
55
49
|
"@types/react": "~18.3.1",
|
|
56
50
|
"react": "^18.2.0",
|
|
57
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
58
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.
|
|
59
|
-
"@deepseek-ai/dsh-client-
|
|
60
|
-
"@deepseek-ai/dsh-client-test-runtime": "^0.1.
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-
|
|
63
|
-
"@deepseek-ai/dsh-
|
|
64
|
-
"@deepseek-ai/dsh-
|
|
65
|
-
"@deepseek-ai/dsh-
|
|
66
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.
|
|
51
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
52
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-store": "^0.1.2-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.2-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.2"
|
|
67
62
|
},
|
|
68
63
|
"dependencies": {
|
|
69
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
64
|
+
"@deepseek-ai/schemastery": "^3.18.2"
|
|
70
65
|
},
|
|
71
66
|
"files": [
|
|
72
67
|
"lib/index.js",
|