ai-i18n-tools 1.5.0 → 1.6.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/README.md +91 -36
- package/bin/ai-i18n-tools.mjs +20 -0
- package/dist/build-info.generated.d.ts +1 -1
- package/dist/build-info.generated.js +1 -1
- package/dist/cli/doc-translate-quality-retry.d.ts +81 -0
- package/dist/cli/doc-translate-quality-retry.d.ts.map +1 -0
- package/dist/cli/doc-translate-quality-retry.js +223 -0
- package/dist/cli/doc-translate-quality-retry.js.map +1 -0
- package/dist/cli/doc-translate.d.ts +13 -1
- package/dist/cli/doc-translate.d.ts.map +1 -1
- package/dist/cli/doc-translate.js +255 -323
- package/dist/cli/doc-translate.js.map +1 -1
- package/dist/cli/index.js +170 -139
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/log-output.d.ts.map +1 -1
- package/dist/cli/log-output.js +2 -8
- package/dist/cli/log-output.js.map +1 -1
- package/dist/cli/translate-json-run.d.ts.map +1 -1
- package/dist/cli/translate-json-run.js +43 -2
- package/dist/cli/translate-json-run.js.map +1 -1
- package/dist/cli/translate-svg.d.ts.map +1 -1
- package/dist/cli/translate-svg.js +98 -57
- package/dist/cli/translate-svg.js.map +1 -1
- package/dist/cli/translate-ui-strings.d.ts +2 -0
- package/dist/cli/translate-ui-strings.d.ts.map +1 -1
- package/dist/cli/translate-ui-strings.js +263 -230
- package/dist/cli/translate-ui-strings.js.map +1 -1
- package/dist/core/cache.d.ts +6 -0
- package/dist/core/cache.d.ts.map +1 -1
- package/dist/core/cache.js +59 -0
- package/dist/core/cache.js.map +1 -1
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +7 -0
- package/dist/core/types.js.map +1 -1
- package/dist/extractors/markdown-quality-split.d.ts +7 -0
- package/dist/extractors/markdown-quality-split.d.ts.map +1 -0
- package/dist/extractors/markdown-quality-split.js +153 -0
- package/dist/extractors/markdown-quality-split.js.map +1 -0
- package/dist/extractors/markdown-segment-split.d.ts +3 -0
- package/dist/extractors/markdown-segment-split.d.ts.map +1 -1
- package/dist/extractors/markdown-segment-split.js +2 -2
- package/dist/extractors/markdown-segment-split.js.map +1 -1
- package/dist/processors/bold-code-placeholders.d.ts.map +1 -1
- package/dist/processors/bold-code-placeholders.js +12 -8
- package/dist/processors/bold-code-placeholders.js.map +1 -1
- package/dist/processors/emphasis-placeholders.d.ts +6 -2
- package/dist/processors/emphasis-placeholders.d.ts.map +1 -1
- package/dist/processors/emphasis-placeholders.js +75 -17
- package/dist/processors/emphasis-placeholders.js.map +1 -1
- package/dist/processors/markdown-source-diagnostics.d.ts +0 -2
- package/dist/processors/markdown-source-diagnostics.d.ts.map +1 -1
- package/dist/processors/markdown-source-diagnostics.js +0 -81
- package/dist/processors/markdown-source-diagnostics.js.map +1 -1
- package/dist/processors/validator.d.ts +2 -0
- package/dist/processors/validator.d.ts.map +1 -1
- package/dist/processors/validator.js +7 -0
- package/dist/processors/validator.js.map +1 -1
- package/dist/utils/concurrency.d.ts +2 -1
- package/dist/utils/concurrency.d.ts.map +1 -1
- package/dist/utils/concurrency.js +41 -5
- package/dist/utils/concurrency.js.map +1 -1
- package/dist/utils/run-interrupt.d.ts +31 -0
- package/dist/utils/run-interrupt.d.ts.map +1 -0
- package/dist/utils/run-interrupt.js +106 -0
- package/dist/utils/run-interrupt.js.map +1 -0
- package/docs/GETTING_STARTED.md +9 -6
- package/docs/PACKAGE_OVERVIEW.md +34 -7
- package/docs/ai-i18n-tools-context.md +3 -3
- package/package.json +16 -13
- package/dist/cli/strip-md-bold-inline.d.ts +0 -15
- package/dist/cli/strip-md-bold-inline.d.ts.map +0 -1
- package/dist/cli/strip-md-bold-inline.js +0 -77
- package/dist/cli/strip-md-bold-inline.js.map +0 -1
- package/dist/utils/stripBoldAroundInlineCode.d.ts +0 -6
- package/dist/utils/stripBoldAroundInlineCode.d.ts.map +0 -1
- package/dist/utils/stripBoldAroundInlineCode.js +0 -52
- package/dist/utils/stripBoldAroundInlineCode.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,26 +7,38 @@
|
|
|
7
7
|
[](./LICENSE)
|
|
8
8
|
[](https://github.com/wsj-br/ai-i18n-tools/actions/workflows/ci.yml)
|
|
9
9
|
|
|
10
|
-
CLI and toolkit for
|
|
10
|
+
A CLI and toolkit for internationalizing JavaScript/TypeScript applications and documentation sites using large language models via [OpenRouter](https://openrouter.ai/). Three modular workflows, all sharing a single config file, support different translation needs:
|
|
11
11
|
|
|
12
|
+
- **Workflow 1 — UI Translation:** Extracts `t("…")` calls from JS/TS (and optionally from `.astro` files) and generates flat, per-locale JSON for i18next or static SSG lookup.
|
|
13
|
+
- **Workflow 2 — Document Translation:** Translates markdown, MDX, and `.astro` pages (for websites and Starlight) listed in `docs[].contentPaths` using `translate-docs`.
|
|
14
|
+
- **Workflow 3 — JSON File Translation:** Translates arbitrary nested JSON bundles defined in `json[]`. Use `translate-json` when UI copy is stored in per-locale JSON files instead of using `t()` in source.
|
|
15
|
+
|
|
16
|
+
**SVG** assets are translated using `features.translateSVG`, the top-level `svg` block, and `translate-svg`—not `docs[].contentPaths`.
|
|
17
|
+
|
|
18
|
+
**Which workflow should I use?**
|
|
19
|
+
- Source uses `t()` → **Workflow 1** (`extract` / `translate-ui`)
|
|
20
|
+
- Localized pages or Docusaurus catalog JSON → **Workflow 2** (`translate-docs`)
|
|
21
|
+
- Only standalone, nested JSON locale files → **Workflow 3** (`translate-json`)
|
|
22
|
+
|
|
23
|
+
All workflows maintain a file/SQLite cache to ensure that only new or changed segments (strings or text chunks) are sent to the LLM.
|
|
12
24
|
|
|
13
25
|
<small>**Read in other languages:** </small>
|
|
14
26
|
<small id="lang-list">[English (GB)](./README.md) · [Deutsch](./translated-docs/README.de.md) · [Español](./translated-docs/README.es.md) · [Français](./translated-docs/README.fr.md) · [हिन्दी](./translated-docs/README.hi.md) · [日本語](./translated-docs/README.ja.md) · [한국어](./translated-docs/README.ko.md) · [Português (Brasil)](./translated-docs/README.pt-BR.md) · [中文 (中国大陆)](./translated-docs/README.zh-CN.md) · [中文 (台灣)](./translated-docs/README.zh-TW.md)</small>
|
|
15
27
|
|
|
16
|
-
<small>Translated READMEs and docs are committed under [`translated-docs/`](https://github.com/wsj-br/ai-i18n-tools/tree/main/translated-docs) on GitHub; the npm package ships English `docs/` only.</small>
|
|
17
28
|
|
|
18
29
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
19
30
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
20
31
|
**Table of Contents**
|
|
21
32
|
|
|
22
|
-
- [
|
|
33
|
+
- [Core workflows](#core-workflows)
|
|
23
34
|
- [Installation](#installation)
|
|
24
35
|
- [Using the CLI](#using-the-cli)
|
|
25
36
|
- [OpenRouter](#openrouter)
|
|
26
37
|
- [Quick start](#quick-start)
|
|
27
38
|
- [Workflow 1 - UI Translation](#workflow-1---ui-translation)
|
|
28
39
|
- [Workflow 2 - Document Translation](#workflow-2---document-translation)
|
|
29
|
-
- [
|
|
40
|
+
- [Astro (plain Astro & Starlight)](#astro-plain-astro--starlight)
|
|
41
|
+
- [Combined workflow](#combined-workflow)
|
|
30
42
|
- [Runtime helpers](#runtime-helpers)
|
|
31
43
|
- [CLI commands](#cli-commands)
|
|
32
44
|
- [Documentation](#documentation)
|
|
@@ -38,18 +50,22 @@ CLI and toolkit for internationalising JavaScript/TypeScript applications and do
|
|
|
38
50
|
|
|
39
51
|
|
|
40
52
|
|
|
41
|
-
<a id="
|
|
42
|
-
##
|
|
53
|
+
<a id="core-workflows"></a>
|
|
54
|
+
## Core workflows
|
|
55
|
+
|
|
56
|
+
**Workflow 1 - UI Translation** — for any JS/TS project using i18next (React, Next.js, Node.js, CLIs) or static Astro SSG
|
|
43
57
|
|
|
44
|
-
|
|
58
|
+
Scans source files for `t("…")` / `i18n.t("…")` literals (add `.astro` to `ui.uiExtractor.extensions` for Astro frontmatter and template expressions), builds a master catalog (`strings.json`), translates missing entries per locale via OpenRouter, and writes flat JSON files (`de.json`, `pt-BR.json`, …). English source text is the runtime lookup key in those bundles — `strings.json` is the extraction cache, not the runtime bundle.
|
|
45
59
|
|
|
46
|
-
|
|
60
|
+
**Workflow 2 - Document Translation** — for markdown, MDX, and `.astro` under `docs[].contentPaths`
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
Designed primarily for **markdown, MDX, and `.astro` documentation** (Docusaurus, [Astro Starlight](https://starlight.astro.build/), plain README files, and plain Astro marketing pages). `translate-docs` writes localised copies with a shared SQLite cache. On Docusaurus sites, set `docs[].docusaurusCatalogDir` to the `write-translations` catalog folder so shell JSON (navbar, footer, theme strings) is translated in the same command. `docs[].docsOutput.style` supports `"nested"`, `"flat"`, `"doc-system"`, and aliases `"docusaurus"` / `"astro-starlight"` (see [Output layouts](docs/GETTING_STARTED.md#output-layouts) in Getting Started). Arbitrary nested UI JSON that is not a Docusaurus catalog belongs in Workflow 3 (`json[]` / `translate-json`), not `docs[]`.
|
|
49
63
|
|
|
50
|
-
|
|
64
|
+
**Workflow 3 - JSON file translation** — nested locale JSON without `t()` in source
|
|
51
65
|
|
|
52
|
-
|
|
66
|
+
Translate files such as `src/i18n/en/translation.json` via top-level `json[]`, `features.translateJson`, and `translate-json`. Scaffold with `init -t ui-json-bundles`.
|
|
67
|
+
|
|
68
|
+
All workflows share `ai-i18n-tools.config.json` and can be combined; `sync` runs extract, UI translation, translate SVG, `translate-docs`, and `translate-json` in order according to your `features` flags.
|
|
53
69
|
|
|
54
70
|
---
|
|
55
71
|
|
|
@@ -76,11 +92,22 @@ npx ai-i18n-tools sync # or: pnpm exec ai-i18n-tools sync
|
|
|
76
92
|
|
|
77
93
|
```json
|
|
78
94
|
"scripts": {
|
|
95
|
+
"i18n:extract": "ai-i18n-tools extract",
|
|
79
96
|
"i18n:sync": "ai-i18n-tools sync",
|
|
80
|
-
"i18n:translate": "ai-i18n-tools translate-
|
|
97
|
+
"i18n:translate:ui": "ai-i18n-tools translate-ui",
|
|
98
|
+
"i18n:translate:svg": "ai-i18n-tools translate-svg",
|
|
99
|
+
"i18n:translate:docs": "ai-i18n-tools translate-docs",
|
|
100
|
+
"i18n:translate:json": "ai-i18n-tools translate-json",
|
|
101
|
+
"i18n:translate": "ai-i18n-tools translate-docs",
|
|
102
|
+
"i18n:dashboard": "ai-i18n-tools dashboard"
|
|
81
103
|
}
|
|
82
104
|
```
|
|
83
105
|
|
|
106
|
+
You can also use the ai-i18n-tools CLI commands directly, for instance `ai-i18n-tools sync`.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
Prefer `sync` over hand-chaining `extract`, `translate-ui`, `translate-svg`, `translate-docs`, and `translate-json` — order and feature flags are easy to get wrong when run manually. See [Recommended `package.json` scripts](docs/GETTING_STARTED.md#recommended-packagejson-scripts) in Getting Started.
|
|
110
|
+
|
|
84
111
|
**Zero-install one-off** — `npx ai-i18n-tools <cmd>` or `pnpm dlx ai-i18n-tools <cmd>` (downloads for that invocation only).
|
|
85
112
|
|
|
86
113
|
> **Tip:** To run `ai-i18n-tools` bare in an interactive shell without `npx`, add `node_modules/.bin` to your `PATH` (bash/zsh: `export PATH="$PWD/node_modules/.bin:$PATH"`). See [Getting Started](docs/GETTING_STARTED.md#installation) for direnv and Windows instructions.
|
|
@@ -96,7 +123,7 @@ export OPENROUTER_API_KEY=sk-or-v1-your-key-here
|
|
|
96
123
|
<a id="openrouter"></a>
|
|
97
124
|
## OpenRouter
|
|
98
125
|
|
|
99
|
-
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.
|
|
126
|
+
Commands that call OpenRouter (`translate-ui`, `translate-docs`, `translate-json`, `sync`, `check-models`, and related scripts) need `OPENROUTER_API_KEY` in the environment. `check-markdown` does not use OpenRouter.
|
|
100
127
|
|
|
101
128
|
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).
|
|
102
129
|
|
|
@@ -111,7 +138,7 @@ Run `ai-i18n-tools check-models` to verify each configured model id against Open
|
|
|
111
138
|
### Workflow 1 - UI Translation
|
|
112
139
|
|
|
113
140
|
```bash
|
|
114
|
-
# 1. Create config
|
|
141
|
+
# 1. Create config (default ui-markdown; plain Astro: init -t ui-astro-website)
|
|
115
142
|
npx ai-i18n-tools init
|
|
116
143
|
|
|
117
144
|
# 2. Extract UI strings to strings.json
|
|
@@ -126,24 +153,48 @@ Then wire i18next in your app using the helpers from `'ai-i18n-tools/runtime'`.
|
|
|
126
153
|
<a id="workflow-2---document-translation"></a>
|
|
127
154
|
### Workflow 2 - Document Translation
|
|
128
155
|
|
|
156
|
+
The default `init` template (`ui-markdown`) enables UI extraction only. Use a docs-oriented template (or enable `features.translateDocs` and add `docs[]`) before `translate-docs`:
|
|
157
|
+
|
|
129
158
|
```bash
|
|
130
|
-
#
|
|
159
|
+
# Docusaurus docs + optional write-translations catalog
|
|
131
160
|
npx ai-i18n-tools init -t ui-docusaurus
|
|
132
|
-
# Astro Starlight: npx ai-i18n-tools init -t ui-starlight
|
|
133
|
-
# Plain Astro website (UI + optional page HTML): npx ai-i18n-tools init -t ui-astro-website
|
|
134
161
|
|
|
135
|
-
#
|
|
136
|
-
npx ai-i18n-tools
|
|
162
|
+
# Astro Starlight documentation
|
|
163
|
+
# npx ai-i18n-tools init -t ui-starlight
|
|
137
164
|
|
|
138
|
-
#
|
|
165
|
+
# Plain Astro website — UI extraction for t() in .astro; add docs[] for page HTML (see Astro below)
|
|
166
|
+
# npx ai-i18n-tools init -t ui-astro-website
|
|
167
|
+
|
|
168
|
+
npx ai-i18n-tools translate-docs
|
|
139
169
|
npx ai-i18n-tools status
|
|
170
|
+
# npx ai-i18n-tools translate-docs --locale de # single locale
|
|
140
171
|
```
|
|
141
172
|
|
|
142
|
-
|
|
143
|
-
|
|
173
|
+
Edit `ai-i18n-tools.config.json`: set `docs[].contentPaths` to markdown, MDX, and/or `.astro` sources; `docs[].outputDir` and `docs[].docsOutput.style` (`"docusaurus"`, `"astro-starlight"`, `"flat"`, etc.). Full field reference: [Workflow 2 - Document Translation](docs/GETTING_STARTED.md#workflow-2---document-translation).
|
|
174
|
+
|
|
175
|
+
<a id="astro-plain-astro--starlight"></a>
|
|
176
|
+
### Astro (plain Astro & Starlight)
|
|
177
|
+
|
|
178
|
+
**Astro Starlight** — `init -t ui-starlight`, then `translate-docs`. Starlight UI overrides can use `src/content/i18n/en.json` with `jsonPathTemplate` in a separate `docs[]` block when needed ([Getting Started → Workflow 2](docs/GETTING_STARTED.md#step-1-initialise-for-documentation)).
|
|
179
|
+
|
|
180
|
+
**Plain Astro** (marketing or app sites, not Starlight) — combine [Astro built-in i18n routing](https://docs.astro.build/en/guides/internationalization/) with ai-i18n-tools. Reference project: [`examples/astro-website`](examples/astro-website/) (English at `/`, locales at `/{locale}/`).
|
|
181
|
+
|
|
182
|
+
Most teams use a **hybrid** of two pipelines:
|
|
183
|
+
|
|
184
|
+
| Pipeline | Use for | Commands | Output |
|
|
185
|
+
|----------|---------|----------|--------|
|
|
186
|
+
| **Page HTML** | Headings, paragraphs, nav labels, inline arrays in the template body | `translate-docs` | `src/pages/{locale}/index.astro` per locale |
|
|
187
|
+
| **UI strings (`t()`)** | Frontmatter data, tab labels, shared arrays | `extract` → `translate-ui` | `public/locales/{locale}.json` (English source as key) |
|
|
188
|
+
|
|
189
|
+
Scaffold UI with `init -t ui-astro-website`. For hardcoded HTML in `.astro` pages, enable `features.translateDocs` and add a `docs[]` block with `docsOutput.style: "astro-starlight"` (see [Astro website pages (parse-and-replace)](docs/GETTING_STARTED.md#astro-website-pages-parse-and-replace)). Keep `targetLocales`, `i18n.locales` in `astro.config.mjs`, and `ui-languages.json` aligned (Astro routes use lowercase codes such as `pt-br`; flat bundle filenames follow config casing, e.g. `pt-BR.json`).
|
|
190
|
+
|
|
191
|
+
Wire `t()` at build time without i18next unless you add client islands — see [Astro website UI strings (SSG)](docs/GETTING_STARTED.md#astro-website-ui-strings-ssg) and the example’s `src/i18n/t.ts`.
|
|
192
|
+
|
|
193
|
+
<a id="combined-workflow"></a>
|
|
194
|
+
### Combined workflow
|
|
144
195
|
|
|
145
196
|
```bash
|
|
146
|
-
npx ai-i18n-tools sync #
|
|
197
|
+
npx ai-i18n-tools sync # extract → translate-ui → translate-svg → translate-docs → translate-json (per features)
|
|
147
198
|
```
|
|
148
199
|
|
|
149
200
|
---
|
|
@@ -157,6 +208,9 @@ The following helpers are exported from `'ai-i18n-tools/runtime'` and work in an
|
|
|
157
208
|
|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
|
|
158
209
|
| `defaultI18nInitOptions(sourceLocale)` | Standard i18next init options for key-as-default setups. |
|
|
159
210
|
| `setupKeyAsDefaultT(i18n, { stringsJson, sourcePluralFlatBundle? })` | Recommended wiring: key-trim + plural `wrapT` from `strings.json`, optionally merges `translate-ui` `{sourceLocale}.json` plural keys. |
|
|
211
|
+
| `wrapT(i18n, options)` | Lower-level plural-aware `t()` wrapper (usually installed by `setupKeyAsDefaultT`). |
|
|
212
|
+
| `buildPluralIndexFromStringsJson(entries)` | Builds the plural group index `wrapT` uses from catalog rows with `"plural": true`. |
|
|
213
|
+
| `extractInterpolationNamesForWrap(key)` | Parses `{{var}}` names from a source key for `wrapT` / key-trim fallback. |
|
|
160
214
|
| `wrapI18nWithKeyTrim(i18n)` | Lower-level key-trim wrapper only (deprecated for app wiring; prefer `setupKeyAsDefaultT`). |
|
|
161
215
|
| `makeLocaleLoadersFromManifest(uiLanguages, sourceLocale, makeLoader)` | Builds the `localeLoaders` map for `makeLoadLocale` from `ui-languages.json` (every `code` except `sourceLocale`). |
|
|
162
216
|
| `makeLoadLocale(i18n, loaders, sourceLocale)` | Factory for async locale file loading. |
|
|
@@ -174,39 +228,40 @@ The following helpers are exported from `'ai-i18n-tools/runtime'` and work in an
|
|
|
174
228
|
|
|
175
229
|
```bash
|
|
176
230
|
ai-i18n-tools version
|
|
177
|
-
ai-i18n-tools help [command]
|
|
178
|
-
ai-i18n-tools init [-t ui-markdown|ui-docusaurus] [-o path] [--with-translate-ignore]
|
|
179
231
|
ai-i18n-tools check-models
|
|
180
|
-
ai-i18n-tools
|
|
232
|
+
ai-i18n-tools init [-t ui-markdown|ui-docusaurus|ui-starlight|ui-astro-website|ui-json-bundles] [-o path] [--with-translate-ignore]
|
|
233
|
+
ai-i18n-tools write-heading-ids …
|
|
181
234
|
ai-i18n-tools extract
|
|
182
235
|
ai-i18n-tools translate-docs …
|
|
183
|
-
ai-i18n-tools
|
|
184
|
-
ai-i18n-tools strip-md-bold-inline …
|
|
185
|
-
ai-i18n-tools check-markdown [-p|--path <path>] [--json] [--no-cache]
|
|
236
|
+
ai-i18n-tools translate-json …
|
|
186
237
|
ai-i18n-tools translate-svg …
|
|
187
238
|
ai-i18n-tools translate-ui …
|
|
239
|
+
ai-i18n-tools sync-ui …
|
|
188
240
|
ai-i18n-tools lint-source …
|
|
241
|
+
ai-i18n-tools check-markdown [-p|--path <path>] [-f|--file <path>] [--json] [--no-cache]
|
|
189
242
|
ai-i18n-tools export-ui-xliff …
|
|
190
243
|
ai-i18n-tools sync …
|
|
191
|
-
ai-i18n-tools status
|
|
192
|
-
ai-i18n-tools statistics
|
|
193
|
-
ai-i18n-tools
|
|
194
|
-
ai-i18n-tools
|
|
195
|
-
ai-i18n-tools
|
|
244
|
+
ai-i18n-tools status …
|
|
245
|
+
ai-i18n-tools statistics …
|
|
246
|
+
ai-i18n-tools cleanup …
|
|
247
|
+
ai-i18n-tools clean-temp …
|
|
248
|
+
ai-i18n-tools dashboard …
|
|
249
|
+
ai-i18n-tools generate-ui-languages [--master path] [--dry-run]
|
|
196
250
|
ai-i18n-tools glossary-generate
|
|
251
|
+
ai-i18n-tools help [command]
|
|
197
252
|
```
|
|
198
253
|
|
|
199
254
|
|
|
200
255
|
Complete per-command flag lists are in [Getting Started — CLI reference](docs/GETTING_STARTED.md#cli-reference). Run `ai-i18n-tools <command> --help` for built-in usage text.
|
|
201
256
|
|
|
202
|
-
Global options on every command: `-c <config>` (default: `ai-i18n-tools.config.json`), `-v` (verbose), optional `-w` / `--write-logs [path]` to tee console output to a log file (default: under the translation cache directory), `-V` / `--version`, and `-h` / `--help`. See [Getting Started](docs/GETTING_STARTED.md#cli-reference) for the command overview table.
|
|
257
|
+
Global options on every command: `-c <config>` (default: `ai-i18n-tools.config.json`), `-v` (verbose), optional `-w` / `--write-logs [path]` to tee console output to a log file (default: under the translation cache directory), `-V` / `--version`, and `-h` / `--help`. Several commands accept `-l` / `--locale <codes>` (comma-separated BCP-47) to limit target locales; `lint-source` uses a single source locale. See [Getting Started](docs/GETTING_STARTED.md#cli-reference) for the command overview table.
|
|
203
258
|
|
|
204
259
|
---
|
|
205
260
|
|
|
206
261
|
<a id="documentation"></a>
|
|
207
262
|
## Documentation
|
|
208
263
|
|
|
209
|
-
- [Getting Started](docs/GETTING_STARTED.md) - full setup
|
|
264
|
+
- [Getting Started](docs/GETTING_STARTED.md) - full setup for all workflows (UI, docs/`.astro`, JSON bundles, Astro Starlight and plain Astro), CLI reference, and config field reference.
|
|
210
265
|
- [Locale assets guide](docs/LOCALE-ASSETS-GUIDE.md) - screenshots and illustrated SVGs in translated docs (Patterns A–E, flat link rewriter, screenshot scripts).
|
|
211
266
|
- [Package Overview](docs/PACKAGE_OVERVIEW.md) - architecture, internals, programmatic API, and extension points.
|
|
212
267
|
- [AI Agent Context](docs/ai-i18n-tools-context.md) - **for apps using the package:** integration prompts for downstream projects (copy into your repo’s agent rules).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stable package.json "bin" entry. pnpm links this file during install before
|
|
4
|
+
* dist/ exists; the compiled CLI is emitted to dist/cli/index.js by `pnpm build`.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
+
const cliEntry = path.join(root, "dist", "cli", "index.js");
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(cliEntry)) {
|
|
14
|
+
console.error(
|
|
15
|
+
"ai-i18n-tools is not built yet. From the repository root, run:\n pnpm run build",
|
|
16
|
+
);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
await import(cliEntry);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { DocumentPromptContentType } from "../core/prompt-builder.js";
|
|
2
|
+
import type { Segment, TranslationFailureInsert } from "../core/types.js";
|
|
3
|
+
import { PlaceholderHandler } from "../processors/placeholder-handler.js";
|
|
4
|
+
import type { Glossary } from "../glossary/glossary.js";
|
|
5
|
+
import type { OpenRouterClient } from "../api/openrouter.js";
|
|
6
|
+
type ProtectState = {
|
|
7
|
+
glossaryForceReplacements?: string[];
|
|
8
|
+
} & ReturnType<PlaceholderHandler["protectForTranslation"]>;
|
|
9
|
+
export type DocQualityLogOutcome = "retrying_next_model" | "fatal" | "individual_success";
|
|
10
|
+
export type DocQualityLogWriter = (opts: {
|
|
11
|
+
segmentsLabel: string;
|
|
12
|
+
outcome: DocQualityLogOutcome;
|
|
13
|
+
failedModel: string;
|
|
14
|
+
nextModel?: string;
|
|
15
|
+
qualityErrors: string[];
|
|
16
|
+
perSegmentLines: string[];
|
|
17
|
+
systemPrompt: string;
|
|
18
|
+
userContent: string;
|
|
19
|
+
rawAssistantContent: string;
|
|
20
|
+
mode: "failed" | "debug";
|
|
21
|
+
}) => string | undefined;
|
|
22
|
+
export type ProtectSegmentFn = (raw: string) => {
|
|
23
|
+
text: string;
|
|
24
|
+
state: ProtectState;
|
|
25
|
+
};
|
|
26
|
+
export type TranslateOneSegmentQualityRetryParams = {
|
|
27
|
+
client: OpenRouterClient;
|
|
28
|
+
locale: string;
|
|
29
|
+
glossary: Glossary;
|
|
30
|
+
contentType: DocumentPromptContentType;
|
|
31
|
+
models: readonly string[];
|
|
32
|
+
/** Original segment (pre-placeholder source text in `content`). */
|
|
33
|
+
original: Segment;
|
|
34
|
+
/** Protected text sent to the API for the top-level segment (unused when splitDepth > 0). */
|
|
35
|
+
protectedContent: string;
|
|
36
|
+
/** Placeholder state for the top-level segment (unused when splitDepth > 0). */
|
|
37
|
+
protectState: ProtectState | undefined;
|
|
38
|
+
/** Model index to start trying from (0-based). */
|
|
39
|
+
startModelIndex: number;
|
|
40
|
+
splitDepth: number;
|
|
41
|
+
qualityRetrySplit: boolean;
|
|
42
|
+
maxQualityRetrySplitDepth: number;
|
|
43
|
+
protectForTranslation: ProtectSegmentFn;
|
|
44
|
+
segmentHash: string;
|
|
45
|
+
failureFp: string | null;
|
|
46
|
+
recordFailures: (rows: TranslationFailureInsert[]) => Promise<void>;
|
|
47
|
+
buildQualityFailureRows: (model: string, modelOrder: number | null, errors: readonly string[], fatal: boolean) => TranslationFailureInsert[];
|
|
48
|
+
buildRuntimeFailureRow: (model: string | null, modelOrder: number | null, message: string, fatal: boolean) => TranslationFailureInsert;
|
|
49
|
+
modelOrder1Based: (model: string | null) => number | null;
|
|
50
|
+
segLabelSingle: string;
|
|
51
|
+
batchSegIndex?: number;
|
|
52
|
+
docIdx0?: number;
|
|
53
|
+
docLog?: {
|
|
54
|
+
relativePath: string;
|
|
55
|
+
totalSegments: number;
|
|
56
|
+
};
|
|
57
|
+
failureLogDirAbs?: string | null;
|
|
58
|
+
writeFailureLog?: DocQualityLogWriter;
|
|
59
|
+
writeDebugLog?: DocQualityLogWriter;
|
|
60
|
+
warnModelSwitch?: (failedModel: string, nextModel: string, detail: string, nextModelOrdinal: {
|
|
61
|
+
index1Based: number;
|
|
62
|
+
total: number;
|
|
63
|
+
}) => void;
|
|
64
|
+
abortSignal?: AbortSignal;
|
|
65
|
+
};
|
|
66
|
+
export type TranslateOneSegmentQualityRetryResult = {
|
|
67
|
+
text: string;
|
|
68
|
+
modelUsed: string;
|
|
69
|
+
inTok: number;
|
|
70
|
+
outTok: number;
|
|
71
|
+
cost: number;
|
|
72
|
+
validationFailures: number;
|
|
73
|
+
individualCalls: number;
|
|
74
|
+
qualitySplitRetries: number;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Translate one markdown segment with model cycling and progressive split-on-AST-failure fallback.
|
|
78
|
+
*/
|
|
79
|
+
export declare function translateOneSegmentWithQualityRetry(params: TranslateOneSegmentQualityRetryParams): Promise<TranslateOneSegmentQualityRetryResult>;
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=doc-translate-quality-retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-translate-quality-retry.d.ts","sourceRoot":"","sources":["../../src/cli/doc-translate-quality-retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { splitSegmentForQualityRetry } from "../extractors/markdown-quality-split.js";
|
|
3
|
+
import { PlaceholderHandler } from "../processors/placeholder-handler.js";
|
|
4
|
+
import { restoreGlossaryForcedTerms } from "../processors/glossary-force-placeholders.js";
|
|
5
|
+
import { errorsIncludeAstMismatch, validateDocTranslatePair } from "../processors/validator.js";
|
|
6
|
+
import { throwIfAbortSignal } from "../utils/run-interrupt.js";
|
|
7
|
+
function restoreSegmentTranslation(ph, raw, st) {
|
|
8
|
+
if (!st) {
|
|
9
|
+
return raw;
|
|
10
|
+
}
|
|
11
|
+
let out = ph.restoreAfterTranslation(raw, st);
|
|
12
|
+
out = restoreGlossaryForcedTerms(out, st.glossaryForceReplacements ?? []);
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
function isModelRetryFailure(result) {
|
|
16
|
+
return result.ok === false;
|
|
17
|
+
}
|
|
18
|
+
function perSegLine(params, ok, errors, partLabel) {
|
|
19
|
+
const hashLabel = partLabel ? `${params.segmentHash} part ${partLabel}` : params.segmentHash;
|
|
20
|
+
if (params.docLog && params.docIdx0 !== undefined && params.batchSegIndex !== undefined) {
|
|
21
|
+
return `doc #${params.docIdx0 + 1}/${params.docLog.totalSegments} seg id=${params.batchSegIndex} (hash ${hashLabel}): ${ok ? "OK" : errors.join("; ")}`;
|
|
22
|
+
}
|
|
23
|
+
if (params.batchSegIndex !== undefined) {
|
|
24
|
+
return `batch seg id=${params.batchSegIndex} (hash ${hashLabel}): ${ok ? "OK" : errors.join("; ")}`;
|
|
25
|
+
}
|
|
26
|
+
return `(hash ${hashLabel}): ${ok ? "OK" : errors.join("; ")}`;
|
|
27
|
+
}
|
|
28
|
+
async function translateProtectedContent(params, originalContent, protectedContent, protectState, startModelIndex, partLabel) {
|
|
29
|
+
const { client, locale, glossary, contentType, models, original, recordFailures, buildQualityFailureRows, buildRuntimeFailureRow, modelOrder1Based, segLabelSingle, failureLogDirAbs, docLog, writeFailureLog, writeDebugLog, warnModelSwitch, } = params;
|
|
30
|
+
const hints = glossary.findTermsInText(protectedContent, locale);
|
|
31
|
+
const ph = new PlaceholderHandler();
|
|
32
|
+
let startIdx = startModelIndex;
|
|
33
|
+
let remainingErrors = [];
|
|
34
|
+
let inTok = 0;
|
|
35
|
+
let outTok = 0;
|
|
36
|
+
let cost = 0;
|
|
37
|
+
let validationFailures = 0;
|
|
38
|
+
let individualCalls = 0;
|
|
39
|
+
while (startIdx < models.length) {
|
|
40
|
+
throwIfAbortSignal(params.abortSignal);
|
|
41
|
+
individualCalls++;
|
|
42
|
+
const startModel = models[startIdx] ?? null;
|
|
43
|
+
let single;
|
|
44
|
+
try {
|
|
45
|
+
single = await client.translateDocumentSegment(protectedContent, locale, hints, {
|
|
46
|
+
contentType,
|
|
47
|
+
startModelIndex: startIdx,
|
|
48
|
+
docLogContext: docLog?.relativePath
|
|
49
|
+
? { locale, relativePath: docLog.relativePath }
|
|
50
|
+
: undefined,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
await recordFailures([
|
|
55
|
+
buildRuntimeFailureRow(startModel, modelOrder1Based(startModel), String(err), true),
|
|
56
|
+
]);
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
throwIfAbortSignal(params.abortSignal);
|
|
60
|
+
inTok += single.usage.inputTokens;
|
|
61
|
+
outTok += single.usage.outputTokens;
|
|
62
|
+
cost += single.cost ?? 0;
|
|
63
|
+
const restored = restoreSegmentTranslation(ph, single.content, protectState);
|
|
64
|
+
const origSeg = { ...original, content: originalContent };
|
|
65
|
+
const v = await validateDocTranslatePair(origSeg, restored);
|
|
66
|
+
const nextIdx = models.indexOf(single.model) + 1;
|
|
67
|
+
const line = perSegLine(params, v.ok, v.errors, partLabel);
|
|
68
|
+
if (failureLogDirAbs && docLog && writeDebugLog) {
|
|
69
|
+
const debugLogPath = writeDebugLog({
|
|
70
|
+
segmentsLabel: segLabelSingle || "(segment range unknown)",
|
|
71
|
+
outcome: v.ok
|
|
72
|
+
? "individual_success"
|
|
73
|
+
: nextIdx >= models.length
|
|
74
|
+
? "fatal"
|
|
75
|
+
: "retrying_next_model",
|
|
76
|
+
failedModel: single.model,
|
|
77
|
+
nextModel: nextIdx < models.length ? models[nextIdx] : undefined,
|
|
78
|
+
qualityErrors: v.errors,
|
|
79
|
+
perSegmentLines: [line],
|
|
80
|
+
systemPrompt: single.debugPrompt?.systemPrompt ?? "",
|
|
81
|
+
userContent: single.debugPrompt?.userContent ?? "",
|
|
82
|
+
rawAssistantContent: single.rawAssistantContent ?? "(missing raw response; rebuild ai-i18n-tools)",
|
|
83
|
+
mode: "debug",
|
|
84
|
+
});
|
|
85
|
+
if (debugLogPath) {
|
|
86
|
+
console.warn(chalk.gray(` 🧪 Debug log: ${debugLogPath}`));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (v.ok) {
|
|
90
|
+
return {
|
|
91
|
+
ok: true,
|
|
92
|
+
text: restored,
|
|
93
|
+
modelUsed: single.model,
|
|
94
|
+
inTok,
|
|
95
|
+
outTok,
|
|
96
|
+
cost,
|
|
97
|
+
validationFailures,
|
|
98
|
+
individualCalls,
|
|
99
|
+
qualitySplitRetries: 0,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
validationFailures++;
|
|
103
|
+
remainingErrors = v.errors;
|
|
104
|
+
await recordFailures(buildQualityFailureRows(single.model, modelOrder1Based(single.model), v.errors, nextIdx >= models.length));
|
|
105
|
+
if (failureLogDirAbs && docLog && writeFailureLog) {
|
|
106
|
+
const failureLogPath = writeFailureLog({
|
|
107
|
+
segmentsLabel: segLabelSingle || "(segment range unknown)",
|
|
108
|
+
outcome: nextIdx >= models.length ? "fatal" : "retrying_next_model",
|
|
109
|
+
failedModel: single.model,
|
|
110
|
+
nextModel: nextIdx < models.length ? models[nextIdx] : undefined,
|
|
111
|
+
qualityErrors: v.errors,
|
|
112
|
+
perSegmentLines: [line],
|
|
113
|
+
systemPrompt: single.debugPrompt?.systemPrompt ?? "",
|
|
114
|
+
userContent: single.debugPrompt?.userContent ?? "",
|
|
115
|
+
rawAssistantContent: single.rawAssistantContent ?? "(missing raw response; rebuild ai-i18n-tools)",
|
|
116
|
+
mode: "failed",
|
|
117
|
+
});
|
|
118
|
+
if (failureLogPath) {
|
|
119
|
+
console.warn(chalk.gray(` 📝 Failure log: ${failureLogPath}`));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (nextIdx >= models.length) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
warnModelSwitch?.(single.model, models[nextIdx], v.errors.join("; "), {
|
|
126
|
+
index1Based: nextIdx + 1,
|
|
127
|
+
total: models.length,
|
|
128
|
+
});
|
|
129
|
+
startIdx = nextIdx;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
remainingErrors,
|
|
134
|
+
inTok,
|
|
135
|
+
outTok,
|
|
136
|
+
cost,
|
|
137
|
+
validationFailures,
|
|
138
|
+
individualCalls,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function throwQualityFatal(params, errors) {
|
|
142
|
+
const loc = params.docLog?.relativePath ?? "?";
|
|
143
|
+
throw new Error(`Doc translation quality failed (${params.locale}, ${loc}): ${params.segLabelSingle ? `${params.segLabelSingle}: ` : ""}${errors.join("; ")}`);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Translate one markdown segment with model cycling and progressive split-on-AST-failure fallback.
|
|
147
|
+
*/
|
|
148
|
+
export async function translateOneSegmentWithQualityRetry(params) {
|
|
149
|
+
const { original, protectedContent, protectState, startModelIndex, splitDepth, qualityRetrySplit, maxQualityRetrySplitDepth, protectForTranslation, docLog, segLabelSingle, locale, } = params;
|
|
150
|
+
const direct = await translateProtectedContent(params, original.content, protectedContent, protectState, startModelIndex);
|
|
151
|
+
if (!isModelRetryFailure(direct)) {
|
|
152
|
+
const { ok: _ok, ...result } = direct;
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
const { remainingErrors } = direct;
|
|
156
|
+
const splittable = qualityRetrySplit &&
|
|
157
|
+
errorsIncludeAstMismatch(remainingErrors) &&
|
|
158
|
+
splitSegmentForQualityRetry(original.content, original.type, splitDepth, maxQualityRetrySplitDepth) !== null;
|
|
159
|
+
if (!splittable) {
|
|
160
|
+
throwQualityFatal(params, remainingErrors);
|
|
161
|
+
}
|
|
162
|
+
const parts = splitSegmentForQualityRetry(original.content, original.type, splitDepth, maxQualityRetrySplitDepth);
|
|
163
|
+
if (!parts || parts.length <= 1) {
|
|
164
|
+
throwQualityFatal(params, remainingErrors);
|
|
165
|
+
}
|
|
166
|
+
const baseLoc = docLog?.relativePath ? `${locale} ${docLog.relativePath}` : locale;
|
|
167
|
+
console.warn(chalk.magenta(` ✂️ ${baseLoc}: splitting segment into ${parts.length} part(s) after model exhaustion (depth ${splitDepth + 1}/${maxQualityRetrySplitDepth}) — ${segLabelSingle || `hash ${params.segmentHash}`}`));
|
|
168
|
+
const translatedParts = [];
|
|
169
|
+
const modelsUsed = new Set();
|
|
170
|
+
let inTok = direct.inTok;
|
|
171
|
+
let outTok = direct.outTok;
|
|
172
|
+
let cost = direct.cost;
|
|
173
|
+
let validationFailures = direct.validationFailures;
|
|
174
|
+
let individualCalls = direct.individualCalls;
|
|
175
|
+
let qualitySplitRetries = 1;
|
|
176
|
+
for (let pi = 0; pi < parts.length; pi++) {
|
|
177
|
+
throwIfAbortSignal(params.abortSignal);
|
|
178
|
+
const partContent = parts[pi];
|
|
179
|
+
const { text: partProtected, state: partState } = protectForTranslation(partContent);
|
|
180
|
+
const partOriginal = {
|
|
181
|
+
...original,
|
|
182
|
+
content: partContent,
|
|
183
|
+
};
|
|
184
|
+
const partResult = await translateOneSegmentWithQualityRetry({
|
|
185
|
+
...params,
|
|
186
|
+
original: partOriginal,
|
|
187
|
+
protectedContent: partProtected,
|
|
188
|
+
protectState: partState,
|
|
189
|
+
startModelIndex: 0,
|
|
190
|
+
splitDepth: splitDepth + 1,
|
|
191
|
+
});
|
|
192
|
+
inTok += partResult.inTok;
|
|
193
|
+
outTok += partResult.outTok;
|
|
194
|
+
cost += partResult.cost;
|
|
195
|
+
validationFailures += partResult.validationFailures;
|
|
196
|
+
individualCalls += partResult.individualCalls;
|
|
197
|
+
qualitySplitRetries += partResult.qualitySplitRetries;
|
|
198
|
+
for (const m of partResult.modelUsed.split(",")) {
|
|
199
|
+
const t = m.trim();
|
|
200
|
+
if (t) {
|
|
201
|
+
modelsUsed.add(t);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
translatedParts.push(partResult.text);
|
|
205
|
+
}
|
|
206
|
+
const joined = translatedParts.join("\n");
|
|
207
|
+
const joinedValidation = await validateDocTranslatePair(original, joined);
|
|
208
|
+
if (!joinedValidation.ok) {
|
|
209
|
+
throwQualityFatal(params, [`joined sub-parts: ${joinedValidation.errors.join("; ")}`]);
|
|
210
|
+
}
|
|
211
|
+
const modelUsed = [...modelsUsed].sort((a, b) => a.localeCompare(b)).join(",");
|
|
212
|
+
return {
|
|
213
|
+
text: joined,
|
|
214
|
+
modelUsed,
|
|
215
|
+
inTok,
|
|
216
|
+
outTok,
|
|
217
|
+
cost,
|
|
218
|
+
validationFailures,
|
|
219
|
+
individualCalls,
|
|
220
|
+
qualitySplitRetries,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=doc-translate-quality-retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-translate-quality-retry.js","sourceRoot":"","sources":["../../src/cli/doc-translate-quality-retry.ts"],"names":[],"mappings":"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"}
|