@deepseek-ai/dsh-client-locale 0.1.6-alpha.2 → 0.1.7-alpha.1
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 +9 -1
- package/README.zh.md +9 -1
- package/lib/client.js +225 -75
- package/lib/index.js +9 -8
- package/lib/types/client/bootstrap.d.ts +22 -0
- package/lib/types/client/index.d.ts +15 -3
- package/lib/types/index.d.ts +15 -5
- package/lib/types/locale-settings.d.ts +9 -1
- package/package.json +14 -13
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: a21f61dd931204f990e6865419ac19476c957ff4
|
|
6
|
+
README.zh.md: c93b555f96e459ddad56faaf46d267cf319218ad
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
Use `dsh-client-locale` to switch the web GUI between the shipped English and Chinese locales or languages added by client plugins. User selections take effect immediately; loopback pages persist them in `$DSH_HOME/
|
|
12
|
+
Use `dsh-client-locale` to switch the web GUI between the shipped English and Chinese locales or languages added by client plugins. User selections take effect immediately; loopback pages persist them in `$DSH_HOME/cordis.patch.yml`, while non-loopback pages keep them only for the current process. New browsers use the first supported language requested by the browser until an allowed stored preference arrives. Plugin authors add typed namespace dictionaries and translate through the public locale API; slot-rendered copy updates without a reload.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -31,10 +31,16 @@ Use it wherever the web GUI needs a language switch or translated copy: the ship
|
|
|
31
31
|
|
|
32
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
33
|
|
|
34
|
+
Native shells may provide `__DSH_LOCALE__` with an asynchronous `read()` and an `onChange(locale)` callback. Initialization supplies the current Host preference and ordered OS languages before the Client mounts. Automatic selection stays provisional; only Settings selections write `locale.preference`. A fresh read on each page load prevents a stale preload preference after reload. Ordinary browsers keep navigator-based detection and their existing settings-scope policy.
|
|
35
|
+
|
|
34
36
|
### Registering a dictionary
|
|
35
37
|
|
|
36
38
|
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
39
|
|
|
40
|
+
### Resolving package text
|
|
41
|
+
|
|
42
|
+
Use `ctx.locale.resolveText(text)` for [`LocalizedText`](../../util/package-manifest/README.md), such as installed plugin titles and descriptions. Literal strings are returned unchanged. Translation maps use lowercase language ids, require an `en` fallback, and follow the active language's declared fallback chain. They do not consult or register namespace dictionaries.
|
|
43
|
+
|
|
38
44
|
### Registering a language pack
|
|
39
45
|
|
|
40
46
|
An external client plugin registers the language definition and each translated namespace as owned effects; definitions and dictionaries may register in either order:
|
|
@@ -83,6 +89,8 @@ The provisional locale comes from the browser (`navigator.languages` matched by
|
|
|
83
89
|
|
|
84
90
|
### Dictionary lookup
|
|
85
91
|
|
|
92
|
+
Document language synchronization writes `<html lang>` only when its value changes; dictionary-only revisions leave the attribute untouched.
|
|
93
|
+
|
|
86
94
|
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
95
|
|
|
88
96
|
### Source map
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
使用 `dsh-client-locale` 可在 web GUI 中切换内置的英文和中文 locale,或 client 插件添加的语言。用户选择会立即生效;loopback 页面把选择持久化到 `$DSH_HOME/
|
|
12
|
+
使用 `dsh-client-locale` 可在 web GUI 中切换内置的英文和中文 locale,或 client 插件添加的语言。用户选择会立即生效;loopback 页面把选择持久化到 `$DSH_HOME/cordis.patch.yml`,非 loopback 页面则只为当前进程保留选择。全新浏览器会使用浏览器请求的第一个受支持语言,直到允许读取的已存储偏好到达。插件作者可添加类型化命名空间字典,并通过公开 locale API 翻译;经 slot 渲染的文案无需重新加载即可随语言切换更新。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -31,10 +31,16 @@ kind: "package-reference"
|
|
|
31
31
|
|
|
32
32
|
打开“设置 → 常规”并选择一种已注册语言。生效中的 locale 会立即应用:UI 文案切换、`<html lang>` 指向外部 id 或内置语言的文档标签,选择写入持久设置分区。没有显式 Host 偏好的浏览器会按完整标签、再按主语言子标签选择 `navigator` 请求的第一个已注册语言,无法匹配时回退到英文。已存储的外部 locale 会等待其定义注册,不会在不可用时生效。
|
|
33
33
|
|
|
34
|
+
原生壳可以提供包含异步 `read()` 和 `onChange(locale)` 回调的 `__DSH_LOCALE__`。初始化在 Client 挂载前提供当前 Host 偏好和有序的系统语言列表。自动选择保持临时状态;只有设置中的选择会写入 `locale.preference`。每次加载页面都重新读取,避免重载后沿用过期的 preload 偏好。普通浏览器继续使用 navigator 检测和原有的设置作用域策略。
|
|
35
|
+
|
|
34
36
|
### 注册字典
|
|
35
37
|
|
|
36
38
|
用已合并进 `LocaleNamespaceMap` 的命名空间调用 `ctx.locale.register(ns, { zh, en })`;编译器会对照该命名空间的类型化键并集检查每个键,并要求两个内置 locale 齐全。消费方通过 `ctx.locale.bind(ns)` 或框架注入的 `t` 席位翻译。UI 已挂载后再注册的字典无需重新挂载即可生效。
|
|
37
39
|
|
|
40
|
+
### 解析包文本
|
|
41
|
+
|
|
42
|
+
使用 `ctx.locale.resolveText(text)` 解析 [`LocalizedText`](../../util/package-manifest/README.zh.md),例如已安装插件的标题与描述。字面字符串原样返回。翻译映射使用小写语言 id,必须提供 `en` 回退值,并沿当前语言声明的回退链查找。它们不查询或注册命名空间字典。
|
|
43
|
+
|
|
38
44
|
### 注册语言包
|
|
39
45
|
|
|
40
46
|
外部 client 插件把语言定义和每个已翻译命名空间注册为自身拥有的 effect;定义与字典可以按任意顺序注册:
|
|
@@ -83,6 +89,8 @@ Host 通过 settings 服务为 loopback 页面持久化偏好。Client 会刻意
|
|
|
83
89
|
|
|
84
90
|
### 字典查找
|
|
85
91
|
|
|
92
|
+
文档语言同步只在值变化时写入 `<html lang>`;仅更新字典的 revision 不改动该属性。
|
|
93
|
+
|
|
86
94
|
带类型的对象形式要求两个内置 locale 都有完整字典;逐 locale 形式允许语言包独立注册每个命名空间。逐键查找会先在请求命名空间中沿生效语言声明的 fallback 链查找,再在 `common` 中重复该链,最后显示键本身。绑定的翻译函数按命名空间保持稳定身份,因此可通过 inject 机制传递,且不会破坏 memoization。
|
|
87
95
|
|
|
88
96
|
### 源码地图
|
package/lib/client.js
CHANGED
|
@@ -8,7 +8,22 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
10
|
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
11
|
-
//#region
|
|
11
|
+
//#region lib/types/client/bootstrap.js
|
|
12
|
+
/** Locale initialization supplied by a native shell before its Client tree mounts. */
|
|
13
|
+
/**
|
|
14
|
+
* Validate initialization data returned over the preload IPC bridge.
|
|
15
|
+
* @param value - untrusted IPC response.
|
|
16
|
+
* @returns language initialization with no persistence side effects.
|
|
17
|
+
*/
|
|
18
|
+
function parseLocaleBootstrap(value) {
|
|
19
|
+
if (typeof value !== "object" || value === null || !("languages" in value) || !Array.isArray(value.languages) || !value.languages.every((language) => typeof language === "string") || !("preference" in value) || value.preference !== null && typeof value.preference !== "string") throw new TypeError("locale: invalid native initialization data");
|
|
20
|
+
return {
|
|
21
|
+
languages: value.languages,
|
|
22
|
+
preference: value.preference
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region ../../../vendor/cosmokit/lib/index.js
|
|
12
27
|
/** Return true when a value is `null` or `undefined`. */
|
|
13
28
|
function isNullable(value) {
|
|
14
29
|
return value === null || value === void 0;
|
|
@@ -32,8 +47,43 @@ window.__ModuleLoader__.load({
|
|
|
32
47
|
for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
|
|
33
48
|
return result;
|
|
34
49
|
}
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
/** Shared config references used by schema validators and plugin runtimes. */
|
|
51
|
+
const write = Symbol.for("cosmokit.volatile.write");
|
|
52
|
+
function snapshot(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
53
|
+
if (typeof value === "function") throw new TypeError("volatile config cannot contain functions");
|
|
54
|
+
if (value === null || typeof value !== "object") return value;
|
|
55
|
+
if (ancestors.has(value)) throw new TypeError("volatile config cannot contain cycles");
|
|
56
|
+
ancestors.add(value);
|
|
57
|
+
try {
|
|
58
|
+
if (Array.isArray(value)) return Object.freeze(value.map((item) => snapshot(item, ancestors)));
|
|
59
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("volatile config objects must be plain objects or arrays");
|
|
60
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])));
|
|
61
|
+
} finally {
|
|
62
|
+
ancestors.delete(value);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create a detached reference containing an immutable copy of the supplied data.
|
|
67
|
+
* @param value - validated config data; class instances and functions are unsupported.
|
|
68
|
+
* @returns a reference whose value is updated only by its owning runtime.
|
|
69
|
+
*/
|
|
70
|
+
function createVolatile(value) {
|
|
71
|
+
let current = snapshot(value);
|
|
72
|
+
return Object.freeze({
|
|
73
|
+
get: () => current,
|
|
74
|
+
[write]: (value) => {
|
|
75
|
+
current = value;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Identify references across ESM/CJS copies of the shared library.
|
|
81
|
+
* @param value - a parsed config value.
|
|
82
|
+
* @returns whether the value implements the shared reference protocol.
|
|
83
|
+
*/
|
|
84
|
+
function isVolatile(value) {
|
|
85
|
+
return typeof value === "object" && value !== null && write in value;
|
|
86
|
+
}
|
|
37
87
|
/** Test values using `instanceof` with a `toStringTag` fallback. */
|
|
38
88
|
function is(type, value) {
|
|
39
89
|
if (arguments.length === 1) return (value) => is(type, value);
|
|
@@ -45,15 +95,16 @@ window.__ModuleLoader__.load({
|
|
|
45
95
|
function isArrayBufferSource(value) {
|
|
46
96
|
return isArrayBufferLike(value) || ArrayBuffer.isView(value);
|
|
47
97
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
98
|
+
/** Binary source detection and base64/hex conversion helpers. */
|
|
99
|
+
var Binary;
|
|
100
|
+
(function(Binary) {
|
|
101
|
+
Binary.is = isArrayBufferLike;
|
|
102
|
+
Binary.isSource = isArrayBufferSource;
|
|
52
103
|
function fromSource(source) {
|
|
53
104
|
if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
|
|
54
105
|
else return source;
|
|
55
106
|
}
|
|
56
|
-
|
|
107
|
+
Binary.fromSource = fromSource;
|
|
57
108
|
function toBase64(source) {
|
|
58
109
|
source = fromSource(source);
|
|
59
110
|
if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
|
|
@@ -62,18 +113,18 @@ window.__ModuleLoader__.load({
|
|
|
62
113
|
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
|
|
63
114
|
return btoa(binary);
|
|
64
115
|
}
|
|
65
|
-
|
|
116
|
+
Binary.toBase64 = toBase64;
|
|
66
117
|
function fromBase64(source) {
|
|
67
118
|
if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
|
|
68
119
|
return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
|
|
69
120
|
}
|
|
70
|
-
|
|
121
|
+
Binary.fromBase64 = fromBase64;
|
|
71
122
|
function toHex(source) {
|
|
72
123
|
source = fromSource(source);
|
|
73
124
|
if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
|
|
74
125
|
return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
75
126
|
}
|
|
76
|
-
|
|
127
|
+
Binary.toHex = toHex;
|
|
77
128
|
function fromHex(source) {
|
|
78
129
|
if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
|
|
79
130
|
const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
|
|
@@ -81,7 +132,7 @@ window.__ModuleLoader__.load({
|
|
|
81
132
|
for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
|
|
82
133
|
return Uint8Array.from(buffer).buffer;
|
|
83
134
|
}
|
|
84
|
-
|
|
135
|
+
Binary.fromHex = fromHex;
|
|
85
136
|
})(Binary || (Binary = {}));
|
|
86
137
|
Binary.fromBase64;
|
|
87
138
|
Binary.toBase64;
|
|
@@ -113,58 +164,78 @@ window.__ModuleLoader__.load({
|
|
|
113
164
|
}
|
|
114
165
|
return result;
|
|
115
166
|
}
|
|
116
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* Compare values recursively, treating two volatile references as equal regardless of value.
|
|
169
|
+
* Strict comparison distinguishes null/undefined, treats opaque objects by identity,
|
|
170
|
+
* compares URLs by normalized href, treats array holes as undefined, and considers distinct cyclic structures unequal.
|
|
171
|
+
* @param a - first value.
|
|
172
|
+
* @param b - second value.
|
|
173
|
+
* @param strict - whether to require strict data equality outside volatile references.
|
|
174
|
+
* @returns whether the values compare equal.
|
|
175
|
+
*/
|
|
117
176
|
function deepEqual(a, b, strict) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
177
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
178
|
+
function compare(a, b) {
|
|
179
|
+
if (a === b) return true;
|
|
180
|
+
if (isVolatile(a) || isVolatile(b)) return isVolatile(a) && isVolatile(b);
|
|
181
|
+
if (!strict && isNullable(a) && isNullable(b)) return true;
|
|
182
|
+
if (typeof a !== typeof b || typeof a !== "object" || !a || !b) return false;
|
|
183
|
+
if (ancestors.has(a)) return false;
|
|
184
|
+
function check(test, then) {
|
|
185
|
+
return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
|
|
186
|
+
}
|
|
187
|
+
ancestors.add(a);
|
|
188
|
+
try {
|
|
189
|
+
return check(Array.isArray, (a, b) => {
|
|
190
|
+
if (a.length !== b.length) return false;
|
|
191
|
+
for (let index = 0; index < a.length; index++) if (!compare(a[index], b[index])) return false;
|
|
192
|
+
return true;
|
|
193
|
+
}) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("URL"), (a, b) => a.href === b.href) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
|
|
194
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
195
|
+
const viewA = new Uint8Array(a);
|
|
196
|
+
const viewB = new Uint8Array(b);
|
|
197
|
+
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
198
|
+
return true;
|
|
199
|
+
}) ?? ((!strict || [a, b].every((value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) && Object.keys({
|
|
200
|
+
...a,
|
|
201
|
+
...b
|
|
202
|
+
}).every((key) => compare(a[key], b[key])));
|
|
203
|
+
} finally {
|
|
204
|
+
ancestors.delete(a);
|
|
205
|
+
}
|
|
125
206
|
}
|
|
126
|
-
return
|
|
127
|
-
if (a.byteLength !== b.byteLength) return false;
|
|
128
|
-
const viewA = new Uint8Array(a);
|
|
129
|
-
const viewB = new Uint8Array(b);
|
|
130
|
-
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
131
|
-
return true;
|
|
132
|
-
}) ?? Object.keys({
|
|
133
|
-
...a,
|
|
134
|
-
...b
|
|
135
|
-
}).every((key) => deepEqual(a[key], b[key], strict));
|
|
207
|
+
return compare(a, b);
|
|
136
208
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const week = _Time.week = day * 7;
|
|
209
|
+
/** Time constants plus parsing and formatting helpers. */
|
|
210
|
+
var Time;
|
|
211
|
+
(function(Time) {
|
|
212
|
+
Time.millisecond = 1;
|
|
213
|
+
Time.second = 1e3;
|
|
214
|
+
Time.minute = Time.second * 60;
|
|
215
|
+
Time.hour = Time.minute * 60;
|
|
216
|
+
Time.day = Time.hour * 24;
|
|
217
|
+
Time.week = Time.day * 7;
|
|
147
218
|
let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
148
219
|
function setTimezoneOffset(offset) {
|
|
149
220
|
timezoneOffset = offset;
|
|
150
221
|
}
|
|
151
|
-
|
|
222
|
+
Time.setTimezoneOffset = setTimezoneOffset;
|
|
152
223
|
function getTimezoneOffset() {
|
|
153
224
|
return timezoneOffset;
|
|
154
225
|
}
|
|
155
|
-
|
|
226
|
+
Time.getTimezoneOffset = getTimezoneOffset;
|
|
156
227
|
function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
|
|
157
228
|
if (typeof date === "number") date = new Date(date);
|
|
158
229
|
if (offset === void 0) offset = timezoneOffset;
|
|
159
|
-
return Math.floor((date.valueOf() / minute - offset) / 1440);
|
|
230
|
+
return Math.floor((date.valueOf() / Time.minute - offset) / 1440);
|
|
160
231
|
}
|
|
161
|
-
|
|
232
|
+
Time.getDateNumber = getDateNumber;
|
|
162
233
|
function fromDateNumber(value, offset) {
|
|
163
|
-
const date = new Date(value * day);
|
|
234
|
+
const date = new Date(value * Time.day);
|
|
164
235
|
if (offset === void 0) offset = timezoneOffset;
|
|
165
|
-
return new Date(+date + offset * minute);
|
|
236
|
+
return new Date(+date + offset * Time.minute);
|
|
166
237
|
}
|
|
167
|
-
|
|
238
|
+
Time.fromDateNumber = fromDateNumber;
|
|
168
239
|
const numeric = /\d+(?:\.\d+)?/.source;
|
|
169
240
|
const timeRegExp = new RegExp(`^${[
|
|
170
241
|
"w(?:eek(?:s)?)?",
|
|
@@ -176,9 +247,9 @@ window.__ModuleLoader__.load({
|
|
|
176
247
|
function parseTime(source) {
|
|
177
248
|
const capture = timeRegExp.exec(source);
|
|
178
249
|
if (!capture) return 0;
|
|
179
|
-
return (parseFloat(capture[1]) * week || 0) + (parseFloat(capture[2]) * day || 0) + (parseFloat(capture[3]) * hour || 0) + (parseFloat(capture[4]) * minute || 0) + (parseFloat(capture[5]) * second || 0);
|
|
250
|
+
return (parseFloat(capture[1]) * Time.week || 0) + (parseFloat(capture[2]) * Time.day || 0) + (parseFloat(capture[3]) * Time.hour || 0) + (parseFloat(capture[4]) * Time.minute || 0) + (parseFloat(capture[5]) * Time.second || 0);
|
|
180
251
|
}
|
|
181
|
-
|
|
252
|
+
Time.parseTime = parseTime;
|
|
182
253
|
function parseDate(date) {
|
|
183
254
|
const parsed = parseTime(date);
|
|
184
255
|
if (parsed) date = Date.now() + parsed;
|
|
@@ -186,27 +257,27 @@ window.__ModuleLoader__.load({
|
|
|
186
257
|
else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
|
|
187
258
|
return date ? new Date(date) : /* @__PURE__ */ new Date();
|
|
188
259
|
}
|
|
189
|
-
|
|
260
|
+
Time.parseDate = parseDate;
|
|
190
261
|
function format(ms) {
|
|
191
262
|
const abs = Math.abs(ms);
|
|
192
|
-
if (abs >= day - hour / 2) return Math.round(ms / day) + "d";
|
|
193
|
-
else if (abs >= hour - minute / 2) return Math.round(ms / hour) + "h";
|
|
194
|
-
else if (abs >= minute - second / 2) return Math.round(ms / minute) + "m";
|
|
195
|
-
else if (abs >= second) return Math.round(ms / second) + "s";
|
|
263
|
+
if (abs >= Time.day - Time.hour / 2) return Math.round(ms / Time.day) + "d";
|
|
264
|
+
else if (abs >= Time.hour - Time.minute / 2) return Math.round(ms / Time.hour) + "h";
|
|
265
|
+
else if (abs >= Time.minute - Time.second / 2) return Math.round(ms / Time.minute) + "m";
|
|
266
|
+
else if (abs >= Time.second) return Math.round(ms / Time.second) + "s";
|
|
196
267
|
return ms + "ms";
|
|
197
268
|
}
|
|
198
|
-
|
|
269
|
+
Time.format = format;
|
|
199
270
|
function toDigits(source, length = 2) {
|
|
200
271
|
return source.toString().padStart(length, "0");
|
|
201
272
|
}
|
|
202
|
-
|
|
273
|
+
Time.toDigits = toDigits;
|
|
203
274
|
function template(template, time = /* @__PURE__ */ new Date()) {
|
|
204
275
|
return template.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
|
|
205
276
|
}
|
|
206
|
-
|
|
277
|
+
Time.template = template;
|
|
207
278
|
})(Time || (Time = {}));
|
|
208
279
|
//#endregion
|
|
209
|
-
//#region ../../../vendor/schemastery/
|
|
280
|
+
//#region ../../../vendor/schemastery/lib/index.mjs
|
|
210
281
|
const kSchema = Symbol.for("schemastery");
|
|
211
282
|
const kValidationError = Symbol.for("ValidationError");
|
|
212
283
|
globalThis.__schemastery_index__ ??= 0;
|
|
@@ -382,6 +453,7 @@ window.__ModuleLoader__.load({
|
|
|
382
453
|
return schema;
|
|
383
454
|
};
|
|
384
455
|
Schema.prototype.simplify = function simplify(value) {
|
|
456
|
+
if (isVolatile(value)) value = value.get();
|
|
385
457
|
if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
|
|
386
458
|
if (isNullable(value)) return value;
|
|
387
459
|
if (this.type === "object" || this.type === "dict") {
|
|
@@ -438,12 +510,49 @@ window.__ModuleLoader__.load({
|
|
|
438
510
|
};
|
|
439
511
|
return schema;
|
|
440
512
|
} });
|
|
513
|
+
Schema.prototype.volatile = function volatile() {
|
|
514
|
+
if (this.meta.volatile) throw new TypeError("volatile schema is already wrapped");
|
|
515
|
+
return this.extra("volatile", true);
|
|
516
|
+
};
|
|
441
517
|
const resolvers = {};
|
|
518
|
+
const checkedVolatile = Symbol("checked-volatile-schema");
|
|
519
|
+
function validateVolatileSchema(schema, path = [], blocked = false, seen = /* @__PURE__ */ new Map()) {
|
|
520
|
+
const states = seen.get(schema) ?? /* @__PURE__ */ new Set();
|
|
521
|
+
if (states.has(blocked)) return;
|
|
522
|
+
states.add(blocked);
|
|
523
|
+
seen.set(schema, states);
|
|
524
|
+
if (schema.meta?.volatile && blocked) throw new ValidationError("volatile fields require a fixed object path without an enclosing volatile field", { path });
|
|
525
|
+
const nested = blocked || !!schema.meta?.volatile;
|
|
526
|
+
if (schema.dict) for (const [key, child] of Object.entries(schema.dict)) validateVolatileSchema(child, [...path, key], nested, seen);
|
|
527
|
+
if (schema.sKey) validateVolatileSchema(schema.sKey, [...path, "<key>"], true, seen);
|
|
528
|
+
if (schema.inner && (schema.type !== "lazy" || schema.inner[kSchema])) validateVolatileSchema(schema.inner, [...path, "*"], true, seen);
|
|
529
|
+
if (schema.list) for (let index = 0; index < schema.list.length; index++) validateVolatileSchema(schema.list[index], [...path, String(index)], true, seen);
|
|
530
|
+
}
|
|
442
531
|
Schema.extend = function extend(type, resolve) {
|
|
443
532
|
resolvers[type] = resolve;
|
|
444
533
|
};
|
|
445
534
|
Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
|
|
446
535
|
if (!schema) return [data];
|
|
536
|
+
if (!options[checkedVolatile]) {
|
|
537
|
+
validateVolatileSchema(schema, options.path);
|
|
538
|
+
options = {
|
|
539
|
+
...options,
|
|
540
|
+
[checkedVolatile]: true
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
if (schema.meta?.volatile) {
|
|
544
|
+
const inner = Schema(schema);
|
|
545
|
+
inner.meta = {
|
|
546
|
+
...schema.meta,
|
|
547
|
+
volatile: false
|
|
548
|
+
};
|
|
549
|
+
const [value, adapted] = Schema.resolve(data, inner, options, strict);
|
|
550
|
+
try {
|
|
551
|
+
return [createVolatile(value), adapted];
|
|
552
|
+
} catch (error) {
|
|
553
|
+
throw new ValidationError(error instanceof Error ? error.message : String(error), options);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
447
556
|
if (options.ignore?.(data, schema)) return [data];
|
|
448
557
|
if (isNullable(data) && schema.type !== "lazy") {
|
|
449
558
|
if (schema.meta.required) throw new ValidationError(`missing required value`, options);
|
|
@@ -546,6 +655,7 @@ window.__ModuleLoader__.load({
|
|
|
546
655
|
...schema.meta,
|
|
547
656
|
...schema.inner.meta
|
|
548
657
|
};
|
|
658
|
+
validateVolatileSchema(schema.inner, options.path, true);
|
|
549
659
|
}
|
|
550
660
|
return Schema.resolve(data, schema.inner, options, strict);
|
|
551
661
|
});
|
|
@@ -645,7 +755,7 @@ window.__ModuleLoader__.load({
|
|
|
645
755
|
} catch (e) {
|
|
646
756
|
if (!options?.autofix) throw e;
|
|
647
757
|
delete data[key];
|
|
648
|
-
return schema.meta.default;
|
|
758
|
+
return schema.meta.volatile ? createVolatile(schema.meta.default) : schema.meta.default;
|
|
649
759
|
}
|
|
650
760
|
}
|
|
651
761
|
Schema.extend("array", (data, { inner, meta }, options) => {
|
|
@@ -810,7 +920,9 @@ window.__ModuleLoader__.load({
|
|
|
810
920
|
const LOCALE_ID_PATTERN = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/u;
|
|
811
921
|
/** Locale identifiers shipped by the browser client. */
|
|
812
922
|
const LOCALE_IDS = ["zh", "en"];
|
|
813
|
-
|
|
923
|
+
/** Durable locale schema; also the wire envelope the browser scope validates against. */
|
|
924
|
+
const LocaleSettingsFields = { [LOCALE_PREFERENCE_FIELD]: Schema.string().pattern(LOCALE_ID_PATTERN).required(false) };
|
|
925
|
+
Schema.object(LocaleSettingsFields);
|
|
814
926
|
//#endregion
|
|
815
927
|
//#region lib/types/locales/zh.js
|
|
816
928
|
/** zh base dictionary for the common namespace: cross-feature standard words. */
|
|
@@ -970,7 +1082,7 @@ window.__ModuleLoader__.load({
|
|
|
970
1082
|
onClick: () => {
|
|
971
1083
|
setOpen((v) => !v);
|
|
972
1084
|
},
|
|
973
|
-
children: [activeLabel, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1085
|
+
children: [activeLabel, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: LanguageRow_module_css_default.chevron })]
|
|
974
1086
|
})
|
|
975
1087
|
})]
|
|
976
1088
|
});
|
|
@@ -1051,7 +1163,8 @@ window.__ModuleLoader__.load({
|
|
|
1051
1163
|
*/
|
|
1052
1164
|
function syncDocumentLanguage(snapshot) {
|
|
1053
1165
|
if (typeof document === "undefined") return;
|
|
1054
|
-
|
|
1166
|
+
const language = snapshot.active === "zh" ? "zh-CN" : snapshot.active;
|
|
1167
|
+
if (document.documentElement.lang !== language) document.documentElement.lang = language;
|
|
1055
1168
|
}
|
|
1056
1169
|
/**
|
|
1057
1170
|
* Dictionary registry plus locale preference. Lookup walks the active
|
|
@@ -1064,6 +1177,7 @@ window.__ModuleLoader__.load({
|
|
|
1064
1177
|
* `ctx.slots.installLocale`.
|
|
1065
1178
|
*/
|
|
1066
1179
|
var LocaleRuntime = class {
|
|
1180
|
+
bootstrap;
|
|
1067
1181
|
dicts = /* @__PURE__ */ new Map();
|
|
1068
1182
|
bound = /* @__PURE__ */ new Map();
|
|
1069
1183
|
catalog = /* @__PURE__ */ new Map();
|
|
@@ -1081,15 +1195,18 @@ window.__ModuleLoader__.load({
|
|
|
1081
1195
|
* listener is released through ctx.effect on dispose).
|
|
1082
1196
|
* @param host - durable preference scope owned by the providing plugin;
|
|
1083
1197
|
* absent compositions (standalone dictionary registries) stay process-local.
|
|
1198
|
+
* @param bootstrap - native initialization; absent in ordinary browsers.
|
|
1084
1199
|
*/
|
|
1085
|
-
constructor(ctx, host) {
|
|
1200
|
+
constructor(ctx, host, bootstrap) {
|
|
1201
|
+
this.bootstrap = bootstrap;
|
|
1086
1202
|
this.ctx = ctx;
|
|
1087
1203
|
this.host = host;
|
|
1088
1204
|
for (const locale of BUILT_IN_LOCALES) this.catalog.set(localeKey(locale.id), locale);
|
|
1089
1205
|
const locales = this.localeList();
|
|
1090
|
-
this.provisional = resolveInitialLocale(locales);
|
|
1206
|
+
this.provisional = resolveInitialLocale(locales, bootstrap?.languages);
|
|
1207
|
+
this.preference = bootstrap?.preference ?? void 0;
|
|
1091
1208
|
this.snapshot = Object.freeze({
|
|
1092
|
-
active: this.
|
|
1209
|
+
active: this.resolveActive(),
|
|
1093
1210
|
locales,
|
|
1094
1211
|
revision: 0
|
|
1095
1212
|
});
|
|
@@ -1108,6 +1225,16 @@ window.__ModuleLoader__.load({
|
|
|
1108
1225
|
return this.snapshot;
|
|
1109
1226
|
}
|
|
1110
1227
|
/**
|
|
1228
|
+
* Resolve package text through the active language's declared fallback chain.
|
|
1229
|
+
* Plain strings stay verbatim; maps do not consult registered dictionaries.
|
|
1230
|
+
* @param text - package text whose locale keys are lowercase and include English.
|
|
1231
|
+
* @returns the first available translation, including an empty string.
|
|
1232
|
+
*/
|
|
1233
|
+
resolveText(text) {
|
|
1234
|
+
if (typeof text === "string") return text;
|
|
1235
|
+
return this.fallbackChain(this.snapshot.active).reduceRight((resolved, locale) => text[localeKey(locale)] ?? resolved, text.en);
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1111
1238
|
* LocaleFace getSnapshot: the current snapshot (carries `revision`; stable
|
|
1112
1239
|
* reference between changes, uSES-safe).
|
|
1113
1240
|
* @returns the current snapshot.
|
|
@@ -1200,7 +1327,7 @@ window.__ModuleLoader__.load({
|
|
|
1200
1327
|
publishCatalog() {
|
|
1201
1328
|
this.fallbackChains.clear();
|
|
1202
1329
|
const locales = this.localeList();
|
|
1203
|
-
this.provisional = resolveInitialLocale(locales);
|
|
1330
|
+
this.provisional = resolveInitialLocale(locales, this.bootstrap?.languages);
|
|
1204
1331
|
const active = this.resolveActive();
|
|
1205
1332
|
this.publish(active, active !== this.snapshot.active, locales);
|
|
1206
1333
|
}
|
|
@@ -1323,8 +1450,8 @@ window.__ModuleLoader__.load({
|
|
|
1323
1450
|
* The browser's own language wins over {@link FALLBACK_LOCALE}; an explicit
|
|
1324
1451
|
* Host preference may replace this provisional value after plugin activation.
|
|
1325
1452
|
*/
|
|
1326
|
-
function resolveInitialLocale(locales) {
|
|
1327
|
-
return detectBrowserLocale(locales) ?? "en";
|
|
1453
|
+
function resolveInitialLocale(locales, languages) {
|
|
1454
|
+
return detectBrowserLocale(locales, languages) ?? "en";
|
|
1328
1455
|
}
|
|
1329
1456
|
/**
|
|
1330
1457
|
* The first registered locale the browser asks for. Each browser tag first
|
|
@@ -1335,12 +1462,15 @@ window.__ModuleLoader__.load({
|
|
|
1335
1462
|
* locale for non-browser runs. `navigator.language` trails the ordered
|
|
1336
1463
|
* `languages` list and covers hosts exposing only the single tag.
|
|
1337
1464
|
* @param locales - definitions currently available to the browser.
|
|
1465
|
+
* @param languages - native system language order, when supplied by a shell.
|
|
1338
1466
|
* @returns the first matching locale id, or undefined.
|
|
1339
1467
|
*/
|
|
1340
|
-
function detectBrowserLocale(locales) {
|
|
1341
|
-
if (
|
|
1342
|
-
|
|
1343
|
-
|
|
1468
|
+
function detectBrowserLocale(locales, languages) {
|
|
1469
|
+
if (languages === void 0) {
|
|
1470
|
+
if (typeof window === "undefined") return void 0;
|
|
1471
|
+
languages = [...navigator.languages ?? [], navigator.language];
|
|
1472
|
+
}
|
|
1473
|
+
for (const tag of languages) {
|
|
1344
1474
|
const requested = localeKey(tag);
|
|
1345
1475
|
const exact = locales.find((locale) => localeKey(locale.id) === requested);
|
|
1346
1476
|
if (exact !== void 0) return exact.id;
|
|
@@ -1353,16 +1483,30 @@ window.__ModuleLoader__.load({
|
|
|
1353
1483
|
const inject = [
|
|
1354
1484
|
"slots",
|
|
1355
1485
|
"remote",
|
|
1356
|
-
"
|
|
1486
|
+
"configForms"
|
|
1357
1487
|
];
|
|
1358
1488
|
/**
|
|
1359
1489
|
* Client plugin body: provide the locale service with base dictionaries and
|
|
1360
1490
|
* register the feature-owned Language preference row into the General
|
|
1361
1491
|
* section's item slot (a feature owns its settings surface).
|
|
1362
1492
|
* @param ctx - client cordis context.
|
|
1493
|
+
* @returns resolves after native language initialization and plugin registration.
|
|
1363
1494
|
*/
|
|
1364
|
-
function apply(ctx) {
|
|
1365
|
-
const
|
|
1495
|
+
async function apply(ctx) {
|
|
1496
|
+
const bridge = globalThis.__DSH_LOCALE__;
|
|
1497
|
+
let bootstrap;
|
|
1498
|
+
if (bridge !== void 0) {
|
|
1499
|
+
let value;
|
|
1500
|
+
try {
|
|
1501
|
+
value = await bridge.read();
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
if (ctx.fiber.uid === null) return;
|
|
1504
|
+
throw error;
|
|
1505
|
+
}
|
|
1506
|
+
if (ctx.fiber.uid === null) return;
|
|
1507
|
+
bootstrap = parseLocaleBootstrap(value);
|
|
1508
|
+
}
|
|
1509
|
+
const locale = new LocaleRuntime(ctx, ctx.configForms.get(LOCALE_SETTINGS_NAMESPACE), bootstrap);
|
|
1366
1510
|
locale.register(COMMON_NS, {
|
|
1367
1511
|
zh: zh$1,
|
|
1368
1512
|
en: en$1
|
|
@@ -1372,6 +1516,12 @@ window.__ModuleLoader__.load({
|
|
|
1372
1516
|
en
|
|
1373
1517
|
});
|
|
1374
1518
|
ctx.provide("locale", locale);
|
|
1519
|
+
if (bridge !== void 0) {
|
|
1520
|
+
ctx.on("locale/change", (snapshot) => {
|
|
1521
|
+
bridge.onChange(snapshot.active);
|
|
1522
|
+
});
|
|
1523
|
+
bridge.onChange(locale.getSnapshot().active);
|
|
1524
|
+
}
|
|
1375
1525
|
ctx.slots.installLocale(locale);
|
|
1376
1526
|
const store = createLanguageRowStore();
|
|
1377
1527
|
let bound;
|
package/lib/index.js
CHANGED
|
@@ -10,18 +10,19 @@ const LOCALE_ID_PATTERN = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/u;
|
|
|
10
10
|
/** Locale identifiers shipped by the browser client. */
|
|
11
11
|
const LOCALE_IDS = ["zh", "en"];
|
|
12
12
|
/** Durable locale schema; also the wire envelope the browser scope validates against. */
|
|
13
|
-
const
|
|
13
|
+
const LocaleSettingsFields = { [LOCALE_PREFERENCE_FIELD]: z.string().pattern(LOCALE_ID_PATTERN).required(false) };
|
|
14
|
+
z.object(LocaleSettingsFields);
|
|
14
15
|
//#endregion
|
|
15
16
|
//#region lib/types/index.js
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* @param ctx
|
|
17
|
+
/** Live preferences projected to the browser. */
|
|
18
|
+
const Config = z.object({ [LOCALE_PREFERENCE_FIELD]: LocaleSettingsFields[LOCALE_PREFERENCE_FIELD].volatile() });
|
|
19
|
+
/** Host preferences are consumed through the configuration form projection.
|
|
20
|
+
* @param ctx Plugin context used for optional settings presentation.
|
|
20
21
|
*/
|
|
21
22
|
function apply(ctx) {
|
|
22
|
-
ctx.inject(["settings"], (
|
|
23
|
-
|
|
23
|
+
ctx.inject(["settings"], (child) => {
|
|
24
|
+
child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
//#endregion
|
|
27
|
-
export { LOCALE_IDS, LOCALE_PREFERENCE_FIELD, LOCALE_SETTINGS_NAMESPACE, apply };
|
|
28
|
+
export { Config, LOCALE_IDS, LOCALE_PREFERENCE_FIELD, LOCALE_SETTINGS_NAMESPACE, apply };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Locale initialization supplied by a native shell before its Client tree mounts. */
|
|
2
|
+
/** System languages and the existing Host preference for one page load. */
|
|
3
|
+
export interface LocaleBootstrap {
|
|
4
|
+
/** Operating-system language tags in preference order. */
|
|
5
|
+
readonly languages: readonly string[];
|
|
6
|
+
/** Stored locale.preference; null preserves automatic language selection. */
|
|
7
|
+
readonly preference: string | null;
|
|
8
|
+
}
|
|
9
|
+
/** Optional page-global bridge named __DSH_LOCALE__; ordinary browsers omit it. */
|
|
10
|
+
export interface LocaleBridge {
|
|
11
|
+
/** Read current initialization data through the shell's isolated preload. @returns unvalidated IPC data. */
|
|
12
|
+
read(): Promise<unknown>;
|
|
13
|
+
/** Publish the resolved Client locale without writing another preference. @param locale - active registered locale id. */
|
|
14
|
+
onChange(locale: string): void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate initialization data returned over the preload IPC bridge.
|
|
18
|
+
* @param value - untrusted IPC response.
|
|
19
|
+
* @returns language initialization with no persistence side effects.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseLocaleBootstrap(value: unknown): LocaleBootstrap;
|
|
22
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* its own settings surface.
|
|
6
6
|
*/
|
|
7
7
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
8
|
+
import type { LocalizedText } from '@deepseek-ai/dsh-package-manifest';
|
|
8
9
|
import { type LocaleDictOf, type LocaleNamespaceMap, type Translate, type TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
|
|
9
|
-
import type {
|
|
10
|
+
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
11
|
+
import { type LocaleBootstrap } from './bootstrap.ts';
|
|
10
12
|
import { type BuiltInLocaleId, type LocaleId, type LocaleSettings } from '../locale-settings.ts';
|
|
11
13
|
import { type CommonKey } from '../locales/index.ts';
|
|
12
14
|
import { type SettingsLocaleKey } from '../locales/settings.ts';
|
|
@@ -93,6 +95,7 @@ export declare const SETTINGS_NS = "settings.locale";
|
|
|
93
95
|
* `ctx.slots.installLocale`.
|
|
94
96
|
*/
|
|
95
97
|
export declare class LocaleRuntime {
|
|
98
|
+
private readonly bootstrap?;
|
|
96
99
|
private dicts;
|
|
97
100
|
private bound;
|
|
98
101
|
private catalog;
|
|
@@ -110,13 +113,21 @@ export declare class LocaleRuntime {
|
|
|
110
113
|
* listener is released through ctx.effect on dispose).
|
|
111
114
|
* @param host - durable preference scope owned by the providing plugin;
|
|
112
115
|
* absent compositions (standalone dictionary registries) stay process-local.
|
|
116
|
+
* @param bootstrap - native initialization; absent in ordinary browsers.
|
|
113
117
|
*/
|
|
114
|
-
constructor(ctx: ClientContext, host?:
|
|
118
|
+
constructor(ctx: ClientContext, host?: ConfigForm<LocaleSettings>, bootstrap?: LocaleBootstrap | undefined);
|
|
115
119
|
/**
|
|
116
120
|
* Read the current immutable locale snapshot.
|
|
117
121
|
* @returns the current snapshot (stable reference until the next change).
|
|
118
122
|
*/
|
|
119
123
|
getLocale(): LocaleSnapshot;
|
|
124
|
+
/**
|
|
125
|
+
* Resolve package text through the active language's declared fallback chain.
|
|
126
|
+
* Plain strings stay verbatim; maps do not consult registered dictionaries.
|
|
127
|
+
* @param text - package text whose locale keys are lowercase and include English.
|
|
128
|
+
* @returns the first available translation, including an empty string.
|
|
129
|
+
*/
|
|
130
|
+
resolveText(text: LocalizedText): string;
|
|
120
131
|
/**
|
|
121
132
|
* LocaleFace getSnapshot: the current snapshot (carries `revision`; stable
|
|
122
133
|
* reference between changes, uSES-safe).
|
|
@@ -231,6 +242,7 @@ export declare const inject: string[];
|
|
|
231
242
|
* register the feature-owned Language preference row into the General
|
|
232
243
|
* section's item slot (a feature owns its settings surface).
|
|
233
244
|
* @param ctx - client cordis context.
|
|
245
|
+
* @returns resolves after native language initialization and plugin registration.
|
|
234
246
|
*/
|
|
235
|
-
export declare function apply(ctx: ClientContext): void
|
|
247
|
+
export declare function apply(ctx: ClientContext): Promise<void>;
|
|
236
248
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
import type { Volatile, Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import z from '@deepseek-ai/schemastery';
|
|
3
3
|
export { LOCALE_IDS, LOCALE_PREFERENCE_FIELD, LOCALE_SETTINGS_NAMESPACE, type BuiltInLocaleId, type LocaleId, type LocaleSettings, } from './locale-settings.ts';
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/** Runtime preferences projected to the browser. */
|
|
5
|
+
export interface Config {
|
|
6
|
+
/** Explicit locale; omission follows the browser. */
|
|
7
|
+
preference: Volatile<string | undefined>;
|
|
8
|
+
}
|
|
9
|
+
/** Live preferences projected to the browser. */
|
|
10
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
11
|
+
preference: z<string, string, "volatile">;
|
|
12
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
13
|
+
preference: z<string, string, "volatile">;
|
|
14
|
+
}>>, "plain">;
|
|
15
|
+
/** Host preferences are consumed through the configuration form projection.
|
|
16
|
+
* @param ctx Plugin context used for optional settings presentation.
|
|
7
17
|
*/
|
|
8
18
|
export declare function apply(ctx: Context): void;
|
|
9
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -18,5 +18,13 @@ export interface LocaleSettings {
|
|
|
18
18
|
preference?: LocaleId;
|
|
19
19
|
}
|
|
20
20
|
/** Durable locale schema; also the wire envelope the browser scope validates against. */
|
|
21
|
-
export declare const
|
|
21
|
+
export declare const LocaleSettingsFields: {
|
|
22
|
+
preference: z<string, string, "plain">;
|
|
23
|
+
};
|
|
24
|
+
/** Schema for the shared locale preference. */
|
|
25
|
+
export declare const LocaleSettingsSchema: z<Schemastery.ObjectS<NoInfer<{
|
|
26
|
+
preference: z<string, string, "plain">;
|
|
27
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
28
|
+
preference: z<string, string, "plain">;
|
|
29
|
+
}>>, "plain">;
|
|
22
30
|
//# sourceMappingURL=locale-settings.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-locale",
|
|
3
3
|
"description": "Locale plugin: Host-backed preference, extensible language catalog, browser fallback, and typed built-in dictionaries",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.7-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -39,24 +39,25 @@
|
|
|
39
39
|
},
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
42
|
+
"@deepseek-ai/cordis": "^4.0.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/react": "~18.3.1",
|
|
46
46
|
"react": "^18.2.0",
|
|
47
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
48
|
-
"@deepseek-ai/dsh-
|
|
49
|
-
"@deepseek-ai/dsh-client-
|
|
50
|
-
"@deepseek-ai/dsh-client-
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.
|
|
52
|
-
"@deepseek-ai/dsh-client-ui-
|
|
53
|
-
"@deepseek-ai/dsh-client-
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
55
|
-
"@deepseek-ai/dsh-client-
|
|
56
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
47
|
+
"@deepseek-ai/cordis": "^4.0.3",
|
|
48
|
+
"@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
|
|
49
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.7-alpha.1",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.7-alpha.1",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
|
|
54
|
+
"@deepseek-ai/dsh-package-manifest": "^0.1.7-alpha.1",
|
|
55
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.7-alpha.1",
|
|
56
|
+
"@deepseek-ai/dsh-settings": "^0.1.7-alpha.1",
|
|
57
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
60
|
+
"@deepseek-ai/schemastery": "^3.18.3"
|
|
60
61
|
},
|
|
61
62
|
"files": [
|
|
62
63
|
"lib/index.js",
|