@hagicode/hagi18n 0.1.0-dev.1.1.45d0d2f
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.md +184 -0
- package/README_cn.md +178 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +174 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +60 -0
- package/dist/config.js +341 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/locale-toolkit.d.ts +113 -0
- package/dist/locale-toolkit.js +679 -0
- package/dist/locale-toolkit.js.map +1 -0
- package/dist/version.d.ts +7 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Hagi18n
|
|
2
|
+
|
|
3
|
+
`@hagicode/hagi18n` is a reusable YAML locale maintenance toolkit for HagiCode projects. It was abstracted from the mature Web workflow in `repos/web/buildTools`, but it runs as an independent package and CLI.
|
|
4
|
+
|
|
5
|
+
It supports:
|
|
6
|
+
|
|
7
|
+
- Auditing locale trees for missing files, extra files, missing keys, extra keys, placeholder mismatches, parse errors, and protected tokens.
|
|
8
|
+
- Repository hygiene checks for legacy locale references.
|
|
9
|
+
- Safe `sync` and `prune` mutations with dry-run defaults.
|
|
10
|
+
- Optional `hagi18n.yaml` defaults so each project can define its own locale layout.
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- Node.js 20 or newer
|
|
15
|
+
- npm for package management
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @hagicode/hagi18n
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The installed CLI command is `hagi18n`.
|
|
24
|
+
|
|
25
|
+
## YAML Locale Layout
|
|
26
|
+
|
|
27
|
+
The package expects a locale tree like this:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
src/locales/
|
|
31
|
+
en-US/
|
|
32
|
+
common.yml
|
|
33
|
+
features/editor.yml
|
|
34
|
+
zh-CN/
|
|
35
|
+
common.yml
|
|
36
|
+
features/editor.yml
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Supported files:
|
|
40
|
+
|
|
41
|
+
- `.yml`
|
|
42
|
+
- `.yaml`
|
|
43
|
+
|
|
44
|
+
Supported values:
|
|
45
|
+
|
|
46
|
+
- top-level mapping documents
|
|
47
|
+
- nested objects
|
|
48
|
+
- arrays
|
|
49
|
+
- scalar translation leaves
|
|
50
|
+
- `{{placeholder}}` interpolation tokens
|
|
51
|
+
|
|
52
|
+
Common locale aliases such as `en`, `en-us`, `zh`, and `zh-cn` are accepted and normalized to canonical locale names like `en-US` and `zh-CN`.
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
By default, the CLI looks for `hagi18n.yaml` in the current working directory. You can also pass `--config <path>`.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
localesRoot: src/locales
|
|
62
|
+
repoRoot: .
|
|
63
|
+
baseLocale: en-US
|
|
64
|
+
targetLocales:
|
|
65
|
+
- zh-CN
|
|
66
|
+
doctor:
|
|
67
|
+
excludedDirectories:
|
|
68
|
+
- .git
|
|
69
|
+
- dist
|
|
70
|
+
- node_modules
|
|
71
|
+
textFileExtensions:
|
|
72
|
+
- .ts
|
|
73
|
+
- .tsx
|
|
74
|
+
- .js
|
|
75
|
+
- .md
|
|
76
|
+
allowlist:
|
|
77
|
+
legacy-language-change-call:
|
|
78
|
+
- src/legacy-test.ts
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Precedence is:
|
|
82
|
+
|
|
83
|
+
1. CLI flags or direct API options
|
|
84
|
+
2. `hagi18n.yaml`
|
|
85
|
+
3. package defaults
|
|
86
|
+
|
|
87
|
+
Relative paths in `hagi18n.yaml` are resolved from the config file directory.
|
|
88
|
+
|
|
89
|
+
## CLI Commands
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
hagi18n info
|
|
93
|
+
hagi18n audit
|
|
94
|
+
hagi18n report
|
|
95
|
+
hagi18n doctor
|
|
96
|
+
hagi18n sync
|
|
97
|
+
hagi18n prune
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Examples:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
hagi18n audit --locales-root src/locales --base-locale en-US
|
|
104
|
+
hagi18n audit --config hagi18n.yaml --json
|
|
105
|
+
hagi18n report --config hagi18n.yaml
|
|
106
|
+
hagi18n doctor --config hagi18n.yaml
|
|
107
|
+
hagi18n sync --from en-US --to zh-CN
|
|
108
|
+
hagi18n sync --from en-US --to zh-CN --write
|
|
109
|
+
hagi18n prune --from en-US --to zh-CN --write
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`sync` and `prune` are dry-run by default. Pass `--write` to mutate files.
|
|
113
|
+
|
|
114
|
+
## Option Reference
|
|
115
|
+
|
|
116
|
+
| Option | Commands | Description |
|
|
117
|
+
| --- | --- | --- |
|
|
118
|
+
| `--config <path>` | all except `info` | Load defaults from a config file |
|
|
119
|
+
| `--locales-root <path>` | audit, report, doctor, sync, prune | Locale root directory |
|
|
120
|
+
| `--base-locale <locale>` | audit, report, doctor | Base locale for comparison |
|
|
121
|
+
| `--from <locale>` | sync, prune | Base locale for mutation commands |
|
|
122
|
+
| `--locale <locale>` | audit, report, doctor | Limit output to one or more locales |
|
|
123
|
+
| `--to <locale>` | sync, prune | Limit mutations to one or more target locales |
|
|
124
|
+
| `--repo-root <path>` | doctor | Repository root for repository scanning |
|
|
125
|
+
| `--json` | audit, doctor, sync, prune | Print JSON instead of text |
|
|
126
|
+
| `--dry-run` | sync, prune | Preview mutations without writing files |
|
|
127
|
+
| `--write` | sync, prune | Apply mutations to disk |
|
|
128
|
+
|
|
129
|
+
## JSON Output and Exit Codes
|
|
130
|
+
|
|
131
|
+
- `audit` returns exit code `0` when clean and `1` when issues exist.
|
|
132
|
+
- `report` runs the same audit and always prints JSON.
|
|
133
|
+
- `doctor` returns exit code `1` when audit issues or repository scan issues exist.
|
|
134
|
+
- `sync` and `prune` return exit code `1` only for parse or processing errors. Planned or applied mutations are reported in the summary.
|
|
135
|
+
- Command parsing failures return exit code `1` from the current `commander` integration.
|
|
136
|
+
|
|
137
|
+
The JSON payload is the same structured summary returned by the TypeScript API.
|
|
138
|
+
|
|
139
|
+
## TypeScript API
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import {
|
|
143
|
+
auditLocaleTree,
|
|
144
|
+
doctorLocaleTree,
|
|
145
|
+
formatAuditSummary,
|
|
146
|
+
pruneLocaleTree,
|
|
147
|
+
resolveHagi18nConfig,
|
|
148
|
+
syncLocaleTree
|
|
149
|
+
} from "@hagicode/hagi18n";
|
|
150
|
+
|
|
151
|
+
const audit = await auditLocaleTree({
|
|
152
|
+
localesRoot: "src/locales",
|
|
153
|
+
baseLocale: "en-US"
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
console.log(formatAuditSummary(audit));
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Main exports include:
|
|
160
|
+
|
|
161
|
+
- configuration helpers such as `findHagi18nConfigPath`, `loadHagi18nConfig`, and `resolveHagi18nConfig`
|
|
162
|
+
- locale helpers such as `normalizeLocaleName`, `listLocaleDirectories`, and `readYamlLocaleFile`
|
|
163
|
+
- workflows such as `auditLocaleTree`, `doctorLocaleTree`, `syncLocaleTree`, and `pruneLocaleTree`
|
|
164
|
+
- text formatters such as `formatAuditSummary`, `formatDoctorSummary`, and `formatMutationSummary`
|
|
165
|
+
- package metadata helpers such as `getPackageMetadata` and `createRuntimeInfo`
|
|
166
|
+
|
|
167
|
+
## Web Reference Mapping
|
|
168
|
+
|
|
169
|
+
This package preserves the maintenance model from the Web repository:
|
|
170
|
+
|
|
171
|
+
- `repos/web/buildTools/lib/i18nLocaleToolkit.mjs` -> `src/locale-toolkit.ts`
|
|
172
|
+
- `repos/web/buildTools/i18n-locale-cli.mjs` -> `src/cli.ts`
|
|
173
|
+
|
|
174
|
+
The Web project remains the behavior reference, but it does not need runtime changes to use this package later.
|
|
175
|
+
|
|
176
|
+
## Development
|
|
177
|
+
|
|
178
|
+
Run commands from `repos/hagi18n/`:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npm install
|
|
182
|
+
npm test
|
|
183
|
+
npm run build
|
|
184
|
+
```
|
package/README_cn.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Hagi18n
|
|
2
|
+
|
|
3
|
+
`@hagicode/hagi18n` 是一个可复用的 YAML 语言包维护工具,抽象自 `repos/web/buildTools` 中已经成熟的 Web i18n 工作流,但可以作为独立 npm 包和 CLI 使用。
|
|
4
|
+
|
|
5
|
+
它提供:
|
|
6
|
+
|
|
7
|
+
- 语言包树审计:缺失文件、多余文件、缺失 key、多余 key、占位符不匹配、解析错误、受保护 token 检测
|
|
8
|
+
- 仓库级 doctor 检查:扫描旧的 locale 引用方式
|
|
9
|
+
- 安全的 `sync` / `prune` 变更流程,默认 dry-run
|
|
10
|
+
- 可选的 `hagi18n.yaml` 项目配置,适配不同仓库目录结构
|
|
11
|
+
|
|
12
|
+
## 前提
|
|
13
|
+
|
|
14
|
+
- Node.js 20 或更高版本
|
|
15
|
+
- 使用 npm 管理依赖
|
|
16
|
+
|
|
17
|
+
## 安装
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @hagicode/hagi18n
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
安装后可直接使用 `hagi18n` 命令。
|
|
24
|
+
|
|
25
|
+
## YAML 目录结构
|
|
26
|
+
|
|
27
|
+
默认语言包目录形态如下:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
src/locales/
|
|
31
|
+
en-US/
|
|
32
|
+
common.yml
|
|
33
|
+
features/editor.yml
|
|
34
|
+
zh-CN/
|
|
35
|
+
common.yml
|
|
36
|
+
features/editor.yml
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
支持:
|
|
40
|
+
|
|
41
|
+
- `.yml`
|
|
42
|
+
- `.yaml`
|
|
43
|
+
- 顶层为 mapping 的 YAML 文档
|
|
44
|
+
- 嵌套对象、数组、标量翻译值
|
|
45
|
+
- `{{placeholder}}` 占位符
|
|
46
|
+
|
|
47
|
+
像 `en`、`en-us`、`zh`、`zh-cn` 这样的常见别名会自动规范化为 `en-US`、`zh-CN`。
|
|
48
|
+
|
|
49
|
+
## 配置文件
|
|
50
|
+
|
|
51
|
+
CLI 默认会在当前工作目录查找 `hagi18n.yaml`。也可以显式传入 `--config <path>`。
|
|
52
|
+
|
|
53
|
+
示例:
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
localesRoot: src/locales
|
|
57
|
+
repoRoot: .
|
|
58
|
+
baseLocale: en-US
|
|
59
|
+
targetLocales:
|
|
60
|
+
- zh-CN
|
|
61
|
+
doctor:
|
|
62
|
+
excludedDirectories:
|
|
63
|
+
- .git
|
|
64
|
+
- dist
|
|
65
|
+
- node_modules
|
|
66
|
+
textFileExtensions:
|
|
67
|
+
- .ts
|
|
68
|
+
- .tsx
|
|
69
|
+
- .js
|
|
70
|
+
- .md
|
|
71
|
+
allowlist:
|
|
72
|
+
legacy-language-change-call:
|
|
73
|
+
- src/legacy-test.ts
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
优先级:
|
|
77
|
+
|
|
78
|
+
1. CLI 参数或直接 API 传参
|
|
79
|
+
2. `hagi18n.yaml`
|
|
80
|
+
3. 包内默认值
|
|
81
|
+
|
|
82
|
+
`hagi18n.yaml` 中的相对路径会相对于配置文件所在目录解析,因此不同项目可以用它自定义 `baseLocale`、`localesRoot`、`repoRoot` 和默认目标语言目录。
|
|
83
|
+
|
|
84
|
+
## CLI 命令
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
hagi18n info
|
|
88
|
+
hagi18n audit
|
|
89
|
+
hagi18n report
|
|
90
|
+
hagi18n doctor
|
|
91
|
+
hagi18n sync
|
|
92
|
+
hagi18n prune
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
示例:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
hagi18n audit --locales-root src/locales --base-locale en-US
|
|
99
|
+
hagi18n audit --config hagi18n.yaml --json
|
|
100
|
+
hagi18n report --config hagi18n.yaml
|
|
101
|
+
hagi18n doctor --config hagi18n.yaml
|
|
102
|
+
hagi18n sync --from en-US --to zh-CN
|
|
103
|
+
hagi18n sync --from en-US --to zh-CN --write
|
|
104
|
+
hagi18n prune --from en-US --to zh-CN --write
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`sync` 和 `prune` 默认不会写盘,只有传入 `--write` 才会真正修改文件。
|
|
108
|
+
|
|
109
|
+
## 参数说明
|
|
110
|
+
|
|
111
|
+
| 参数 | 命令 | 说明 |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `--config <path>` | `info` 之外所有命令 | 加载配置文件 |
|
|
114
|
+
| `--locales-root <path>` | audit, report, doctor, sync, prune | 语言包根目录 |
|
|
115
|
+
| `--base-locale <locale>` | audit, report, doctor | 审计时使用的基准语言 |
|
|
116
|
+
| `--from <locale>` | sync, prune | 变更时使用的基准语言 |
|
|
117
|
+
| `--locale <locale>` | audit, report, doctor | 限定一个或多个目标语言 |
|
|
118
|
+
| `--to <locale>` | sync, prune | 限定一个或多个目标语言 |
|
|
119
|
+
| `--repo-root <path>` | doctor | 仓库扫描根目录 |
|
|
120
|
+
| `--json` | audit, doctor, sync, prune | 输出 JSON |
|
|
121
|
+
| `--dry-run` | sync, prune | 仅预览,不写盘 |
|
|
122
|
+
| `--write` | sync, prune | 真正写入文件 |
|
|
123
|
+
|
|
124
|
+
## JSON 输出与退出码
|
|
125
|
+
|
|
126
|
+
- `audit`:无问题退出码为 `0`,有问题为 `1`
|
|
127
|
+
- `report`:执行与 `audit` 相同的检查,但默认输出 JSON
|
|
128
|
+
- `doctor`:审计或仓库扫描存在问题时返回 `1`
|
|
129
|
+
- `sync` / `prune`:只有解析或处理错误时返回 `1`,正常的预览或写入结果都会输出摘要
|
|
130
|
+
|
|
131
|
+
JSON 输出与 TypeScript API 返回的结构化 summary 保持一致。
|
|
132
|
+
|
|
133
|
+
## TypeScript API
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import {
|
|
137
|
+
auditLocaleTree,
|
|
138
|
+
doctorLocaleTree,
|
|
139
|
+
formatAuditSummary,
|
|
140
|
+
pruneLocaleTree,
|
|
141
|
+
resolveHagi18nConfig,
|
|
142
|
+
syncLocaleTree
|
|
143
|
+
} from "@hagicode/hagi18n";
|
|
144
|
+
|
|
145
|
+
const audit = await auditLocaleTree({
|
|
146
|
+
localesRoot: "src/locales",
|
|
147
|
+
baseLocale: "en-US"
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
console.log(formatAuditSummary(audit));
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
主要导出包括:
|
|
154
|
+
|
|
155
|
+
- 配置辅助函数:`findHagi18nConfigPath`、`loadHagi18nConfig`、`resolveHagi18nConfig`
|
|
156
|
+
- locale 辅助函数:`normalizeLocaleName`、`listLocaleDirectories`、`readYamlLocaleFile`
|
|
157
|
+
- 核心流程:`auditLocaleTree`、`doctorLocaleTree`、`syncLocaleTree`、`pruneLocaleTree`
|
|
158
|
+
- 文本格式化函数:`formatAuditSummary`、`formatDoctorSummary`、`formatMutationSummary`
|
|
159
|
+
- 元数据函数:`getPackageMetadata`、`createRuntimeInfo`
|
|
160
|
+
|
|
161
|
+
## 与 Web 参考实现的对应关系
|
|
162
|
+
|
|
163
|
+
此包保留了 Web 项目的维护模型:
|
|
164
|
+
|
|
165
|
+
- `repos/web/buildTools/lib/i18nLocaleToolkit.mjs` -> `src/locale-toolkit.ts`
|
|
166
|
+
- `repos/web/buildTools/i18n-locale-cli.mjs` -> `src/cli.ts`
|
|
167
|
+
|
|
168
|
+
也就是说,这次抽象保留了 Web 的参考行为,但不会要求 `repos/web` 立即修改运行时代码。
|
|
169
|
+
|
|
170
|
+
## 开发
|
|
171
|
+
|
|
172
|
+
在 `repos/hagi18n/` 目录执行:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npm install
|
|
176
|
+
npm test
|
|
177
|
+
npm run build
|
|
178
|
+
```
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Command, CommanderError, Option } from "commander";
|
|
5
|
+
import { auditLocaleTree, createRuntimeInfo, doctorLocaleTree, formatAuditSummary, formatDoctorSummary, formatMutationSummary, packageVersion, pruneLocaleTree, syncLocaleTree } from "./index.js";
|
|
6
|
+
function collectValues(value, previous = []) {
|
|
7
|
+
previous.push(value);
|
|
8
|
+
return previous;
|
|
9
|
+
}
|
|
10
|
+
function toToolkitOptions(options) {
|
|
11
|
+
const targetLocales = [...(options.locale ?? []), ...(options.to ?? [])];
|
|
12
|
+
return {
|
|
13
|
+
configPath: options.config,
|
|
14
|
+
localesRoot: options.localesRoot,
|
|
15
|
+
repoRoot: options.repoRoot,
|
|
16
|
+
baseLocale: options.baseLocale ?? options.from,
|
|
17
|
+
targetLocales: targetLocales.length > 0 ? targetLocales : undefined
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function resolveDryRun(options) {
|
|
21
|
+
if (options.dryRun) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return !options.write;
|
|
25
|
+
}
|
|
26
|
+
function printSummary(summary, { json = false, formatter }) {
|
|
27
|
+
if (json) {
|
|
28
|
+
process.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
process.stdout.write(`${formatter(summary)}\n`);
|
|
32
|
+
}
|
|
33
|
+
function applySharedOptions(command, { includeRepoRoot = false } = {}) {
|
|
34
|
+
command
|
|
35
|
+
.addOption(new Option("--config <path>", "load defaults from a hagi18n.yaml file"))
|
|
36
|
+
.addOption(new Option("--locales-root <path>", "locale root directory"))
|
|
37
|
+
.addOption(new Option("--base-locale <locale>", "base locale to compare against"))
|
|
38
|
+
.addOption(new Option("--from <locale>", "source locale used as the structure baseline"))
|
|
39
|
+
.addOption(new Option("--locale <locale>", "limit the command to one or more locales").argParser(collectValues))
|
|
40
|
+
.addOption(new Option("--to <locale>", "target locale; can be repeated").argParser(collectValues))
|
|
41
|
+
.addOption(new Option("--json", "print machine-readable JSON output"));
|
|
42
|
+
if (includeRepoRoot) {
|
|
43
|
+
command.addOption(new Option("--repo-root <path>", "repository root for doctor scanning"));
|
|
44
|
+
}
|
|
45
|
+
return command;
|
|
46
|
+
}
|
|
47
|
+
function applyMutationOptions(command) {
|
|
48
|
+
return applySharedOptions(command)
|
|
49
|
+
.addOption(new Option("--dry-run", "preview changes without writing files"))
|
|
50
|
+
.addOption(new Option("--write", "apply file mutations"));
|
|
51
|
+
}
|
|
52
|
+
export function createCli() {
|
|
53
|
+
const program = new Command();
|
|
54
|
+
program
|
|
55
|
+
.name("hagi18n")
|
|
56
|
+
.description("Hagi18n YAML locale maintenance toolkit.")
|
|
57
|
+
.version(packageVersion, "-v, --version", "print the hagi18n version");
|
|
58
|
+
program.showHelpAfterError();
|
|
59
|
+
program.exitOverride();
|
|
60
|
+
program
|
|
61
|
+
.command("info")
|
|
62
|
+
.description("print package foundation metadata")
|
|
63
|
+
.action(() => {
|
|
64
|
+
const info = createRuntimeInfo();
|
|
65
|
+
process.stdout.write(`${JSON.stringify(info, null, 2)}\n`);
|
|
66
|
+
});
|
|
67
|
+
applySharedOptions(program
|
|
68
|
+
.command("audit")
|
|
69
|
+
.description("audit YAML locale files for drift against a base locale")
|
|
70
|
+
.action(async (options) => {
|
|
71
|
+
const summary = await auditLocaleTree(toToolkitOptions(options));
|
|
72
|
+
printSummary(summary, {
|
|
73
|
+
json: options.json,
|
|
74
|
+
formatter: formatAuditSummary
|
|
75
|
+
});
|
|
76
|
+
process.exitCode = summary.hasIssues ? 1 : 0;
|
|
77
|
+
}));
|
|
78
|
+
applySharedOptions(program
|
|
79
|
+
.command("report")
|
|
80
|
+
.description("run audit and print JSON output")
|
|
81
|
+
.action(async (options) => {
|
|
82
|
+
const summary = await auditLocaleTree(toToolkitOptions(options));
|
|
83
|
+
printSummary(summary, {
|
|
84
|
+
json: true,
|
|
85
|
+
formatter: formatAuditSummary
|
|
86
|
+
});
|
|
87
|
+
process.exitCode = summary.hasIssues ? 1 : 0;
|
|
88
|
+
}));
|
|
89
|
+
applySharedOptions(program
|
|
90
|
+
.command("doctor")
|
|
91
|
+
.description("audit locale drift and scan the repository for legacy locale references")
|
|
92
|
+
.action(async (options) => {
|
|
93
|
+
const summary = await doctorLocaleTree(toToolkitOptions(options));
|
|
94
|
+
printSummary(summary, {
|
|
95
|
+
json: options.json,
|
|
96
|
+
formatter: formatDoctorSummary
|
|
97
|
+
});
|
|
98
|
+
process.exitCode = summary.hasIssues ? 1 : 0;
|
|
99
|
+
}), { includeRepoRoot: true });
|
|
100
|
+
applyMutationOptions(program
|
|
101
|
+
.command("sync")
|
|
102
|
+
.description("add missing files and keys from the base locale")
|
|
103
|
+
.action(async (options) => {
|
|
104
|
+
const summary = await syncLocaleTree({
|
|
105
|
+
...toToolkitOptions(options),
|
|
106
|
+
dryRun: resolveDryRun(options)
|
|
107
|
+
});
|
|
108
|
+
printSummary(summary, {
|
|
109
|
+
json: options.json,
|
|
110
|
+
formatter: formatMutationSummary
|
|
111
|
+
});
|
|
112
|
+
process.exitCode = summary.hasIssues ? 1 : 0;
|
|
113
|
+
}));
|
|
114
|
+
applyMutationOptions(program
|
|
115
|
+
.command("prune")
|
|
116
|
+
.description("remove files and keys absent from the base locale")
|
|
117
|
+
.action(async (options) => {
|
|
118
|
+
const summary = await pruneLocaleTree({
|
|
119
|
+
...toToolkitOptions(options),
|
|
120
|
+
dryRun: resolveDryRun(options)
|
|
121
|
+
});
|
|
122
|
+
printSummary(summary, {
|
|
123
|
+
json: options.json,
|
|
124
|
+
formatter: formatMutationSummary
|
|
125
|
+
});
|
|
126
|
+
process.exitCode = summary.hasIssues ? 1 : 0;
|
|
127
|
+
}));
|
|
128
|
+
program.addHelpText("after", `
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
hagi18n audit --locales-root src/locales --base-locale en-US
|
|
132
|
+
hagi18n report --config hagi18n.yaml
|
|
133
|
+
hagi18n doctor --repo-root . --locales-root src/locales
|
|
134
|
+
hagi18n sync --from en-US --to zh-CN --dry-run
|
|
135
|
+
hagi18n prune --from en-US --to zh-CN --write
|
|
136
|
+
`);
|
|
137
|
+
program.action(() => {
|
|
138
|
+
program.outputHelp();
|
|
139
|
+
});
|
|
140
|
+
return program;
|
|
141
|
+
}
|
|
142
|
+
export async function runCli(argv = process.argv) {
|
|
143
|
+
const program = createCli();
|
|
144
|
+
process.exitCode = 0;
|
|
145
|
+
try {
|
|
146
|
+
await program.parseAsync(argv);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (error instanceof CommanderError) {
|
|
150
|
+
process.exitCode = error.exitCode;
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
export function isCliEntrypoint(moduleUrl = import.meta.url, argvPath = process.argv[1]) {
|
|
157
|
+
if (!argvPath) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(argvPath);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (isCliEntrypoint()) {
|
|
168
|
+
runCli().catch((error) => {
|
|
169
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
170
|
+
process.stderr.write(`${message}\n`);
|
|
171
|
+
process.exitCode = 1;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export declare const DEFAULT_BASE_LOCALE = "en-US";
|
|
2
|
+
export declare const DEFAULT_CONFIG_FILE_NAMES: readonly ["hagi18n.yaml", "hagi18n.yml"];
|
|
3
|
+
export declare const DEFAULT_DOCTOR_EXCLUDED_DIRECTORIES: readonly [".git", "coverage", "dist", "indexGenerator", "node_modules", "public"];
|
|
4
|
+
export declare const DEFAULT_DOCTOR_TEXT_FILE_EXTENSIONS: readonly [".cjs", ".js", ".jsx", ".md", ".mjs", ".ts", ".tsx"];
|
|
5
|
+
export declare const DEFAULT_DOCTOR_EXCLUDED_PATH_PREFIXES: readonly ["src/generated/"];
|
|
6
|
+
export interface DoctorScanRuleConfig {
|
|
7
|
+
id: string;
|
|
8
|
+
message: string;
|
|
9
|
+
pattern: string;
|
|
10
|
+
flags?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface Hagi18nDoctorConfig {
|
|
13
|
+
excludedDirectories?: string[];
|
|
14
|
+
textFileExtensions?: string[];
|
|
15
|
+
excludedPathPrefixes?: string[];
|
|
16
|
+
allowlist?: Record<string, string[]>;
|
|
17
|
+
scanRules?: DoctorScanRuleConfig[];
|
|
18
|
+
}
|
|
19
|
+
export interface Hagi18nConfig {
|
|
20
|
+
localesRoot?: string;
|
|
21
|
+
repoRoot?: string;
|
|
22
|
+
baseLocale?: string;
|
|
23
|
+
targetLocales?: string[];
|
|
24
|
+
doctor?: Hagi18nDoctorConfig;
|
|
25
|
+
}
|
|
26
|
+
export interface ResolvedDoctorScanRule extends DoctorScanRuleConfig {
|
|
27
|
+
regex: RegExp;
|
|
28
|
+
}
|
|
29
|
+
export interface ResolvedHagi18nDoctorConfig {
|
|
30
|
+
excludedDirectories: string[];
|
|
31
|
+
textFileExtensions: string[];
|
|
32
|
+
excludedPathPrefixes: string[];
|
|
33
|
+
allowlist: Record<string, string[]>;
|
|
34
|
+
scanRules: ResolvedDoctorScanRule[];
|
|
35
|
+
}
|
|
36
|
+
export interface ResolvedHagi18nConfig {
|
|
37
|
+
cwd: string;
|
|
38
|
+
configPath: string | null;
|
|
39
|
+
localesRoot: string;
|
|
40
|
+
repoRoot: string;
|
|
41
|
+
baseLocale: string;
|
|
42
|
+
targetLocales: string[];
|
|
43
|
+
doctor: ResolvedHagi18nDoctorConfig;
|
|
44
|
+
}
|
|
45
|
+
export interface LoadHagi18nConfigOptions {
|
|
46
|
+
cwd?: string;
|
|
47
|
+
configPath?: string | null;
|
|
48
|
+
}
|
|
49
|
+
export interface LoadHagi18nConfigResult {
|
|
50
|
+
cwd: string;
|
|
51
|
+
configPath: string | null;
|
|
52
|
+
config: Hagi18nConfig | null;
|
|
53
|
+
}
|
|
54
|
+
export interface ResolveHagi18nConfigOptions extends LoadHagi18nConfigOptions, Hagi18nConfig {
|
|
55
|
+
}
|
|
56
|
+
export declare const DEFAULT_DOCTOR_SCAN_RULES: DoctorScanRuleConfig[];
|
|
57
|
+
export declare const DEFAULT_DOCTOR_ALLOWLIST: Record<string, string[]>;
|
|
58
|
+
export declare function findHagi18nConfigPath(options?: LoadHagi18nConfigOptions): Promise<string | null>;
|
|
59
|
+
export declare function loadHagi18nConfig(options?: LoadHagi18nConfigOptions): Promise<LoadHagi18nConfigResult>;
|
|
60
|
+
export declare function resolveHagi18nConfig(options?: ResolveHagi18nConfigOptions): Promise<ResolvedHagi18nConfig>;
|