ai-i18n-tools 1.2.8 → 1.3.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.md +7 -5
- package/dist/build-info.generated.d.ts +1 -1
- package/dist/build-info.generated.js +1 -1
- package/dist/cli/check-markdown.d.ts +25 -0
- package/dist/cli/check-markdown.d.ts.map +1 -0
- package/dist/cli/check-markdown.js +78 -0
- package/dist/cli/check-markdown.js.map +1 -0
- package/dist/cli/clean-temp.d.ts +17 -0
- package/dist/cli/clean-temp.d.ts.map +1 -0
- package/dist/cli/clean-temp.js +109 -0
- package/dist/cli/clean-temp.js.map +1 -0
- package/dist/cli/doc-translate.d.ts +10 -1
- package/dist/cli/doc-translate.d.ts.map +1 -1
- package/dist/cli/doc-translate.js +50 -8
- package/dist/cli/doc-translate.js.map +1 -1
- package/dist/cli/index.js +46 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/cache.d.ts +20 -1
- package/dist/core/cache.d.ts.map +1 -1
- package/dist/core/cache.js +124 -1
- package/dist/core/cache.js.map +1 -1
- package/dist/core/doc-file-tracking.d.ts +5 -0
- package/dist/core/doc-file-tracking.d.ts.map +1 -1
- package/dist/core/doc-file-tracking.js +13 -0
- package/dist/core/doc-file-tracking.js.map +1 -1
- package/dist/core/types.d.ts +24 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +5 -0
- package/dist/core/types.js.map +1 -1
- package/dist/edit-cache-app/app.js +215 -0
- package/dist/edit-cache-app/index.html +60 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/processors/emphasis-placeholders.d.ts +32 -0
- package/dist/processors/emphasis-placeholders.d.ts.map +1 -1
- package/dist/processors/emphasis-placeholders.js +35 -5
- package/dist/processors/emphasis-placeholders.js.map +1 -1
- package/dist/processors/markdown-source-diagnostics.d.ts +27 -0
- package/dist/processors/markdown-source-diagnostics.d.ts.map +1 -0
- package/dist/processors/markdown-source-diagnostics.js +324 -0
- package/dist/processors/markdown-source-diagnostics.js.map +1 -0
- package/dist/server/translation-editor.d.ts.map +1 -1
- package/dist/server/translation-editor.js +50 -1
- package/dist/server/translation-editor.js.map +1 -1
- package/docs/GETTING_STARTED.md +20 -7
- package/docs/ai-i18n-tools-context.md +105 -55
- package/docs/translation-cache-editor.png +0 -0
- package/package.json +2 -2
- package/translated-docs/README.de.md +7 -5
- package/translated-docs/README.es.md +7 -5
- package/translated-docs/README.fr.md +7 -5
- package/translated-docs/README.hi.md +7 -5
- package/translated-docs/README.ja.md +7 -5
- package/translated-docs/README.ko.md +7 -5
- package/translated-docs/README.pt-BR.md +7 -5
- package/translated-docs/README.zh-CN.md +7 -5
- package/translated-docs/README.zh-TW.md +7 -5
- package/translated-docs/docs/GETTING_STARTED.de.md +23 -10
- package/translated-docs/docs/GETTING_STARTED.es.md +20 -7
- package/translated-docs/docs/GETTING_STARTED.fr.md +22 -9
- package/translated-docs/docs/GETTING_STARTED.hi.md +22 -9
- package/translated-docs/docs/GETTING_STARTED.ja.md +22 -9
- package/translated-docs/docs/GETTING_STARTED.ko.md +21 -8
- package/translated-docs/docs/GETTING_STARTED.pt-BR.md +21 -8
- package/translated-docs/docs/GETTING_STARTED.zh-CN.md +22 -9
- package/translated-docs/docs/GETTING_STARTED.zh-TW.md +26 -13
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](./LICENSE)
|
|
8
8
|
[](https://github.com/wsj-br/ai-i18n-tools/actions/workflows/ci.yml)
|
|
9
9
|
|
|
10
|
-
CLI and
|
|
10
|
+
CLI and toolkit for internationalizing JavaScript/TypeScript applications and documentation sites. Extracts UI strings, translates them using large language models via OpenRouter, and generates locale-ready JSON files for i18next. Also includes pipelines for markdown, Docusaurus JSON, and standalone SVG assets.
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
<small>**Read in other languages:** </small>
|
|
@@ -54,7 +54,7 @@ Both workflows share a single `ai-i18n-tools.config.json` file and can be used i
|
|
|
54
54
|
<a id="installation"></a>
|
|
55
55
|
## Installation
|
|
56
56
|
|
|
57
|
-
The published package is **ESM-only** (`"type": "module"`). Use `import` from Node.js, bundlers, or `import()` — `require('ai-i18n-tools')` **is not supported.** The package declares
|
|
57
|
+
The published package is **ESM-only** (`"type": "module"`). Use `import` from Node.js, bundlers, or `import()` — `require('ai-i18n-tools')` **is not supported.** The package declares `engines.node` `>=22.16.0`; older Node.js versions are unsupported.
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
60
|
npm install ai-i18n-tools
|
|
@@ -80,7 +80,7 @@ npx ai-i18n-tools sync # or: pnpm exec ai-i18n-tools sync
|
|
|
80
80
|
|
|
81
81
|
The package manager writes `node_modules/.bin/ai-i18n-tools` with the correct permissions on Linux and macOS and `.cmd` / `.ps1` shims on Windows; script runners pick it up automatically.
|
|
82
82
|
|
|
83
|
-
**Bare** `ai-i18n-tools` **in the terminal
|
|
83
|
+
**Bare** `ai-i18n-tools` **in the terminal:** `package.json` scripts already run with `node_modules/.bin` on `PATH`, so commands like `pnpm run i18n:sync` invoke the CLI without typing `npx`. To run `ai-i18n-tools` directly in an interactive shell (from the project root, after a local install), prepend the local bin directory to `PATH`:
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
86
|
# bash/zsh — project root
|
|
@@ -94,7 +94,7 @@ $env:Path = "$PWD\node_modules\.bin;$env:Path"
|
|
|
94
94
|
ai-i18n-tools sync
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
With [direnv](https://direnv.net/), add `PATH_add node_modules/.bin` to a `.envrc` in the project root so the bare command is available after `cd` into the repo. Without adjusting `PATH`, keep using `npx ai-i18n-tools …` or `pnpm exec ai-i18n-tools …`.
|
|
97
|
+
With [**direnv**](https://direnv.net/), add `PATH_add node_modules/.bin` to a `.envrc` in the project root so the bare command is available after `cd` into the repo. Without adjusting `PATH`, keep using `npx ai-i18n-tools …` or `pnpm exec ai-i18n-tools …`.
|
|
98
98
|
|
|
99
99
|
**Zero-install one-off** — `npx ai-i18n-tools <cmd>` or `pnpm dlx ai-i18n-tools <cmd>` (downloads the package for that invocation; no entry in `package.json`).
|
|
100
100
|
|
|
@@ -111,7 +111,7 @@ export OPENROUTER_API_KEY=sk-or-v1-your-key-here
|
|
|
111
111
|
<a id="openrouter"></a>
|
|
112
112
|
## OpenRouter
|
|
113
113
|
|
|
114
|
-
Commands that call OpenRouter (`translate-ui`, `translate-docs`, `sync`, `check-models`, and related scripts) need `OPENROUTER_API_KEY` in the environment.
|
|
114
|
+
Commands that call OpenRouter (`translate-ui`, `translate-docs`, `sync`, `check-models`, and related scripts) need `OPENROUTER_API_KEY` in the environment. `check-markdown` does not use OpenRouter.
|
|
115
115
|
|
|
116
116
|
In `ai-i18n-tools.config.json`, the `openrouter` object includes model lists, `baseUrl`, `maxTokens`, `temperature`, and `requestTimeoutMs`: the maximum time in milliseconds to wait for each HTTP request to OpenRouter (chat completions and internal `GET /models` calls). The default is `30000` (30 seconds).
|
|
117
117
|
|
|
@@ -226,6 +226,7 @@ ai-i18n-tools translate-docs [--locale <code>] Translate documentation (mar
|
|
|
226
226
|
--prompt-format (xml | json-array | json-object)
|
|
227
227
|
ai-i18n-tools write-heading-ids … Insert HTML anchor lines before ATX headings in .md/.mdx (documentations[])
|
|
228
228
|
ai-i18n-tools strip-md-bold-inline … Remove bold (**) around inline code in markdown/MDX (documentations[])
|
|
229
|
+
ai-i18n-tools check-markdown [-p|--path <path>] [--json] [--no-cache] Scan documentation markdown for delimiter / inline-code issues and strong-outside-code or strong-outside-link patterns; refresh SQLite markdown_source_issues; exit 1 if any issue
|
|
229
230
|
ai-i18n-tools translate-svg [--locale <code>] Standalone SVG assets (features.translateSVG + config.svg); see --no-cache
|
|
230
231
|
ai-i18n-tools translate-ui [--locale <code>] Translate UI strings only; see --force, --dry-run
|
|
231
232
|
ai-i18n-tools lint-source … Run extract, then LLM review of source-locale UI strings (OpenRouter)
|
|
@@ -235,6 +236,7 @@ ai-i18n-tools status [--max-columns <n>] UI strings per locale; markdown per f
|
|
|
235
236
|
ai-i18n-tools statistics [--max-columns <n>] Documentation cache + strings.json aggregates (same as editor Statistics)
|
|
236
237
|
ai-i18n-tools editor Open cache/glossary web editor
|
|
237
238
|
ai-i18n-tools cleanup [--dry-run] [--no-backup] [--backup <path>] Runs sync --force-update, then cleans stale + orphaned cache rows; backs up SQLite by default
|
|
239
|
+
ai-i18n-tools clean-temp [-r|--root <path>] [-f|--force] [--dry-run] List *.log and cache.db.backup*.sqlite; delete after `y`, with `-f`, or skip if none match
|
|
238
240
|
ai-i18n-tools glossary-generate Create empty glossary CSV template
|
|
239
241
|
```
|
|
240
242
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { I18nConfig } from "../core/types.js";
|
|
2
|
+
export interface CheckMarkdownOptions {
|
|
3
|
+
cwd: string;
|
|
4
|
+
config: I18nConfig;
|
|
5
|
+
pathFilter?: string;
|
|
6
|
+
json: boolean;
|
|
7
|
+
noCache: boolean;
|
|
8
|
+
verbose: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface CheckMarkdownJsonRow {
|
|
11
|
+
filepath: string;
|
|
12
|
+
displayPath: string;
|
|
13
|
+
line: number | null;
|
|
14
|
+
issueCode: string;
|
|
15
|
+
detail: string;
|
|
16
|
+
sourceHash: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Scan configured documentation markdown for delimiter / inline-code issues; optional SQLite refresh.
|
|
20
|
+
* @returns exit code 0 = no issues, 1 = at least one issue
|
|
21
|
+
*/
|
|
22
|
+
export declare function runCheckMarkdown(opts: CheckMarkdownOptions): Promise<{
|
|
23
|
+
exitCode: number;
|
|
24
|
+
}>;
|
|
25
|
+
//# sourceMappingURL=check-markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-markdown.d.ts","sourceRoot":"","sources":["../../src/cli/check-markdown.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAenD,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAuEhG"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { toDocTranslateConfig } from "../core/config.js";
|
|
5
|
+
import { TranslationCache } from "../core/cache.js";
|
|
6
|
+
import { documentationFileTrackingKey } from "../core/doc-file-tracking.js";
|
|
7
|
+
import { MarkdownExtractor } from "../extractors/markdown-extractor.js";
|
|
8
|
+
import { collectMarkdownIssuesForSegment } from "../processors/markdown-source-diagnostics.js";
|
|
9
|
+
import { collectFilesByExtension } from "./file-utils.js";
|
|
10
|
+
import { loadTranslateIgnore, isIgnored } from "../utils/ignore-parser.js";
|
|
11
|
+
import { matchesPathFilter, buildMarkdownExtractOpts } from "./doc-translate.js";
|
|
12
|
+
function filterIgnoredFiles(files, cwd) {
|
|
13
|
+
const ig = loadTranslateIgnore(".translate-ignore", cwd);
|
|
14
|
+
return files.filter((f) => !isIgnored(ig, path.join(cwd, f), cwd));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Scan configured documentation markdown for delimiter / inline-code issues; optional SQLite refresh.
|
|
18
|
+
* @returns exit code 0 = no issues, 1 = at least one issue
|
|
19
|
+
*/
|
|
20
|
+
export async function runCheckMarkdown(opts) {
|
|
21
|
+
const projectRoot = opts.cwd;
|
|
22
|
+
const jsonRows = [];
|
|
23
|
+
let issueCount = 0;
|
|
24
|
+
const cache = !opts.noCache && opts.config.cacheDir
|
|
25
|
+
? new TranslationCache(path.join(projectRoot, opts.config.cacheDir))
|
|
26
|
+
: null;
|
|
27
|
+
try {
|
|
28
|
+
for (let bi = 0; bi < opts.config.documentations.length; bi++) {
|
|
29
|
+
const block = opts.config.documentations[bi];
|
|
30
|
+
const view = toDocTranslateConfig(opts.config, block);
|
|
31
|
+
const md = filterIgnoredFiles(collectFilesByExtension(block.contentPaths, [".md", ".mdx"], projectRoot), projectRoot).filter((r) => matchesPathFilter(r, opts.pathFilter));
|
|
32
|
+
const mdExtractOpts = buildMarkdownExtractOpts(view.documentation);
|
|
33
|
+
const extractor = new MarkdownExtractor();
|
|
34
|
+
for (const relPath of md) {
|
|
35
|
+
const abs = path.join(projectRoot, relPath);
|
|
36
|
+
const content = fs.readFileSync(abs, "utf8");
|
|
37
|
+
const segments = extractor.extract(content, relPath, mdExtractOpts);
|
|
38
|
+
const trackKey = documentationFileTrackingKey(bi, relPath);
|
|
39
|
+
const rows = segments.flatMap((s) => collectMarkdownIssuesForSegment(s, trackKey));
|
|
40
|
+
if (cache) {
|
|
41
|
+
cache.replaceMarkdownIssuesForFilepath(trackKey, rows);
|
|
42
|
+
}
|
|
43
|
+
for (const row of rows) {
|
|
44
|
+
issueCount++;
|
|
45
|
+
if (opts.json) {
|
|
46
|
+
jsonRows.push({
|
|
47
|
+
filepath: row.filepath,
|
|
48
|
+
displayPath: relPath,
|
|
49
|
+
line: row.startLine,
|
|
50
|
+
issueCode: row.issueCode,
|
|
51
|
+
detail: row.detail,
|
|
52
|
+
sourceHash: row.sourceHash,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const line = row.startLine ?? "?";
|
|
57
|
+
console.error(chalk.cyan(`${relPath}:${line}`) +
|
|
58
|
+
chalk.yellow(`: [${row.issueCode}] ${row.detail}${opts.verbose ? ` (hash ${row.sourceHash})` : ""}`));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
cache?.close();
|
|
66
|
+
}
|
|
67
|
+
if (opts.json) {
|
|
68
|
+
console.log(JSON.stringify({ issues: jsonRows, count: issueCount }, null, 2));
|
|
69
|
+
}
|
|
70
|
+
else if (issueCount > 0) {
|
|
71
|
+
console.error(chalk.red(`\n❌ check-markdown: ${issueCount} issue(s) in documentation sources`));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
console.log(chalk.green("✅ check-markdown: no markdown source issues found"));
|
|
75
|
+
}
|
|
76
|
+
return { exitCode: issueCount > 0 ? 1 : 0 };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=check-markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-markdown.js","sourceRoot":"","sources":["../../src/cli/check-markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Basenames matched by `find … \( -name '*.log' -o -name 'cache.db.backup*.sqlite' \)`. */
|
|
2
|
+
export declare function matchesCleanTempBasename(basename: string): boolean;
|
|
3
|
+
/** Print path like `find . -print` (posix-style `./…` under `rootAbs`). */
|
|
4
|
+
export declare function formatFindPrintLine(rootAbs: string, fileAbs: string): string;
|
|
5
|
+
export type RunCleanTempOptions = {
|
|
6
|
+
rootDir: string;
|
|
7
|
+
/** When true, skip the prompt and do not delete (list only). Overrides `force`. */
|
|
8
|
+
dryRun?: boolean;
|
|
9
|
+
/** When true, delete without prompting (after listing). Ignored when `dryRun` is true. */
|
|
10
|
+
force?: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Lists `*.log` and `cache.db.backup*.sqlite` under `rootDir`, prints paths like `find -print`,
|
|
14
|
+
* then prompts unless `force` or there are no matches; deletes on exact `y`, or immediately when `force`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runCleanTemp(opts: RunCleanTempOptions): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=clean-temp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clean-temp.d.ts","sourceRoot":"","sources":["../../src/cli/clean-temp.ts"],"names":[],"mappings":"AAIA,4FAA4F;AAC5F,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIlE;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAK5E;AA+CD,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyC3E"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import readline from "node:readline/promises";
|
|
4
|
+
/** Basenames matched by `find … \( -name '*.log' -o -name 'cache.db.backup*.sqlite' \)`. */
|
|
5
|
+
export function matchesCleanTempBasename(basename) {
|
|
6
|
+
if (basename.endsWith(".log"))
|
|
7
|
+
return true;
|
|
8
|
+
if (basename.startsWith("cache.db.backup") && basename.endsWith(".sqlite"))
|
|
9
|
+
return true;
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
/** Print path like `find . -print` (posix-style `./…` under `rootAbs`). */
|
|
13
|
+
export function formatFindPrintLine(rootAbs, fileAbs) {
|
|
14
|
+
const rel = path.relative(rootAbs, fileAbs);
|
|
15
|
+
const norm = rel.split(path.sep).join("/");
|
|
16
|
+
if (norm === "" || norm === ".")
|
|
17
|
+
return "./";
|
|
18
|
+
return `./${norm}`;
|
|
19
|
+
}
|
|
20
|
+
async function collectCleanTempFiles(rootAbs) {
|
|
21
|
+
const out = [];
|
|
22
|
+
async function visit(dir) {
|
|
23
|
+
let entries;
|
|
24
|
+
try {
|
|
25
|
+
entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
for (const ent of entries) {
|
|
31
|
+
const full = path.join(dir, ent.name);
|
|
32
|
+
if (ent.isSymbolicLink()) {
|
|
33
|
+
let st;
|
|
34
|
+
try {
|
|
35
|
+
st = await fs.promises.lstat(full);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (st.isDirectory())
|
|
41
|
+
continue;
|
|
42
|
+
if (matchesCleanTempBasename(ent.name))
|
|
43
|
+
out.push(full);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (ent.isDirectory()) {
|
|
47
|
+
await visit(full);
|
|
48
|
+
}
|
|
49
|
+
else if (ent.isFile() && matchesCleanTempBasename(ent.name)) {
|
|
50
|
+
out.push(full);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
await visit(rootAbs);
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
async function promptDeleteConfirmed() {
|
|
58
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
59
|
+
try {
|
|
60
|
+
const ans = await rl.question("\nDelete these files? (y/n) ");
|
|
61
|
+
return ans === "y";
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
rl.close();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Lists `*.log` and `cache.db.backup*.sqlite` under `rootDir`, prints paths like `find -print`,
|
|
69
|
+
* then prompts unless `force` or there are no matches; deletes on exact `y`, or immediately when `force`.
|
|
70
|
+
*/
|
|
71
|
+
export async function runCleanTemp(opts) {
|
|
72
|
+
const rootAbs = path.resolve(opts.rootDir);
|
|
73
|
+
if (!fs.existsSync(rootAbs)) {
|
|
74
|
+
console.error(`Directory not found: ${rootAbs}`);
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const st = await fs.promises.stat(rootAbs);
|
|
79
|
+
if (!st.isDirectory()) {
|
|
80
|
+
console.error(`Not a directory: ${rootAbs}`);
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const files = await collectCleanTempFiles(rootAbs);
|
|
85
|
+
for (const abs of files) {
|
|
86
|
+
console.log(formatFindPrintLine(rootAbs, abs));
|
|
87
|
+
}
|
|
88
|
+
if (opts.dryRun) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (files.length === 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const ok = opts.force ? true : await promptDeleteConfirmed();
|
|
95
|
+
if (!ok) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const abs of files) {
|
|
99
|
+
try {
|
|
100
|
+
await fs.promises.unlink(abs);
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
104
|
+
console.error(`Failed to delete ${formatFindPrintLine(rootAbs, abs)}: ${msg}`);
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=clean-temp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clean-temp.js","sourceRoot":"","sources":["../../src/cli/clean-temp.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type { DocSegmentTranslation, I18nConfig, I18nDocTranslateConfig } from "../core/types.js";
|
|
1
|
+
import type { DocSegmentTranslation, DocumentationBlock, I18nConfig, I18nDocTranslateConfig } from "../core/types.js";
|
|
2
2
|
export type { DocSegmentTranslation };
|
|
3
3
|
import { TranslationCache } from "../core/cache.js";
|
|
4
|
+
import type { MarkdownExtractOptions } from "../extractors/markdown-extractor.js";
|
|
4
5
|
import { PlaceholderHandler } from "../processors/placeholder-handler.js";
|
|
5
6
|
import { Glossary } from "../glossary/glossary.js";
|
|
6
7
|
import { OpenRouterClient } from "../api/openrouter.js";
|
|
7
8
|
import type { DocumentBatchResponseFormat } from "../core/prompt-builder.js";
|
|
8
9
|
import { AsyncMutex } from "../utils/concurrency.js";
|
|
10
|
+
/** Same segment-extraction options as `translate-docs` for one documentation block. */
|
|
11
|
+
export declare function buildMarkdownExtractOpts(documentation: DocumentationBlock): MarkdownExtractOptions | undefined;
|
|
9
12
|
/** Batch segment prompt/response shape for `translate-docs --prompt-format`. */
|
|
10
13
|
export type TranslatePromptFormat = "xml" | "json-array" | "json-object";
|
|
11
14
|
export declare function translatePromptFormatToResponseFormat(fmt: TranslatePromptFormat | undefined): DocumentBatchResponseFormat;
|
|
@@ -96,6 +99,12 @@ export declare function jsonFileProjectRelativePath(projectRoot: string, jsonAbs
|
|
|
96
99
|
* Empty input or a path equal to the project root means no filter (translate everything).
|
|
97
100
|
*/
|
|
98
101
|
export declare function normalizePathFilterForProjectRoot(projectRoot: string, raw: string | undefined): string | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Locale-independent: read source, extract segments (same options as translation), then refresh
|
|
104
|
+
* `markdown_source_issues` and stderr warnings. Call once per markdown file before parallel
|
|
105
|
+
* per-locale work so diagnostics are not repeated for every locale.
|
|
106
|
+
*/
|
|
107
|
+
export declare function scanAndRecordMarkdownSourceIssuesForTranslate(absSource: string, relPath: string, config: I18nDocTranslateConfig, cache: TranslationCache, opts: TranslateRunOptions): Promise<void>;
|
|
99
108
|
export declare function translateMarkdownFile(absSource: string, relPath: string, locale: string, config: I18nDocTranslateConfig, cache: TranslationCache | null, client: OpenRouterClient | null, glossary: Glossary, opts: TranslateRunOptions, hitKeys: Set<string>, translatedMarkdownRelPaths: ReadonlySet<string>): Promise<{
|
|
100
109
|
skipped: boolean;
|
|
101
110
|
totals: TranslateTotals;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doc-translate.d.ts","sourceRoot":"","sources":["../../src/cli/doc-translate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,qBAAqB,EACrB,UAAU,EACV,sBAAsB,EAGvB,MAAM,kBAAkB,CAAC;AAI1B,YAAY,EAAE,qBAAqB,EAAE,CAAC;AAStC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"doc-translate.d.ts","sourceRoot":"","sources":["../../src/cli/doc-translate.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,19 @@ import { ensureDirForFile, hashFileContent, resolveTranslatedOutputPath, writeAt
|
|
|
27
27
|
import { normalizeLocale } from "../core/config.js";
|
|
28
28
|
import { runMapWithConcurrency, AsyncSemaphore, AsyncMutex } from "../utils/concurrency.js";
|
|
29
29
|
import { describeEmphasisPlaceholdersPolicy, resolveMarkdownEmphasisPlaceholders, usesAutomaticEmphasisPlaceholdersForLocale, } from "../core/markdown-emphasis-defaults.js";
|
|
30
|
+
import { collectMarkdownIssuesForSegment } from "../processors/markdown-source-diagnostics.js";
|
|
31
|
+
/** Same segment-extraction options as `translate-docs` for one documentation block. */
|
|
32
|
+
export function buildMarkdownExtractOpts(documentation) {
|
|
33
|
+
const langListCfg = documentation.markdownOutput.postProcessing?.languageListBlock;
|
|
34
|
+
const splitCfg = segmentSplittingSchema.parse(documentation.segmentSplitting ?? {});
|
|
35
|
+
if (!langListCfg && !splitCfg.enabled) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
...(langListCfg ? { languageListBlock: langListCfg } : {}),
|
|
40
|
+
...(splitCfg.enabled ? { segmentSplitting: splitCfg } : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
30
43
|
export function translatePromptFormatToResponseFormat(fmt) {
|
|
31
44
|
if (fmt === "json-array") {
|
|
32
45
|
return "json-array";
|
|
@@ -829,6 +842,33 @@ failureDocMeta) {
|
|
|
829
842
|
individualSegmentTranslations,
|
|
830
843
|
};
|
|
831
844
|
}
|
|
845
|
+
/**
|
|
846
|
+
* Locale-independent: read source, extract segments (same options as translation), then refresh
|
|
847
|
+
* `markdown_source_issues` and stderr warnings. Call once per markdown file before parallel
|
|
848
|
+
* per-locale work so diagnostics are not repeated for every locale.
|
|
849
|
+
*/
|
|
850
|
+
export async function scanAndRecordMarkdownSourceIssuesForTranslate(absSource, relPath, config, cache, opts) {
|
|
851
|
+
if (config.documentation.warnMarkdownSourceIssues === false || opts.noCache || opts.dryRun) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
const content = fs.readFileSync(absSource, "utf8");
|
|
855
|
+
const blockIdx = opts.documentationBlockIndex ?? 0;
|
|
856
|
+
const fileTrackingKey = documentationFileTrackingKey(blockIdx, relPath);
|
|
857
|
+
const md = new MarkdownExtractor();
|
|
858
|
+
const mdExtractOpts = buildMarkdownExtractOpts(config.documentation);
|
|
859
|
+
const segments = md.extract(content, relPath, mdExtractOpts);
|
|
860
|
+
const markdownIssueRows = [];
|
|
861
|
+
for (const s of segments) {
|
|
862
|
+
markdownIssueRows.push(...collectMarkdownIssuesForSegment(s, fileTrackingKey));
|
|
863
|
+
}
|
|
864
|
+
await withCacheMutex(opts.cacheMutex, () => cache.replaceMarkdownIssuesForFilepath(fileTrackingKey, markdownIssueRows));
|
|
865
|
+
if (markdownIssueRows.length > 0) {
|
|
866
|
+
for (const row of markdownIssueRows) {
|
|
867
|
+
const ln = row.startLine ?? "?";
|
|
868
|
+
console.warn(chalk.yellow(` ⚠️ Markdown source ${relPath}:${ln} [${row.issueCode}] ${row.detail}\n`));
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
832
872
|
export async function translateMarkdownFile(absSource, relPath, locale, config, cache, client, glossary, opts, hitKeys, translatedMarkdownRelPaths) {
|
|
833
873
|
const totals = {
|
|
834
874
|
filesWritten: 0,
|
|
@@ -869,14 +909,7 @@ export async function translateMarkdownFile(absSource, relPath, locale, config,
|
|
|
869
909
|
}
|
|
870
910
|
const fileStartTime = Date.now();
|
|
871
911
|
const md = new MarkdownExtractor();
|
|
872
|
-
const
|
|
873
|
-
const splitCfg = segmentSplittingSchema.parse(config.documentation.segmentSplitting ?? {});
|
|
874
|
-
const mdExtractOpts = langListCfg || splitCfg.enabled
|
|
875
|
-
? {
|
|
876
|
-
...(langListCfg ? { languageListBlock: langListCfg } : {}),
|
|
877
|
-
...(splitCfg.enabled ? { segmentSplitting: splitCfg } : {}),
|
|
878
|
-
}
|
|
879
|
-
: undefined;
|
|
912
|
+
const mdExtractOpts = buildMarkdownExtractOpts(config.documentation);
|
|
880
913
|
const segments = md.extract(content, relPath, mdExtractOpts);
|
|
881
914
|
const translatableCount = segments.filter((s) => s.translatable).length;
|
|
882
915
|
console.log(chalk.yellow(`📄 ${locale} ${relPath}: ${segments.length} segment(s) (${translatableCount} translatable)`));
|
|
@@ -1457,6 +1490,15 @@ export async function runTranslate(config, opts, files, jsonAbsRoot) {
|
|
|
1457
1490
|
batchConcurrency: batchConcurrencyEffective,
|
|
1458
1491
|
cacheMutex,
|
|
1459
1492
|
};
|
|
1493
|
+
if (shouldRunMarkdown(opts, config) && cache && !opts.noCache && !opts.dryRun) {
|
|
1494
|
+
for (const rel of files.markdown) {
|
|
1495
|
+
if (!matchesPathFilter(rel, opts.pathFilter)) {
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
const abs = path.join(opts.cwd, rel);
|
|
1499
|
+
await scanAndRecordMarkdownSourceIssuesForTranslate(abs, rel, config, cache, runOpts);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1460
1502
|
const processLocale = async (locale) => {
|
|
1461
1503
|
const markdownHitKeysLocal = new Set();
|
|
1462
1504
|
const partial = {
|