@pactor-app/i18n 0.0.0-stage → 1.55.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pactor Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,119 @@
1
- # Temporary Holding Version
1
+ # @pactor-app/i18n
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Pactor 服务端国际化纯逻辑包:请求语言识别(LocaleResolver)与语言包 Provider(统一契约 + YAML / API 两种来源)。
4
+
5
+ - 不依赖 `next` 或任何宿主 API;请求信息(path/query/cookie/Accept-Language)以普通对象传入。
6
+ - 默认入口 `@pactor-app/i18n` 为浏览器安全纯逻辑,不含 `yaml`。
7
+ - `YamlMessageProvider` 仅从 Node 入口 `@pactor-app/i18n/node` 导出,`yaml` 为 peerDependency。
8
+
9
+ ## LocaleResolver
10
+
11
+ ```ts
12
+ import { resolveRequestLocale } from '@pactor-app/i18n';
13
+
14
+ const result = resolveRequestLocale(
15
+ { queryLocale: 'en-US', cookieLocale: 'zh-CN', acceptLanguage: 'zh-CN' },
16
+ {
17
+ supportedLocales: ['zh-CN', 'en-US'],
18
+ defaultLocale: 'zh-CN',
19
+ defaultStrategy: 'system', // 或 'application'
20
+ aliases: { en: 'en-US' },
21
+ fallbackLocales: { 'fr-CA': ['fr'] },
22
+ },
23
+ );
24
+ // → { locale: 'en-US', source: 'url-query', explicit: true }
25
+ ```
26
+
27
+ 优先级:URL path/query > `locale` Cookie > Accept-Language(q 权重、忽略 q=0、`*` 用默认语言、同权重按出现顺序)> 应用 defaultLocale。显式值非法/不支持/冲突抛 `I18nError`(code:`INVALID_LOCALE` / `UNSUPPORTED_LOCALE` / `LOCALE_CONFLICT` / `DUPLICATE_LOCALE_PARAM`);失效 Cookie 忽略并继续解析链。
28
+
29
+ ## MessageProvider 契约
30
+
31
+ ```ts
32
+ interface MessageProvider {
33
+ load(input: {
34
+ appId: string;
35
+ tenantId?: string;
36
+ locale: string;
37
+ namespaces: string[];
38
+ release: string;
39
+ }): Promise<MessageBundleSet>; // @pactor-app/core 类型
40
+ }
41
+ ```
42
+
43
+ ## ApiMessageProvider(默认入口)
44
+
45
+ ```ts
46
+ import { ApiMessageProvider } from '@pactor-app/i18n';
47
+
48
+ const provider = new ApiMessageProvider({
49
+ endpoint: 'https://language-service.example/v1/i18n/bundles',
50
+ fetch: globalThis.fetch.bind(globalThis),
51
+ headers: { Authorization: 'Bearer ...' }, // 服务端凭据
52
+ });
53
+ const bundles = await provider.load({ appId, locale, namespaces, release });
54
+ ```
55
+
56
+ 校验 HTTP 状态与 `Content-Type: application/json`、解析 JSON、校验 envelope(`format: 'simple-v1'` 与协议字段);支持 ETag/If-None-Match,304 时复用同缓存键的已校验包(`loadWithCacheInfo` 返回 `{ bundleSet, etag, notModified }`)。
57
+
58
+ ## YamlMessageProvider(Node 入口)
59
+
60
+ ```ts
61
+ import { readFile } from 'node:fs/promises';
62
+ import { YamlMessageProvider } from '@pactor-app/i18n/node';
63
+
64
+ const provider = new YamlMessageProvider({
65
+ manifest, // 语言包发布清单普通对象(appId/release/locales→namespace→{path, version})
66
+ readFile: (path) => readFile(join(baseDir, path), 'utf8'),
67
+ });
68
+ ```
69
+
70
+ 严格解析 YAML:语法错误、重复 key、自定义标签、非字符串词条、超出大小/别名展开限制均拒绝;namespace 间完整 key 冲突检查;输出与 API 来源相同的 `MessageBundleSet`。文件路径只取自受信任清单(校验为安全相对路径),不用用户输入拼接。
71
+
72
+ ## TranslatableFieldRegistry(可翻译字段注册表)
73
+
74
+ 按组件 type / 动作名注册可翻译字段路径(支持 `props.text` 与数组通配 `props.columns[*].title` 形态):
75
+
76
+ ```ts
77
+ import { createDefaultTranslatableFieldRegistry } from '@pactor-app/i18n';
78
+
79
+ const registry = createDefaultTranslatableFieldRegistry();
80
+ registry.register('Card', ['props.title', 'props.items[*].label']);
81
+ registry.registerAction('notify', 'params.text');
82
+ ```
83
+
84
+ 内置注册 Button/Text/Input/Table 列/Select 选项的常用展示字段(`text`、`label`、`placeholder`、`description`、`ariaLabel`、`columns[*].title`、`options[*].label`)与 `message`/`confirm` 动作参数。DslLocalizer 只转换注册字段;未注册字段出现 `$i18n` 抛 `UNREGISTERED_I18N_FIELD`。
85
+
86
+ ## localizePageDsl(纯转换入口)
87
+
88
+ ```ts
89
+ import { localizePageDsl } from '@pactor-app/i18n';
90
+
91
+ const schema = localizePageDsl({
92
+ page: authorizedPage, // 作者态 Page DSL(可含顶层 i18n 字段)
93
+ locale: 'en-US',
94
+ defaultLocale: 'zh-CN',
95
+ languageBundles: { current: englishBundleSet, fallbacks: [chineseBundleSet] },
96
+ registry, // 可选,缺省用内置注册表
97
+ onDiagnostic, // 可选,missing-translation / missing-param 诊断
98
+ });
99
+ ```
100
+
101
+ 统一处理 `$i18n.messages`、`$i18n.key` 与页面 `i18n.bindings` 三种声明;查找顺序为 当前 locale → 显式语言级回退(fallbacks 有序)→ defaultLocale → 描述 fallback(bindings 未显式配置时用目标字段原始字符串)→ key;空字符串视为有效翻译。返回新对象,不修改输入;静态结果输出字符串;含 `${...}` 运行时参数时编译为 `{ $localizedText: { template, params } }`;词条/fallback 含字面量 `${` 抛 `UNSAFE_MESSAGE`。
102
+
103
+ ## localizeYamlResource(Node 入口,YAML 资源编排)
104
+
105
+ ```ts
106
+ import { readFile } from 'node:fs/promises';
107
+ import { localizeYamlResource, YamlMessageProvider } from '@pactor-app/i18n/node';
108
+
109
+ const result = await localizeYamlResource({
110
+ loadText: () => readFile('pages/editor.page.yaml', 'utf8'),
111
+ provider, // YamlMessageProvider / ApiMessageProvider 均可
112
+ appId, release, locale, defaultLocale,
113
+ fallbackLocales: ['fr'], // 可选,显式语言级回退
114
+ prune, // 可选,会话裁剪钩子
115
+ });
116
+ // → { schema: 已本地化 Page DSL, localization: { locale, defaultLocale, release, bundleVersions } }
117
+ ```
118
+
119
+ 流程:加载文本 → 严格解析(默认 `parsePageYamlStrict`,可注入)→ 作者态校验 → 可选裁剪 → 提取 namespaces → Provider 一次性批量加载当前与回退语言包 → localizePageDsl → 下发态校验。作者态/下发态校验失败抛 `PAGE_DSL_INVALID`,解析失败抛 `PAGE_YAML_INVALID`,加载失败抛 `PAGE_LOAD_FAILED`。