ai-i18n-tools 1.8.1 → 1.8.2

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.
Files changed (48) hide show
  1. package/README.md +41 -358
  2. package/dist/api/llm-client.d.ts +4 -3
  3. package/dist/api/llm-client.d.ts.map +1 -1
  4. package/dist/api/llm-client.js +6 -5
  5. package/dist/api/llm-client.js.map +1 -1
  6. package/dist/build-info.generated.d.ts +1 -1
  7. package/dist/build-info.generated.js +1 -1
  8. package/dist/cli/index.js +13 -2
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/core/cache.d.ts +17 -0
  11. package/dist/core/cache.d.ts.map +1 -1
  12. package/dist/core/cache.js +43 -0
  13. package/dist/core/cache.js.map +1 -1
  14. package/dist/core/config.d.ts +2 -2
  15. package/dist/core/config.d.ts.map +1 -1
  16. package/dist/core/config.js +2 -2
  17. package/dist/core/config.js.map +1 -1
  18. package/dist/core/han-variant-data.d.ts +1 -1
  19. package/dist/core/han-variant-data.js +1 -1
  20. package/dist/core/locale-utils.d.ts +9 -0
  21. package/dist/core/locale-utils.d.ts.map +1 -1
  22. package/dist/core/locale-utils.js +24 -0
  23. package/dist/core/locale-utils.js.map +1 -1
  24. package/dist/core/prompt-builder.d.ts +7 -6
  25. package/dist/core/prompt-builder.d.ts.map +1 -1
  26. package/dist/core/prompt-builder.js +6 -5
  27. package/dist/core/prompt-builder.js.map +1 -1
  28. package/dist/core/prompts.d.ts +1 -1
  29. package/dist/core/prompts.d.ts.map +1 -1
  30. package/dist/core/ui-languages.d.ts +7 -0
  31. package/dist/core/ui-languages.d.ts.map +1 -1
  32. package/dist/core/ui-languages.js +39 -0
  33. package/dist/core/ui-languages.js.map +1 -1
  34. package/dist/i18n/locales/de.json +2 -1
  35. package/dist/i18n/locales/es.json +2 -1
  36. package/dist/i18n/locales/fr.json +2 -1
  37. package/dist/i18n/locales/hi.json +656 -0
  38. package/dist/i18n/locales/ja.json +2 -1
  39. package/dist/i18n/locales/ko.json +2 -1
  40. package/dist/i18n/locales/pt-BR.json +2 -1
  41. package/dist/i18n/locales/ui-languages.json +3 -3
  42. package/dist/i18n/locales/zh-Hans.json +2 -1
  43. package/dist/i18n/locales/zh-Hant.json +2 -1
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +2 -2
  47. package/dist/index.js.map +1 -1
  48. package/package.json +2 -3
package/README.md CHANGED
@@ -2,398 +2,81 @@
2
2
  <img src="docs/public/ai-i18n-tools_logo.png" alt="ai-i18n-tools logo" width="128" />
3
3
  </p>
4
4
 
5
- <a id="ai-i18n-tools"></a>
6
5
  # ai-i18n-tools
7
6
 
8
-
9
- <small id="lang-list">[English (UK)](./README.md) · [Deutsch](./translated-docs/README.de.md) · [Español](./translated-docs/README.es.md) · [Français](./translated-docs/README.fr.md) · [Hindi (Roman)](./translated-docs/README.hi-Latn.md) · [日本語](./translated-docs/README.ja.md) · [한국어](./translated-docs/README.ko.md) · [Português (Brasil)](./translated-docs/README.pt-BR.md) · [简体中文](./translated-docs/README.zh-Hans.md) · [繁體中文](./translated-docs/README.zh-Hant.md)</small>
10
-
7
+ <small id="lang-list">[English (UK)](./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-Hans.md) · [繁體中文](./translated-docs/README.zh-Hant.md)</small>
11
8
 
12
9
  [![npm version](https://img.shields.io/npm/v/ai-i18n-tools.svg)](https://www.npmjs.com/package/ai-i18n-tools) [![npm downloads](https://img.shields.io/npm/dm/ai-i18n-tools.svg)](https://www.npmjs.com/package/ai-i18n-tools) [![Node.js](https://img.shields.io/node/v/ai-i18n-tools.svg)](https://nodejs.org/) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/wsj-br/ai-i18n-tools/blob/main/LICENSE) [![CI](https://github.com/wsj-br/ai-i18n-tools/actions/workflows/ci.yml/badge.svg)](https://github.com/wsj-br/ai-i18n-tools/actions/workflows/ci.yml)
13
10
 
11
+ **Translate your app and documentation with the AI model of your choice — no lock-in, no rewrites.**
14
12
 
15
- **Translate your app and documentation using the AI model of your choice: no lock-in, no rewrites.**
16
-
17
-
18
-
19
- `ai-i18n-tools` is a CLI and toolkit for internationalizing JavaScript/TypeScript applications and documentation sites - including Docusaurus, Astro, Starlight, VitePress, Nextra, Fumadocs, and plain Markdown/MDX - using large language models.
20
-
21
- Choose from built-in presets (**OpenAI**, **Anthropic**, **Google Gemini**, **NVIDIA**, **DeepSeek**, **Groq**, **Mistral**, **xAI**, **Cerebras**, **Alibaba**, **APIFUN**, **OpenRouter**, **Ollama**) or point at any OpenAI-compatible API. Switch providers or models per project—or even per language—without modifying your codebase.
22
-
23
- One config file drives three translation modes, so you can mix and match based on how your content is structured:
24
-
25
- - **UI strings** — Extracts `t("…")` calls from JS/TS (and optionally `.astro` files) and generates flat, per-locale JSON for i18next or static SSG lookup.
26
- - **Documents** — Translates Markdown, MDX, and `.astro` pages listed in `docs[].contentPaths` using `translate-docs`. Works with **VitePress**, **Starlight**, **Docusaurus**, **Nextra**, **Fumadocs**, Astro-based sites, or any static site generator that reads from Markdown/MDX/`.astro` source files.
27
- - **JSON** — Translates arbitrary nested JSON bundles defined in `json[]`. Use `translate-json` when UI copy lives in per-locale JSON files instead of `t()` calls in source.
28
-
29
- **SVG** assets get their own path: `features.translateSVG`, the top-level `svg` block, and `translate-svg`—not `docs[].contentPaths`.
30
-
31
- **Which should I use?**
32
-
33
- | Your content | Command |
34
- |-------------------------------------------------------------------------------|---------------------------------------------|
35
- | Source code uses `t()` | **UI strings** — `extract` / `translate-ui` |
36
- | Localized pages or docs sites (VitePress, Starlight, Docusaurus, Nextra, Fumadocs, Astro, etc.) | **Documents** — `translate-docs` |
37
- | Standalone, nested JSON locale files | **JSON** — `translate-json` |
38
-
39
- All three share a file/SQLite cache, so only new or changed segments (strings or text chunks) are ever re-sent to the model — reruns are fast and cheap regardless of which provider you're using.
40
-
41
-
42
- <!-- START doctoc generated TOC please keep comment here to allow auto update -->
43
- <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
44
- **Table of Contents**
13
+ CLI and toolkit for internationalizing JavaScript/TypeScript apps and documentation sites (VitePress, Starlight, Docusaurus, Nextra, Fumadocs, Astro, plain Markdown/MDX). Use built-in presets for OpenAI, Anthropic, Gemini, OpenRouter, Ollama, and more — or any OpenAI-compatible API. Switch provider or model per project or per locale without changing your codebase.
45
14
 
46
- - [Translation types](#translation-types)
47
- - [Installation](#installation)
48
- - [Using the CLI](#using-the-cli)
49
- - [LLM providers](#llm-providers)
50
- - [Quick start](#quick-start)
51
- - [UI strings](#ui-strings)
52
- - [Documents](#documents)
53
- - [VitePress](#vitepress)
54
- - [Nextra](#nextra)
55
- - [Fumadocs](#fumadocs)
56
- - [Astro (plain Astro & Starlight)](#astro-plain-astro--starlight)
57
- - [Combined sync](#combined-sync)
58
- - [Runtime helpers](#runtime-helpers)
59
- - [CLI commands](#cli-commands)
60
- - [Tool UI language (logs, help, dashboard)](#tool-ui-language-logs-help-dashboard)
61
- - [Documentation](#documentation)
62
- - [License](#license)
15
+ ## Features
63
16
 
64
- <!-- END doctoc generated TOC please keep comment here to allow auto update -->
17
+ | | |
18
+ | --- | --- |
19
+ | **UI strings** | Extract `t("…")` from JS/TS/Astro (and `data-i18n*` in HTML) → flat per-locale JSON |
20
+ | **Documents** | Translate Markdown, MDX, and `.astro` pages for major doc frameworks |
21
+ | **JSON** | Translate nested locale bundles when copy lives outside `t()` calls |
22
+ | **SVG** | Translate illustrated SVG labels via `translate-svg` |
23
+ | **Smart cache** | Shared SQLite cache — only new or changed segments hit the model |
24
+ | **One `sync`** | Runs extract → UI → SVG → docs → JSON in the right order from one config |
65
25
 
26
+ ## Which pipeline?
66
27
 
28
+ | Your content | Command |
29
+ | --- | --- |
30
+ | Source uses `t()` or HTML markers | **UI strings** — `extract` / `translate-ui` |
31
+ | Localized pages or docs sites | **Documents** — `translate-docs` |
32
+ | Standalone nested JSON locale files | **JSON** — `translate-json` |
67
33
 
68
- <a id="translation-types"></a>
69
- ## Translation types
34
+ See [What is ai-i18n-tools?](docs/guide/what-is-ai-i18n-tools.md) for a full comparison.
70
35
 
71
- Each translation type has its own guide with full configuration details: [UI strings](docs/guide/ui-strings/), [Documents](docs/guide/documents/), and [JSON](docs/guide/json.md). See [What is ai-i18n-tools?](docs/guide/what-is-ai-i18n-tools.md) for a side-by-side comparison.
36
+ ## Install
72
37
 
73
- A few things worth knowing up front: UI strings translates missing entries per locale via the active LLM provider (see [LLM providers](#llm-providers)) and writes flat JSON files (`de.json`, `pt-BR.json`, …), with the English source text as the runtime lookup key — `strings.json` is the extraction cache, not the runtime bundle. Documents supports `docs[].docsOutput.style` values `"nested"`, `"flat"`, `"doc-system"`, and aliases `"docusaurus"` / `"astro-starlight"` / `"vitepress"` / `"nextra"` / `"fumadocs"` (see [Output layouts](docs/guide/documents/output-layouts.md)). All three 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.
74
-
75
- ---
76
-
77
- <a id="installation"></a>
78
- ## Installation
79
-
80
- The published package is **ESM-only** (`"type": "module"`). Node.js `>=22.16.0` required.
38
+ ESM-only. Requires Node.js `>=22.16.0`.
81
39
 
82
40
  ```bash
83
- npm install ai-i18n-tools
84
- # or
85
41
  pnpm add ai-i18n-tools
42
+ # or: npm install ai-i18n-tools
86
43
  ```
87
44
 
88
- <a id="using-the-cli"></a>
89
- ### Using the CLI
90
-
91
- After you install the package in your project, npm/pnpm/yarn link the published bin entry (`bin/ai-i18n-tools.mjs`) into `node_modules/.bin/ai-i18n-tools`. That shim loads the compiled CLI from the installed package.
92
-
93
- To type the bare `ai-i18n-tools` command in an interactive shell, configure one of the options below. Without setup, the shell cannot find the binary even after a local install.
94
-
95
- **direnv** — add to a `.envrc` in the project root (bash/zsh; see [direnv.net](https://direnv.net/)):
45
+ Set an API key for your provider (default `init` uses OpenRouter; Ollama needs none):
96
46
 
97
47
  ```bash
98
- PATH_add node_modules/.bin
99
- ```
100
-
101
- After `direnv allow`, the bare command is available whenever you `cd` into the project.
102
-
103
- **Manual PATH** — from the project root in an interactive shell:
104
-
105
- ```bash
106
- # bash/zsh
107
- export PATH="$PWD/node_modules/.bin:$PATH"
108
- ai-i18n-tools sync
109
- ```
110
-
111
- ```powershell
112
- # Windows PowerShell
113
- $env:Path = "$PWD\node_modules\.bin;$env:Path"
114
- ai-i18n-tools sync
48
+ export OPENROUTER_API_KEY=sk-or-v1-your-key-here
115
49
  ```
116
50
 
117
- **Global install** — install the CLI once and invoke it from any directory:
118
-
119
- ```bash
120
- npm install -g ai-i18n-tools
121
- # or
122
- pnpm add -g ai-i18n-tools
123
- ```
124
-
125
- A global install uses the globally pinned version. For per-project version pinning, prefer direnv or manual PATH so `node_modules/.bin` resolves to the project's dependency.
126
-
127
- **`package.json` scripts** — when npm or pnpm runs a script, it prepends `node_modules/.bin` to `PATH`, so the bare command name works inside scripts without shell PATH changes:
128
-
129
- ```json
130
- "scripts": {
131
- "i18n:extract": "ai-i18n-tools extract",
132
- "i18n:sync": "ai-i18n-tools sync",
133
- "i18n:translate:ui": "ai-i18n-tools translate-ui",
134
- "i18n:translate:svg": "ai-i18n-tools translate-svg",
135
- "i18n:translate:docs": "ai-i18n-tools translate-docs",
136
- "i18n:translate:json": "ai-i18n-tools translate-json",
137
- "i18n:translate": "ai-i18n-tools translate-docs",
138
- "i18n:dashboard": "ai-i18n-tools dashboard"
139
- }
140
- ```
141
-
142
- Then run e.g. `pnpm run i18n:sync` — scripts resolve the local binary without extra shell setup.
143
-
144
- **Alternatives** — if you prefer not to adjust `PATH`: `npx ai-i18n-tools …` (npm) or `pnpm exec ai-i18n-tools …` (pnpm). For a zero-install one-off with no `package.json` entry: `npx ai-i18n-tools <cmd>` or `pnpm dlx ai-i18n-tools <cmd>`.
145
-
146
- 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/guide/quick-start.md#recommended-packagejson-scripts) in the Quick start guide.
147
-
148
- Set the API key for your chosen provider (environment variable names are in [LLM providers](#llm-providers)):
149
-
150
- ```bash
151
- export PROVIDER_API_KEY=sk-your-key-here
152
- ```
153
-
154
- ---
155
-
156
- <a id="llm-providers"></a>
157
- ## LLM providers
158
-
159
- Translation commands (`translate-ui`, `translate-docs`, `translate-json`, `sync`, `check-models`, and related scripts) call an LLM provider; `check-markdown`, `mark-html`, and `extract` do not.
160
-
161
- Configure providers under a top-level `providers` map and pick the active one with a top-level `provider` selector (optional when exactly one provider is configured). Most providers need only a `translationModels` list — `baseUrl` and the API-key environment variable come from a built-in preset; you can override `baseUrl`, `apiKeyEnv`, `headers`, `maxTokens`, `temperature`, and `requestTimeoutMs` per provider. `requestTimeoutMs` is the maximum time in milliseconds to wait for each request (default `30000`).
162
-
163
- Optional model tiers on each provider block:
164
-
165
- - `translationModels` — global ordered fallback chain (required for translation features).
166
- - `uiModels` — UI-only chain (`translate-ui`, plural generation, `proofread-ui`): tried after any matching `localeModels` entry, before `translationModels`.
167
- - `localeModels` — per-locale overrides for **all** pipelines: each entry maps a BCP-47 locale to an ordered model list tried first for that locale only (`pt-br` matches `pt-BR`).
168
-
169
- Resolution order: **UI** → `localeModels(locale)` → `uiModels` → `translationModels`; **docs / JSON / SVG** → `localeModels(locale)` → `translationModels`. Duplicate model ids are skipped while preserving order.
170
-
171
- To switch providers for a single run without editing the config, pass the global `-P` / `--provider <name>` option (e.g. `ai-i18n-tools -P groq translate-ui`); the name must be one of the configured `providers` keys.
172
-
173
- ```jsonc
174
- {
175
- "provider": "ollama",
176
- "providers": {
177
- "groq": { "translationModels": ["llama-3.3-70b-versatile"] },
178
- "openrouter": {
179
- "translationModels": ["qwen/qwen3-235b-a22b-2507", "openai/gpt-4o-mini"],
180
- "uiModels": ["anthropic/claude-sonnet-latest"],
181
- "localeModels": [
182
- { "locale": "pt-BR", "models": ["google/gemini-3-flash-preview"] }
183
- ]
184
- },
185
- "ollama": { "baseUrl": "http://localhost:11434/v1", "translationModels": ["llama3.2"] }
186
- }
187
- }
188
- ```
189
-
190
- Built-in provider presets (key — base URL — API-key env var):
191
-
192
- | Provider | Base URL | API-key env var |
193
- |--------------|-----------------------------------------------------------|----------------------|
194
- | `alibaba` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | `ALIBABA_API_KEY` |
195
- | `anthropic` | `https://api.anthropic.com/v1` | `ANTHROPIC_API_KEY` |
196
- | `apifun` | `https://api.apikey.fun/v1` | `APIFUN_API_KEY` |
197
- | `cerebras` | `https://api.cerebras.ai/v1` | `CEREBRAS_API_KEY` |
198
- | `deepseek` | `https://api.deepseek.com` | `DEEPSEEK_API_KEY` |
199
- | `gemini` | `https://generativelanguage.googleapis.com/v1beta/openai` | `GOOGLE_API_KEY` |
200
- | `groq` | `https://api.groq.com/openai/v1` | `GROQ_API_KEY` |
201
- | `mistral` | `https://api.mistral.ai/v1` | `MISTRAL_API_KEY` |
202
- | `nvidia` | `https://integrate.api.nvidia.com/v1` | `NVIDIA_API_KEY` |
203
- | `ollama` | `http://localhost:11434/v1` | (none) |
204
- | `openai` | `https://api.openai.com/v1` | `OPENAI_API_KEY` |
205
- | `openrouter` | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` |
206
- | `xai` | `https://api.x.ai/v1` | `XAI_API_KEY` |
51
+ Configure the bare `ai-i18n-tools` command (direnv, PATH, `package.json` scripts, or `npx`) — see [Installation](docs/guide/installation.md).
207
52
 
208
- Define a custom OpenAI-compatible provider by adding a new key with `baseUrl` (and `apiKeyEnv` unless it needs no key). Model ids are plain upstream ids — the provider is chosen at the config level, so no `provider/` prefix is needed (OpenRouter ids keep their native `vendor/model` form).
209
-
210
- Token usage is reported for every provider; exact USD cost is shown only when the provider returns it. `ai-i18n-tools check-models` validates all configured model ids (`translationModels`, `uiModels`, and every `localeModels` entry) against the active provider's live `GET /models` list, and shows pricing when the provider returns it. `ai-i18n-tools list-models` lists every model the active provider advertises (use `-P` / `--provider` to inspect another configured provider). `ai-i18n-tools bench-models` benchmarks every unique configured model id (`translationModels`, `uiModels`, and `localeModels`) by translating a sample in isolation (models run in parallel, bounded by `concurrency`) and prints per-model input/output tokens, wall-clock time, and USD cost.
211
-
212
- For a hands-on demo of switching providers with `-P` on a single document, see [`examples/multi-provider`](https://github.com/wsj-br/ai-i18n-tools/tree/main/examples/multi-provider/).
213
-
214
- ---
215
-
216
- <a id="quick-start"></a>
217
53
  ## Quick start
218
54
 
219
- Configure your shell for the bare command first — see [Using the CLI](#using-the-cli).
220
-
221
- <a id="ui-strings"></a>
222
- ### UI strings
223
-
224
55
  ```bash
225
- # 1. Create config (default ui-markdown; plain Astro: init -t ui-astro-website)
226
- ai-i18n-tools init [-P <provider>]
227
-
228
- # 2. Extract UI strings to strings.json
229
- ai-i18n-tools extract
230
-
231
- # 3. Translate to all target locales
232
- ai-i18n-tools translate-ui
56
+ ai-i18n-tools init [-P <provider>] # scaffold config (default: UI strings)
57
+ ai-i18n-tools sync # extract + translate per features
233
58
  ```
234
59
 
235
- Then wire i18next in your app using the helpers from `'ai-i18n-tools/runtime'`. See [Step 4: Wire i18next at runtime](docs/guide/ui-strings/i18next-runtime.md) in the UI strings guide for the full setup.
236
-
237
- <a id="documents"></a>
238
- ### Documents
239
-
240
- 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`:
241
-
242
- ```bash
243
- # Docusaurus docs + optional write-translations catalog
244
- ai-i18n-tools init -t ui-docusaurus [-P <provider>]
245
-
246
- # Astro Starlight documentation
247
- # ai-i18n-tools init -t ui-starlight [-P <provider>]
248
-
249
- # VitePress documentation (pages + theme catalog)
250
- # ai-i18n-tools init -t ui-vitepress [-P <provider>]
251
-
252
- # Nextra documentation (pages + _meta.ts + theme dictionary)
253
- # ai-i18n-tools init -t ui-nextra [-P <provider>]
254
-
255
- # Fumadocs documentation (pages + meta.json + UI catalog)
256
- # ai-i18n-tools init -t ui-fumadocs [-P <provider>]
257
-
258
- # Plain Astro website — UI extraction for t() in .astro; add docs[] for page HTML (see Astro below)
259
- # ai-i18n-tools init -t ui-astro-website [-P <provider>]
260
-
261
- ai-i18n-tools translate-docs
262
- ai-i18n-tools status
263
- # ai-i18n-tools translate-docs --locale de # single locale
264
- ```
265
-
266
- 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"`, `"vitepress"`, `"nextra"`, `"fumadocs"`, `"flat"`, etc.). Full field reference: [Documents](docs/guide/documents/).
267
-
268
- <a id="vitepress"></a>
269
- ### VitePress
270
-
271
- `init -t ui-vitepress` scaffolds `docsOutput.style: "vitepress"` plus `docsOutput.vitepressThemeCatalog` for nav/sidebar/footer strings. Run `sync` to translate page markdown and the theme catalog together — no separate JSON pipeline. See [VitePress integration](docs/guide/integrations/vitepress.md) and [examples/vitepress-docs](https://github.com/wsj-br/ai-i18n-tools/tree/main/examples/vitepress-docs/).
272
-
273
- <a id="nextra"></a>
274
- ### Nextra
275
-
276
- `init -t ui-nextra` scaffolds `docsOutput.style: "nextra"`. `translate-docs` automatically collects and translates `_meta.ts` sidebar labels; set `docs[].nextraDictionaryPath` to also translate the theme dictionary module (e.g. `app/_dictionaries/en.ts`) — all in the same `sync` run, no JSON sidecars. See [Nextra integration](docs/guide/integrations/nextra.md) and [examples/nextra-docs](https://github.com/wsj-br/ai-i18n-tools/tree/main/examples/nextra-docs/).
277
-
278
- <a id="fumadocs"></a>
279
- ### Fumadocs
280
-
281
- `init -t ui-fumadocs` scaffolds `docsOutput.style: "fumadocs"` with the dot parser (default) or dir parser for Nextra-style locale folders. `translate-docs` automatically collects and translates `meta.json` sidebar labels; set `docsOutput.fumadocsUiCatalog` to also translate UI overrides in `lib/layout.shared.ts` — all in the same `sync` run, no JSON sidecars. See [Fumadocs integration](docs/guide/integrations/fumadocs.md) and [examples/fumadocs-docs](https://github.com/wsj-br/ai-i18n-tools/tree/main/examples/fumadocs-docs/).
282
-
283
- <a id="astro-plain-astro--starlight"></a>
284
- ### Astro (plain Astro & Starlight)
60
+ Docs-oriented scaffolds: `-t ui-docusaurus`, `ui-starlight`, `ui-vitepress`, `ui-nextra`, `ui-fumadocs`, `ui-astro-website`, or `ui-json-bundles`.
285
61
 
286
- **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 ([Documents — initialise for documentation](docs/guide/documents/index.md#step-1-initialise-for-documentation)).
62
+ Prefer `sync` over chaining individual translate commands. Full walkthrough: [Quick start](docs/guide/quick-start.md).
287
63
 
288
- **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`](https://github.com/wsj-br/ai-i18n-tools/tree/main/examples/astro-website/) (English at `/`, locales at `/{locale}/`).
289
-
290
- Most teams use a **hybrid** of two pipelines:
291
-
292
- | Pipeline | Use for | Commands | Output |
293
- |------------------------|----------------------------------------------------------------------|----------------------------|--------------------------------------------------------|
294
- | **Page HTML** | Headings, paragraphs, nav labels, inline arrays in the template body | `translate-docs` | `src/pages/{locale}/index.astro` per locale |
295
- | **UI strings (`t()`)** | Frontmatter data, tab labels, shared arrays | `extract` → `translate-ui` | `public/locales/{locale}.json` (English source as key) |
296
-
297
- 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/guide/ui-strings/astro-website.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`).
298
-
299
- Wire `t()` at build time without i18next unless you add client islands — see [Astro website UI strings (SSG)](docs/guide/ui-strings/astro-website.md#astro-website-ui-strings-ssg) and the example’s `src/i18n/t.ts`.
300
-
301
- <a id="combined-sync"></a>
302
- ### Combined sync
303
-
304
- ```bash
305
- ai-i18n-tools sync # extract → translate-ui → translate-svg → translate-docs → translate-json (per features)
306
- ```
307
-
308
- ---
309
-
310
- <a id="runtime-helpers"></a>
311
- ## Runtime helpers
312
-
313
- The following helpers are exported from `'ai-i18n-tools/runtime'` and work in any JavaScript environment. You do not need to import i18next to use them:
314
-
315
- | Helper | Description |
316
- |------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
317
- | `defaultI18nInitOptions(sourceLocale)` | Standard i18next init options for key-as-default setups. |
318
- | `setupKeyAsDefaultT(i18n, { stringsJson, sourcePluralFlatBundle? })` | Recommended wiring: key-trim + plural `wrapT` from `strings.json`, optionally merges `translate-ui` `{sourceLocale}.json` plural keys. |
319
- | `wrapT(i18n, options)` | Lower-level plural-aware `t()` wrapper (usually installed by `setupKeyAsDefaultT`). |
320
- | `buildPluralIndexFromStringsJson(entries)` | Builds the plural group index `wrapT` uses from catalog rows with `"plural": true`. |
321
- | `extractInterpolationNamesForWrap(key)` | Parses `{{var}}` names from a source key for `wrapT` / key-trim fallback. |
322
- | `wrapI18nWithKeyTrim(i18n)` | Lower-level key-trim wrapper only (deprecated for app wiring; prefer `setupKeyAsDefaultT`). |
323
- | `makeLocaleLoadersFromManifest(uiLanguages, sourceLocale, makeLoader)` | Builds the `localeLoaders` map for `makeLoadLocale` from `ui-languages.json` (every `code` except `sourceLocale`). |
324
- | `makeLoadLocale(i18n, loaders, sourceLocale)` | Factory for async locale file loading. |
325
- | `getTextDirection(lng)` | Returns `'ltr'` or `'rtl'` for a BCP-47 code. |
326
- | `applyDirection(lng, element?)` | Sets `dir` attribute on `document.documentElement`. |
327
- | `getUILanguageLabel(lang, t)` | Display label for a language menu row (with i18n). |
328
- | `getUILanguageLabelNative(lang)` | Display label without calling `t()` (header-style). |
329
- | `interpolateTemplate(str, vars)` | Low-level `{{var}}` substitution on a plain string (used internally; app code should use `t()` instead). |
330
- | `flipUiArrowsForRtl(text, isRtl)` | Flip `→` to `←` for RTL layouts. |
331
-
332
- ---
333
-
334
- <a id="cli-commands"></a>
335
- ## CLI commands
336
-
337
- ```bash
338
- ai-i18n-tools version
339
- ai-i18n-tools check-models
340
- ai-i18n-tools list-models
341
- ai-i18n-tools bench-models [--model <ids>] [--text <text>|--file <path>] [--source <locale>] [--target <locale>]
342
- ai-i18n-tools list-languages [search]
343
- ai-i18n-tools init [-t ui-markdown|ui-docusaurus|ui-starlight|ui-vitepress|ui-nextra|ui-fumadocs|ui-astro-website|ui-json-bundles] [-o path] [-P <provider>] [--with-translate-ignore]
344
- ai-i18n-tools write-heading-ids …
345
- ai-i18n-tools mark-html [paths...] [--write]
346
- ai-i18n-tools extract
347
- ai-i18n-tools translate-docs …
348
- ai-i18n-tools translate-json …
349
- ai-i18n-tools translate-svg …
350
- ai-i18n-tools translate-ui …
351
- ai-i18n-tools sync-ui …
352
- ai-i18n-tools proofread-ui …
353
- ai-i18n-tools check-markdown [-p|--path <path>] [-f|--file <path>] [--json] [--no-cache]
354
- ai-i18n-tools export-ui-xliff …
355
- ai-i18n-tools sync …
356
- ai-i18n-tools status …
357
- ai-i18n-tools statistics …
358
- ai-i18n-tools cleanup …
359
- ai-i18n-tools clean-temp …
360
- ai-i18n-tools purge-locale -l <code> [-l <code> …] [--dry-run] [-y|--yes] [-f|--force] [--keep-files] [--backup <path>]
361
- ai-i18n-tools dashboard …
362
- ai-i18n-tools generate-ui-languages [--master path] [--dry-run]
363
- ai-i18n-tools glossary-generate
364
- ai-i18n-tools help [command]
365
- ```
366
-
367
-
368
- For plain HTML apps, annotate elements with bare `data-i18n` / `data-i18n-title` / `data-i18n-placeholder` markers (the source text is taken from the element's own textContent / title / placeholder, written once); `mark-html` inserts them for you and `extract` then captures them into `strings.json`. See [Marking HTML for translation](docs/guide/ui-strings/plain-html.md#marking-html-for-translation).
369
-
370
- Complete per-command flag lists are in [CLI reference](docs/reference/cli-commands/). Run `ai-i18n-tools <command> --help` for built-in usage text.
371
-
372
- Global options: `-c <config>` (default: `ai-i18n-tools.config.json`), `-v` (verbose), `-P` / `--provider <name>` (override the active LLM provider; must be configured under `providers`), `-L` / `--ui-lang <code>` (language for the tool's own UI/logs), `-V` / `--version`, and `-h` / `--help` — accepted on every command. `-w` / `--write-logs [path]` tees console output to a log file (default: under the translation cache directory), but only takes effect on the translation and sync commands (`translate-docs`, `translate-json`, `translate-svg`, `translate-ui`, `sync-ui`, `sync`, `cleanup`). Several commands accept `-l` / `--locale <codes>` (comma-separated BCP-47) to limit target locales; `proofread-ui` uses a single source locale. See [CLI reference](docs/reference/cli-commands/) for the command overview.
373
-
374
- <a id="tool-ui-language-logs-help-dashboard"></a>
375
- ### Tool UI language (logs, help, dashboard)
376
-
377
- The tool localizes its own CLI help, log summaries, and Translation Dashboard independently of the locales you translate. By default it follows your OS locale; override with `-L pt-BR`, `export AI_I18N_LANG=es`, or `"uiLanguage"` in config. See [Tool UI language](docs/guide/tool-ui-language.md) for locale resolution, shipped languages, and dashboard behaviour.
378
-
379
- ---
380
-
381
- <a id="documentation"></a>
382
64
  ## Documentation
383
65
 
384
- - [Documentation site](https://wsj-br.github.io/ai-i18n-tools/) — VitePress guide (9 locales on GitHub Pages); slim entry point with links into the full guide.
385
- - [Quick start](docs/guide/quick-start.md) — setup for UI strings, documents, and JSON (UI, docs/`.astro`, JSON bundles, VitePress, Nextra, Fumadocs, Astro Starlight and plain Astro).
386
- - [Locale assets guide](docs/guide/images-and-screenshots/) - screenshots and illustrated SVGs in translated docs (flat link rewriter, screenshot scripts).
387
- - [Architecture](docs/reference/architecture.md) - architecture, internals, programmatic API, and extension points.
388
- - [AI Agent Context](https://github.com/wsj-br/ai-i18n-tools/blob/main/docs/ai-i18n-tools-context.md) - **for apps using the package:** integration prompts for downstream projects (copy into your repo’s agent rules).
389
- - Maintainer guide for **this** repository: `AGENT.md` (rules and workflows; clone-only; not on npm). Pipeline reference: `docs/reference/`. Local dev and publishing: `dev/DEVEL.md`.
66
+ - [Documentation site](https://wsj-br.github.io/ai-i18n-tools/) — guides, integrations, and reference
67
+ - [Installation](docs/guide/installation.md) · [Quick start](docs/guide/quick-start.md) · [Providers and models](docs/guide/providers-and-models.md)
68
+ - [UI strings](docs/guide/ui-strings/) · [Documents](docs/guide/documents/) · [JSON](docs/guide/json.md) · [SVG](docs/guide/svg-translation/)
69
+ - [Integrations](docs/guide/integrations/) — VitePress, Nextra, Fumadocs, Docusaurus, Astro
70
+ - [CLI reference](docs/reference/cli-commands/) · [Configuration](docs/reference/configuration.md) · [Runtime helpers](docs/guide/runtime-helpers.md)
71
+ - [Examples](https://github.com/wsj-br/ai-i18n-tools/tree/main/examples/) — runnable demos (`npx degit …`)
72
+ - [AI Agent Context](https://github.com/wsj-br/ai-i18n-tools/blob/main/docs/ai-i18n-tools-context.md) — integration guide for assistants in consumer repos
73
+
74
+ ## Contributing
390
75
 
391
- ---
76
+ Issues and pull requests are welcome. Maintainer workflows for this repository: [`AGENTS.md`](https://github.com/wsj-br/ai-i18n-tools/blob/main/AGENTS.md) and [`dev/DEVEL.md`](https://github.com/wsj-br/ai-i18n-tools/blob/main/dev/DEVEL.md).
392
77
 
393
- <a id="license"></a>
394
78
  ## License
395
79
 
396
- This project is licensed under the MIT License.
397
- See the [LICENSE](https://github.com/wsj-br/ai-i18n-tools/blob/main/LICENSE) file for details.
80
+ MIT — see [LICENSE](https://github.com/wsj-br/ai-i18n-tools/blob/main/LICENSE).
398
81
 
399
- Copyright &copy; 2026 Waldemar Scudeller Jr.
82
+ Copyright © 2026 Waldemar Scudeller Jr.
@@ -87,14 +87,15 @@ export declare class LlmClient {
87
87
  */
88
88
  private languageLabelForPrompt;
89
89
  /**
90
- * Enforce the target locale's ISO 15924 script subtag on a model response, throwing
90
+ * Enforce the target locale's expected script on a model response, throwing
91
91
  * {@link ScriptValidationError} so the model-fallback loop retries with the next model.
92
92
  *
93
93
  * Uses a statistical dominant-script check ({@link scriptValidationIssue}): Latin text
94
94
  * (code, URLs, brand names, placeholders) and letter-like symbols such as `ℹ` are ignored,
95
95
  * a stray foreign-language quote does not fail the output, and `zh-Hans`/`zh-Hant` are told
96
- * apart via variant-distinct characters. Locales without a script subtag, and composite
97
- * scripts (e.g. `Jpan`, `Kore`), are not enforced.
96
+ * apart via variant-distinct characters. Uses {@link effectiveScriptSubtag} so bare `hi`
97
+ * (Devanagari by default) is enforced like an explicit `*-Deva` tag. Locales with no
98
+ * effective script, and composite scripts (e.g. `Jpan`, `Kore`), are not enforced.
98
99
  */
99
100
  private assertExpectedScript;
100
101
  private appendDebugLog;
@@ -1 +1 @@
1
- {"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/api/llm-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/api/llm-client.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import { generateText } from "ai";
4
4
  import { createOpenAICompatible, } from "@ai-sdk/openai-compatible";
5
5
  import { BatchTranslationError, } from "../core/types.js";
6
6
  import { englishLanguageNameForLocale, normalizeLocale, resolveTranslationModels, } from "../core/config.js";
7
- import { scriptSubtag, scriptValidationIssue } from "../core/locale-utils.js";
7
+ import { effectiveScriptSubtag, scriptValidationIssue } from "../core/locale-utils.js";
8
8
  import { OPENROUTER_PROVIDER_KEY, resolveActiveProvider, resolveApiKey, resolveProviderSettings, } from "../core/llm-providers.js";
9
9
  import { buildDocumentBatchPrompt, buildDocumentSinglePrompt, buildProofreadUIPromptMessages, buildUIPromptMessages, parseBatchJsonArrayResponse, parseBatchJsonObjectResponse, parseBatchTranslationResponse, parseProofreadUIBatchResponse, parsePluralFormsJsonResponse, parseUIJsonArrayResponse, ScriptValidationError, } from "../core/prompt-builder.js";
10
10
  /** OpenRouter: prefer throughput; allow backup providers (top-level `provider` routing field). */
@@ -144,17 +144,18 @@ export class LlmClient {
144
144
  return localeCode;
145
145
  }
146
146
  /**
147
- * Enforce the target locale's ISO 15924 script subtag on a model response, throwing
147
+ * Enforce the target locale's expected script on a model response, throwing
148
148
  * {@link ScriptValidationError} so the model-fallback loop retries with the next model.
149
149
  *
150
150
  * Uses a statistical dominant-script check ({@link scriptValidationIssue}): Latin text
151
151
  * (code, URLs, brand names, placeholders) and letter-like symbols such as `ℹ` are ignored,
152
152
  * a stray foreign-language quote does not fail the output, and `zh-Hans`/`zh-Hant` are told
153
- * apart via variant-distinct characters. Locales without a script subtag, and composite
154
- * scripts (e.g. `Jpan`, `Kore`), are not enforced.
153
+ * apart via variant-distinct characters. Uses {@link effectiveScriptSubtag} so bare `hi`
154
+ * (Devanagari by default) is enforced like an explicit `*-Deva` tag. Locales with no
155
+ * effective script, and composite scripts (e.g. `Jpan`, `Kore`), are not enforced.
155
156
  */
156
157
  assertExpectedScript(text, targetLocale) {
157
- const script = scriptSubtag(targetLocale);
158
+ const script = effectiveScriptSubtag(targetLocale);
158
159
  if (!script) {
159
160
  return;
160
161
  }