ai-i18n-tools 1.7.1 → 1.8.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.
Files changed (301) hide show
  1. package/README.md +191 -110
  2. package/dist/api/llm-client.d.ts +31 -10
  3. package/dist/api/llm-client.d.ts.map +1 -1
  4. package/dist/api/llm-client.js +153 -40
  5. package/dist/api/llm-client.js.map +1 -1
  6. package/dist/api/openrouter-models-catalog.d.ts +32 -0
  7. package/dist/api/openrouter-models-catalog.d.ts.map +1 -0
  8. package/dist/api/openrouter-models-catalog.js +89 -0
  9. package/dist/api/openrouter-models-catalog.js.map +1 -0
  10. package/dist/build-info.generated.d.ts +1 -1
  11. package/dist/build-info.generated.js +1 -1
  12. package/dist/cli/bench-models.d.ts +23 -0
  13. package/dist/cli/bench-models.d.ts.map +1 -0
  14. package/dist/cli/bench-models.js +236 -0
  15. package/dist/cli/bench-models.js.map +1 -0
  16. package/dist/cli/check-markdown.d.ts.map +1 -1
  17. package/dist/cli/check-markdown.js +16 -6
  18. package/dist/cli/check-markdown.js.map +1 -1
  19. package/dist/cli/check-models.d.ts.map +1 -1
  20. package/dist/cli/check-models.js +19 -13
  21. package/dist/cli/check-models.js.map +1 -1
  22. package/dist/cli/clean-temp.d.ts.map +1 -1
  23. package/dist/cli/clean-temp.js +11 -7
  24. package/dist/cli/clean-temp.js.map +1 -1
  25. package/dist/cli/doc-shell-ts-translate.d.ts +28 -0
  26. package/dist/cli/doc-shell-ts-translate.d.ts.map +1 -0
  27. package/dist/cli/doc-shell-ts-translate.js +181 -0
  28. package/dist/cli/doc-shell-ts-translate.js.map +1 -0
  29. package/dist/cli/doc-translate-quality-retry.d.ts.map +1 -1
  30. package/dist/cli/doc-translate-quality-retry.js +15 -4
  31. package/dist/cli/doc-translate-quality-retry.js.map +1 -1
  32. package/dist/cli/doc-translate.d.ts.map +1 -1
  33. package/dist/cli/doc-translate.js +295 -137
  34. package/dist/cli/doc-translate.js.map +1 -1
  35. package/dist/cli/export-ui-xliff.d.ts.map +1 -1
  36. package/dist/cli/export-ui-xliff.js +10 -6
  37. package/dist/cli/export-ui-xliff.js.map +1 -1
  38. package/dist/cli/extract-strings.d.ts.map +1 -1
  39. package/dist/cli/extract-strings.js +55 -14
  40. package/dist/cli/extract-strings.js.map +1 -1
  41. package/dist/cli/format.d.ts +24 -5
  42. package/dist/cli/format.d.ts.map +1 -1
  43. package/dist/cli/format.js +48 -13
  44. package/dist/cli/format.js.map +1 -1
  45. package/dist/cli/fumadocs-meta-translate.d.ts +8 -0
  46. package/dist/cli/fumadocs-meta-translate.d.ts.map +1 -0
  47. package/dist/cli/fumadocs-meta-translate.js +203 -0
  48. package/dist/cli/fumadocs-meta-translate.js.map +1 -0
  49. package/dist/cli/fumadocs-ui-catalog.d.ts +14 -0
  50. package/dist/cli/fumadocs-ui-catalog.d.ts.map +1 -0
  51. package/dist/cli/fumadocs-ui-catalog.js +256 -0
  52. package/dist/cli/fumadocs-ui-catalog.js.map +1 -0
  53. package/dist/cli/generate-ui-languages.d.ts.map +1 -1
  54. package/dist/cli/generate-ui-languages.js +3 -2
  55. package/dist/cli/generate-ui-languages.js.map +1 -1
  56. package/dist/cli/helpers.d.ts +8 -0
  57. package/dist/cli/helpers.d.ts.map +1 -1
  58. package/dist/cli/helpers.js +29 -0
  59. package/dist/cli/helpers.js.map +1 -1
  60. package/dist/cli/index.js +583 -340
  61. package/dist/cli/index.js.map +1 -1
  62. package/dist/cli/list-languages.d.ts.map +1 -1
  63. package/dist/cli/list-languages.js +38 -16
  64. package/dist/cli/list-languages.js.map +1 -1
  65. package/dist/cli/list-models.d.ts.map +1 -1
  66. package/dist/cli/list-models.js +12 -8
  67. package/dist/cli/list-models.js.map +1 -1
  68. package/dist/cli/llm-client-factory.d.ts +12 -0
  69. package/dist/cli/llm-client-factory.d.ts.map +1 -0
  70. package/dist/cli/llm-client-factory.js +25 -0
  71. package/dist/cli/llm-client-factory.js.map +1 -0
  72. package/dist/cli/mark-html.d.ts +31 -0
  73. package/dist/cli/mark-html.d.ts.map +1 -0
  74. package/dist/cli/mark-html.js +68 -0
  75. package/dist/cli/mark-html.js.map +1 -0
  76. package/dist/cli/nextra-dictionary-translate.d.ts +8 -0
  77. package/dist/cli/nextra-dictionary-translate.d.ts.map +1 -0
  78. package/dist/cli/nextra-dictionary-translate.js +76 -0
  79. package/dist/cli/nextra-dictionary-translate.js.map +1 -0
  80. package/dist/cli/nextra-meta-translate.d.ts +8 -0
  81. package/dist/cli/nextra-meta-translate.d.ts.map +1 -0
  82. package/dist/cli/nextra-meta-translate.js +97 -0
  83. package/dist/cli/nextra-meta-translate.js.map +1 -0
  84. package/dist/cli/openrouter-catalog-model-filter.d.ts.map +1 -1
  85. package/dist/cli/openrouter-catalog-model-filter.js +7 -3
  86. package/dist/cli/openrouter-catalog-model-filter.js.map +1 -1
  87. package/dist/cli/{lint-source.d.ts → proofread-ui.d.ts} +17 -17
  88. package/dist/cli/proofread-ui.d.ts.map +1 -0
  89. package/dist/cli/{lint-source.js → proofread-ui.js} +104 -63
  90. package/dist/cli/proofread-ui.js.map +1 -0
  91. package/dist/cli/purge-locale.d.ts +28 -0
  92. package/dist/cli/purge-locale.d.ts.map +1 -0
  93. package/dist/cli/purge-locale.js +260 -0
  94. package/dist/cli/purge-locale.js.map +1 -0
  95. package/dist/cli/translate-json-run.d.ts +2 -1
  96. package/dist/cli/translate-json-run.d.ts.map +1 -1
  97. package/dist/cli/translate-json-run.js +72 -39
  98. package/dist/cli/translate-json-run.js.map +1 -1
  99. package/dist/cli/translate-summary.d.ts +23 -0
  100. package/dist/cli/translate-summary.d.ts.map +1 -0
  101. package/dist/cli/translate-summary.js +92 -0
  102. package/dist/cli/translate-summary.js.map +1 -0
  103. package/dist/cli/translate-svg.d.ts.map +1 -1
  104. package/dist/cli/translate-svg.js +41 -67
  105. package/dist/cli/translate-svg.js.map +1 -1
  106. package/dist/cli/translate-ui-strings.d.ts.map +1 -1
  107. package/dist/cli/translate-ui-strings.js +222 -88
  108. package/dist/cli/translate-ui-strings.js.map +1 -1
  109. package/dist/cli/vitepress-catalog-usage.d.ts +8 -0
  110. package/dist/cli/vitepress-catalog-usage.d.ts.map +1 -0
  111. package/dist/cli/vitepress-catalog-usage.js +155 -0
  112. package/dist/cli/vitepress-catalog-usage.js.map +1 -0
  113. package/dist/cli/vitepress-init-scaffold.d.ts +2 -0
  114. package/dist/cli/vitepress-init-scaffold.d.ts.map +1 -0
  115. package/dist/cli/vitepress-init-scaffold.js +174 -0
  116. package/dist/cli/vitepress-init-scaffold.js.map +1 -0
  117. package/dist/cli/vitepress-theme-catalog.d.ts +14 -0
  118. package/dist/cli/vitepress-theme-catalog.d.ts.map +1 -0
  119. package/dist/cli/vitepress-theme-catalog.js +262 -0
  120. package/dist/cli/vitepress-theme-catalog.js.map +1 -0
  121. package/dist/cli/write-heading-ids.d.ts.map +1 -1
  122. package/dist/cli/write-heading-ids.js +17 -8
  123. package/dist/cli/write-heading-ids.js.map +1 -1
  124. package/dist/core/cache.d.ts +6 -0
  125. package/dist/core/cache.d.ts.map +1 -1
  126. package/dist/core/cache.js +9 -0
  127. package/dist/core/cache.js.map +1 -1
  128. package/dist/core/config-migrate.d.ts.map +1 -1
  129. package/dist/core/config-migrate.js +16 -0
  130. package/dist/core/config-migrate.js.map +1 -1
  131. package/dist/core/config.d.ts +48 -13
  132. package/dist/core/config.d.ts.map +1 -1
  133. package/dist/core/config.js +231 -70
  134. package/dist/core/config.js.map +1 -1
  135. package/dist/core/doc-file-tracking.d.ts +9 -0
  136. package/dist/core/doc-file-tracking.d.ts.map +1 -1
  137. package/dist/core/doc-file-tracking.js +35 -2
  138. package/dist/core/doc-file-tracking.js.map +1 -1
  139. package/dist/core/docs-output-normalize.d.ts +9 -0
  140. package/dist/core/docs-output-normalize.d.ts.map +1 -1
  141. package/dist/core/docs-output-normalize.js +40 -1
  142. package/dist/core/docs-output-normalize.js.map +1 -1
  143. package/dist/core/fumadocs-dot-source-filter.d.ts +16 -0
  144. package/dist/core/fumadocs-dot-source-filter.d.ts.map +1 -0
  145. package/dist/core/fumadocs-dot-source-filter.js +61 -0
  146. package/dist/core/fumadocs-dot-source-filter.js.map +1 -0
  147. package/dist/core/fumadocs-parser.d.ts +8 -0
  148. package/dist/core/fumadocs-parser.d.ts.map +1 -0
  149. package/dist/core/fumadocs-parser.js +15 -0
  150. package/dist/core/fumadocs-parser.js.map +1 -0
  151. package/dist/core/han-variant-data.d.ts +36 -0
  152. package/dist/core/han-variant-data.d.ts.map +1 -0
  153. package/dist/core/han-variant-data.js +50 -0
  154. package/dist/core/han-variant-data.js.map +1 -0
  155. package/dist/core/llm-providers.d.ts +11 -1
  156. package/dist/core/llm-providers.d.ts.map +1 -1
  157. package/dist/core/llm-providers.js +51 -1
  158. package/dist/core/llm-providers.js.map +1 -1
  159. package/dist/core/locale-utils.d.ts +75 -1
  160. package/dist/core/locale-utils.d.ts.map +1 -1
  161. package/dist/core/locale-utils.js +191 -1
  162. package/dist/core/locale-utils.js.map +1 -1
  163. package/dist/core/output-paths.d.ts +17 -0
  164. package/dist/core/output-paths.d.ts.map +1 -1
  165. package/dist/core/output-paths.js +106 -3
  166. package/dist/core/output-paths.js.map +1 -1
  167. package/dist/core/prompt-builder.d.ts +10 -10
  168. package/dist/core/prompt-builder.d.ts.map +1 -1
  169. package/dist/core/prompt-builder.js +20 -13
  170. package/dist/core/prompt-builder.js.map +1 -1
  171. package/dist/core/prompts.d.ts +8 -4
  172. package/dist/core/prompts.d.ts.map +1 -1
  173. package/dist/core/prompts.js +4 -2
  174. package/dist/core/prompts.js.map +1 -1
  175. package/dist/core/types.d.ts +181 -6
  176. package/dist/core/types.d.ts.map +1 -1
  177. package/dist/core/types.js +118 -11
  178. package/dist/core/types.js.map +1 -1
  179. package/dist/core/ui-languages-catalog.d.ts.map +1 -1
  180. package/dist/core/ui-languages-catalog.js +38 -18
  181. package/dist/core/ui-languages-catalog.js.map +1 -1
  182. package/dist/core/ui-languages.d.ts +3 -1
  183. package/dist/core/ui-languages.d.ts.map +1 -1
  184. package/dist/core/ui-languages.js +5 -3
  185. package/dist/core/ui-languages.js.map +1 -1
  186. package/dist/core/ui-locale.d.ts +37 -0
  187. package/dist/core/ui-locale.d.ts.map +1 -0
  188. package/dist/core/ui-locale.js +78 -0
  189. package/dist/core/ui-locale.js.map +1 -0
  190. package/dist/dashboard-app/app.js +348 -151
  191. package/dist/dashboard-app/index.html +182 -181
  192. package/dist/extractors/classify-segment.js +1 -1
  193. package/dist/extractors/frontmatter-fields.d.ts.map +1 -1
  194. package/dist/extractors/frontmatter-fields.js +17 -0
  195. package/dist/extractors/frontmatter-fields.js.map +1 -1
  196. package/dist/extractors/fumadocs-ui-extractor.d.ts +8 -0
  197. package/dist/extractors/fumadocs-ui-extractor.d.ts.map +1 -0
  198. package/dist/extractors/fumadocs-ui-extractor.js +157 -0
  199. package/dist/extractors/fumadocs-ui-extractor.js.map +1 -0
  200. package/dist/extractors/html-i18n-marks.d.ts +48 -0
  201. package/dist/extractors/html-i18n-marks.d.ts.map +1 -0
  202. package/dist/extractors/html-i18n-marks.js +351 -0
  203. package/dist/extractors/html-i18n-marks.js.map +1 -0
  204. package/dist/extractors/image-markdown.d.ts +11 -0
  205. package/dist/extractors/image-markdown.d.ts.map +1 -0
  206. package/dist/extractors/image-markdown.js +24 -0
  207. package/dist/extractors/image-markdown.js.map +1 -0
  208. package/dist/extractors/markdown-extractor.d.ts.map +1 -1
  209. package/dist/extractors/markdown-extractor.js +38 -19
  210. package/dist/extractors/markdown-extractor.js.map +1 -1
  211. package/dist/extractors/ts-object-literal-extractor.d.ts +17 -0
  212. package/dist/extractors/ts-object-literal-extractor.d.ts.map +1 -0
  213. package/dist/extractors/ts-object-literal-extractor.js +165 -0
  214. package/dist/extractors/ts-object-literal-extractor.js.map +1 -0
  215. package/dist/extractors/ui-string-babel.d.ts.map +1 -1
  216. package/dist/extractors/ui-string-babel.js +11 -3
  217. package/dist/extractors/ui-string-babel.js.map +1 -1
  218. package/dist/extractors/vitepress-theme-extractor.d.ts +9 -0
  219. package/dist/extractors/vitepress-theme-extractor.d.ts.map +1 -0
  220. package/dist/extractors/vitepress-theme-extractor.js +194 -0
  221. package/dist/extractors/vitepress-theme-extractor.js.map +1 -0
  222. package/dist/i18n/index.d.ts +59 -0
  223. package/dist/i18n/index.d.ts.map +1 -0
  224. package/dist/i18n/index.js +186 -0
  225. package/dist/i18n/index.js.map +1 -0
  226. package/dist/i18n/locales/de.json +655 -0
  227. package/dist/i18n/locales/es.json +655 -0
  228. package/dist/i18n/locales/fr.json +655 -0
  229. package/dist/i18n/locales/hi-Latn.json +655 -0
  230. package/dist/i18n/locales/ja.json +655 -0
  231. package/dist/i18n/locales/ko.json +655 -0
  232. package/dist/i18n/locales/pt-BR.json +655 -0
  233. package/dist/i18n/locales/ui-languages.json +63 -0
  234. package/dist/i18n/locales/zh-Hans.json +655 -0
  235. package/dist/i18n/locales/zh-Hant.json +655 -0
  236. package/dist/index.d.ts +16 -6
  237. package/dist/index.d.ts.map +1 -1
  238. package/dist/index.js +15 -5
  239. package/dist/index.js.map +1 -1
  240. package/dist/processors/admonition-placeholders.d.ts +3 -1
  241. package/dist/processors/admonition-placeholders.d.ts.map +1 -1
  242. package/dist/processors/admonition-placeholders.js +29 -8
  243. package/dist/processors/admonition-placeholders.js.map +1 -1
  244. package/dist/processors/admonition-syntax.d.ts +39 -0
  245. package/dist/processors/admonition-syntax.d.ts.map +1 -0
  246. package/dist/processors/admonition-syntax.js +39 -0
  247. package/dist/processors/admonition-syntax.js.map +1 -0
  248. package/dist/processors/anchor-placeholders.d.ts +2 -0
  249. package/dist/processors/anchor-placeholders.d.ts.map +1 -1
  250. package/dist/processors/anchor-placeholders.js +2 -1
  251. package/dist/processors/anchor-placeholders.js.map +1 -1
  252. package/dist/processors/doc-postprocess.js +2 -2
  253. package/dist/processors/doc-postprocess.js.map +1 -1
  254. package/dist/processors/fumadocs-link-normalize.d.ts +17 -0
  255. package/dist/processors/fumadocs-link-normalize.d.ts.map +1 -0
  256. package/dist/processors/fumadocs-link-normalize.js +124 -0
  257. package/dist/processors/fumadocs-link-normalize.js.map +1 -0
  258. package/dist/processors/markdown-source-diagnostics.d.ts +19 -0
  259. package/dist/processors/markdown-source-diagnostics.d.ts.map +1 -1
  260. package/dist/processors/markdown-source-diagnostics.js +96 -3
  261. package/dist/processors/markdown-source-diagnostics.js.map +1 -1
  262. package/dist/processors/nextra-link-normalize.d.ts +17 -0
  263. package/dist/processors/nextra-link-normalize.d.ts.map +1 -0
  264. package/dist/processors/nextra-link-normalize.js +124 -0
  265. package/dist/processors/nextra-link-normalize.js.map +1 -0
  266. package/dist/processors/placeholder-handler.d.ts +5 -1
  267. package/dist/processors/placeholder-handler.d.ts.map +1 -1
  268. package/dist/processors/placeholder-handler.js +10 -4
  269. package/dist/processors/placeholder-handler.js.map +1 -1
  270. package/dist/processors/translation-placeholder-leaks.js +1 -1
  271. package/dist/processors/translation-placeholder-leaks.js.map +1 -1
  272. package/dist/processors/validator.d.ts.map +1 -1
  273. package/dist/processors/validator.js +10 -0
  274. package/dist/processors/validator.js.map +1 -1
  275. package/dist/processors/vitepress-link-normalize.d.ts +37 -0
  276. package/dist/processors/vitepress-link-normalize.d.ts.map +1 -0
  277. package/dist/processors/vitepress-link-normalize.js +286 -0
  278. package/dist/processors/vitepress-link-normalize.js.map +1 -0
  279. package/dist/runtime/i18next-helpers.d.ts.map +1 -1
  280. package/dist/runtime/i18next-helpers.js.map +1 -1
  281. package/dist/server/translation-dashboard.d.ts +10 -0
  282. package/dist/server/translation-dashboard.d.ts.map +1 -1
  283. package/dist/server/translation-dashboard.js +4 -0
  284. package/dist/server/translation-dashboard.js.map +1 -1
  285. package/dist/utils/load-dotenv.d.ts +10 -0
  286. package/dist/utils/load-dotenv.d.ts.map +1 -0
  287. package/dist/utils/load-dotenv.js +27 -0
  288. package/dist/utils/load-dotenv.js.map +1 -0
  289. package/dist/utils/table.d.ts +29 -0
  290. package/dist/utils/table.d.ts.map +1 -0
  291. package/dist/utils/table.js +102 -0
  292. package/dist/utils/table.js.map +1 -0
  293. package/docs/ai-i18n-tools-context.md +50 -47
  294. package/package.json +37 -33
  295. package/dist/cli/lint-source.d.ts.map +0 -1
  296. package/dist/cli/lint-source.js.map +0 -1
  297. package/docs/GETTING_STARTED.md +0 -1753
  298. package/docs/LOCALE-ASSETS-GUIDE.md +0 -671
  299. package/docs/PACKAGE_OVERVIEW.md +0 -557
  300. package/docs/markdown-mdx-stress-test.md +0 -170
  301. package/docs/translation-dashboard.png +0 -0
package/README.md CHANGED
@@ -1,46 +1,59 @@
1
1
  <a id="ai-i18n-tools"></a>
2
2
  # ai-i18n-tools
3
3
 
4
- [![npm version](https://img.shields.io/npm/v/ai-i18n-tools.svg)](https://www.npmjs.com/package/ai-i18n-tools)
5
- [![npm downloads](https://img.shields.io/npm/dm/ai-i18n-tools.svg)](https://www.npmjs.com/package/ai-i18n-tools)
6
- [![Node.js](https://img.shields.io/node/v/ai-i18n-tools.svg)](https://nodejs.org/)
7
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
8
- [![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)
4
+ [![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)
9
5
 
10
- A CLI and toolkit for internationalizing JavaScript/TypeScript applications and documentation sites using large language models. It works with [OpenRouter](https://openrouter.ai/) and any OpenAI-compatible provider (OpenAI, Anthropic, Gemini, DeepSeek, Groq, Mistral, xAI, Cerebras, NVIDIA, Alibaba, APIFUN, Ollama, and more). Three modular workflows, all sharing a single config file, support different translation needs:
11
6
 
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.
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) · [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>
15
8
 
16
- **SVG** assets are translated using `features.translateSVG`, the top-level `svg` block, and `translate-svg`—not `docs[].contentPaths`.
17
9
 
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
10
 
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.
11
+ **Translate your app and documentation using the AI model of your choice: no lock-in, no rewrites.**
24
12
 
25
- <small>**Read in other languages:** </small>
26
- <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>
13
+
14
+
15
+ `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.
16
+
17
+ 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.
18
+
19
+ One config file drives three translation modes, so you can mix and match based on how your content is structured:
20
+
21
+ - **UI strings** — Extracts `t("…")` calls from JS/TS (and optionally `.astro` files) and generates flat, per-locale JSON for i18next or static SSG lookup.
22
+ - **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.
23
+ - **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.
24
+
25
+ **SVG** assets get their own path: `features.translateSVG`, the top-level `svg` block, and `translate-svg`—not `docs[].contentPaths`.
26
+
27
+ **Which should I use?**
28
+
29
+ | Your content | Command |
30
+ |-------------------------------------------------------------------------------|---------------------------------------------|
31
+ | Source code uses `t()` | **UI strings** — `extract` / `translate-ui` |
32
+ | Localized pages or docs sites (VitePress, Starlight, Docusaurus, Nextra, Fumadocs, Astro, etc.) | **Documents** — `translate-docs` |
33
+ | Standalone, nested JSON locale files | **JSON** — `translate-json` |
34
+
35
+ 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.
27
36
 
28
37
 
29
38
  <!-- START doctoc generated TOC please keep comment here to allow auto update -->
30
39
  <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
31
40
  **Table of Contents**
32
41
 
33
- - [Core workflows](#core-workflows)
42
+ - [Translation types](#translation-types)
34
43
  - [Installation](#installation)
35
44
  - [Using the CLI](#using-the-cli)
36
- - [LLM providers](#openrouter)
45
+ - [LLM providers](#llm-providers)
37
46
  - [Quick start](#quick-start)
38
- - [Workflow 1 - UI Translation](#workflow-1---ui-translation)
39
- - [Workflow 2 - Document Translation](#workflow-2---document-translation)
47
+ - [UI strings](#ui-strings)
48
+ - [Documents](#documents)
49
+ - [VitePress](#vitepress)
50
+ - [Nextra](#nextra)
51
+ - [Fumadocs](#fumadocs)
40
52
  - [Astro (plain Astro & Starlight)](#astro-plain-astro--starlight)
41
- - [Combined workflow](#combined-workflow)
53
+ - [Combined sync](#combined-sync)
42
54
  - [Runtime helpers](#runtime-helpers)
43
55
  - [CLI commands](#cli-commands)
56
+ - [Tool UI language (logs, help, dashboard)](#tool-ui-language-logs-help-dashboard)
44
57
  - [Documentation](#documentation)
45
58
  - [License](#license)
46
59
 
@@ -48,24 +61,12 @@ All workflows maintain a file/SQLite cache to ensure that only new or changed se
48
61
 
49
62
 
50
63
 
64
+ <a id="translation-types"></a>
65
+ ## Translation types
51
66
 
67
+ 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.
52
68
 
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
57
-
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.
59
-
60
- **Workflow 2 - Document Translation** — for markdown, MDX, and `.astro` under `docs[].contentPaths`
61
-
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[]`.
63
-
64
- **Workflow 3 - JSON file translation** — nested locale JSON without `t()` in source
65
-
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.
69
+ 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.
69
70
 
70
71
  ---
71
72
 
@@ -83,13 +84,44 @@ pnpm add ai-i18n-tools
83
84
  <a id="using-the-cli"></a>
84
85
  ### Using the CLI
85
86
 
86
- **Per-project (recommended)** — install as a dev dependency, then run via `npx`, `pnpm exec`, or a `package.json` script:
87
+ 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.
88
+
89
+ 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.
90
+
91
+ **direnv** — add to a `.envrc` in the project root (bash/zsh; see [direnv.net](https://direnv.net/)):
92
+
93
+ ```bash
94
+ PATH_add node_modules/.bin
95
+ ```
96
+
97
+ After `direnv allow`, the bare command is available whenever you `cd` into the project.
98
+
99
+ **Manual PATH** — from the project root in an interactive shell:
87
100
 
88
101
  ```bash
89
- pnpm add -D ai-i18n-tools # or: npm i -D ai-i18n-tools
90
- npx ai-i18n-tools sync # or: pnpm exec ai-i18n-tools sync
102
+ # bash/zsh
103
+ export PATH="$PWD/node_modules/.bin:$PATH"
104
+ ai-i18n-tools sync
91
105
  ```
92
106
 
107
+ ```powershell
108
+ # Windows PowerShell
109
+ $env:Path = "$PWD\node_modules\.bin;$env:Path"
110
+ ai-i18n-tools sync
111
+ ```
112
+
113
+ **Global install** — install the CLI once and invoke it from any directory:
114
+
115
+ ```bash
116
+ npm install -g ai-i18n-tools
117
+ # or
118
+ pnpm add -g ai-i18n-tools
119
+ ```
120
+
121
+ 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.
122
+
123
+ **`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:
124
+
93
125
  ```json
94
126
  "scripts": {
95
127
  "i18n:extract": "ai-i18n-tools extract",
@@ -103,38 +135,49 @@ npx ai-i18n-tools sync # or: pnpm exec ai-i18n-tools sync
103
135
  }
104
136
  ```
105
137
 
106
- You can also use the ai-i18n-tools CLI commands directly, for instance `ai-i18n-tools sync`.
107
-
138
+ Then run e.g. `pnpm run i18n:sync` — scripts resolve the local binary without extra shell setup.
108
139
 
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.
140
+ **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>`.
110
141
 
111
- **Zero-install one-off** — `npx ai-i18n-tools <cmd>` or `pnpm dlx ai-i18n-tools <cmd>` (downloads for that invocation only).
142
+ 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.
112
143
 
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.
114
-
115
- Set your provider API key (OpenRouter shown; use the matching variable for your provider):
144
+ Set the API key for your chosen provider (environment variable names are in [LLM providers](#llm-providers)):
116
145
 
117
146
  ```bash
118
- export OPENROUTER_API_KEY=sk-or-v1-your-key-here
147
+ export PROVIDER_API_KEY=sk-your-key-here
119
148
  ```
120
149
 
121
150
  ---
122
151
 
123
- <a id="openrouter"></a>
152
+ <a id="llm-providers"></a>
124
153
  ## LLM providers
125
154
 
126
- Translation commands (`translate-ui`, `translate-docs`, `translate-json`, `sync`, `check-models`, and related scripts) call an LLM provider; `check-markdown` does not.
155
+ 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.
127
156
 
128
157
  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`).
129
158
 
159
+ Optional model tiers on each provider block:
160
+
161
+ - `translationModels` — global ordered fallback chain (required for translation features).
162
+ - `uiModels` — UI-only chain (`translate-ui`, plural generation, `proofread-ui`): tried after any matching `localeModels` entry, before `translationModels`.
163
+ - `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`).
164
+
165
+ Resolution order: **UI** → `localeModels(locale)` → `uiModels` → `translationModels`; **docs / JSON / SVG** → `localeModels(locale)` → `translationModels`. Duplicate model ids are skipped while preserving order.
166
+
130
167
  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.
131
168
 
132
169
  ```jsonc
133
170
  {
134
- "provider": "openrouter",
171
+ "provider": "ollama",
135
172
  "providers": {
136
- "openrouter": { "translationModels": ["qwen/qwen3-235b-a22b-2507", "openai/gpt-4o-mini"] },
137
173
  "groq": { "translationModels": ["llama-3.3-70b-versatile"] },
174
+ "openrouter": {
175
+ "translationModels": ["qwen/qwen3-235b-a22b-2507", "openai/gpt-4o-mini"],
176
+ "uiModels": ["anthropic/claude-sonnet-latest"],
177
+ "localeModels": [
178
+ { "locale": "pt-BR", "models": ["google/gemini-3-flash-preview"] }
179
+ ]
180
+ },
138
181
  "ollama": { "baseUrl": "http://localhost:11434/v1", "translationModels": ["llama3.2"] }
139
182
  }
140
183
  }
@@ -142,96 +185,120 @@ To switch providers for a single run without editing the config, pass the global
142
185
 
143
186
  Built-in provider presets (key — base URL — API-key env var):
144
187
 
145
- | Provider | Base URL | API-key env var |
146
- | --- | --- | --- |
147
- | `openrouter` | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` |
148
- | `openai` | `https://api.openai.com/v1` | `OPENAI_API_KEY` |
149
- | `anthropic` | `https://api.anthropic.com/v1` | `ANTHROPIC_API_KEY` |
150
- | `gemini` | `https://generativelanguage.googleapis.com/v1beta/openai` | `GOOGLE_API_KEY` |
151
- | `deepseek` | `https://api.deepseek.com` | `DEEPSEEK_API_KEY` |
152
- | `cerebras` | `https://api.cerebras.ai/v1` | `CEREBRAS_API_KEY` |
153
- | `groq` | `https://api.groq.com/openai/v1` | `GROQ_API_KEY` |
154
- | `mistral` | `https://api.mistral.ai/v1` | `MISTRAL_API_KEY` |
155
- | `xai` | `https://api.x.ai/v1` | `XAI_API_KEY` |
156
- | `nvidia` | `https://integrate.api.nvidia.com/v1` | `NVIDIA_API_KEY` |
157
- | `alibaba` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | `ALIBABA_API_KEY` |
158
- | `apifun` | `https://api.apikey.fun/v1` | `APIFUN_API_KEY` |
159
- | `ollama` | `http://localhost:11434/v1` | (none) |
188
+ | Provider | Base URL | API-key env var |
189
+ |--------------|-----------------------------------------------------------|----------------------|
190
+ | `alibaba` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | `ALIBABA_API_KEY` |
191
+ | `anthropic` | `https://api.anthropic.com/v1` | `ANTHROPIC_API_KEY` |
192
+ | `apifun` | `https://api.apikey.fun/v1` | `APIFUN_API_KEY` |
193
+ | `cerebras` | `https://api.cerebras.ai/v1` | `CEREBRAS_API_KEY` |
194
+ | `deepseek` | `https://api.deepseek.com` | `DEEPSEEK_API_KEY` |
195
+ | `gemini` | `https://generativelanguage.googleapis.com/v1beta/openai` | `GOOGLE_API_KEY` |
196
+ | `groq` | `https://api.groq.com/openai/v1` | `GROQ_API_KEY` |
197
+ | `mistral` | `https://api.mistral.ai/v1` | `MISTRAL_API_KEY` |
198
+ | `nvidia` | `https://integrate.api.nvidia.com/v1` | `NVIDIA_API_KEY` |
199
+ | `ollama` | `http://localhost:11434/v1` | (none) |
200
+ | `openai` | `https://api.openai.com/v1` | `OPENAI_API_KEY` |
201
+ | `openrouter` | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` |
202
+ | `xai` | `https://api.x.ai/v1` | `XAI_API_KEY` |
160
203
 
161
204
  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).
162
205
 
163
- Token usage is reported for every provider; exact USD cost is shown only when the provider returns it (OpenRouter). `ai-i18n-tools check-models` validates configured model ids against the active provider's live `GET /models` list (any provider), and shows pricing when the provider returns it (e.g. OpenRouter). `ai-i18n-tools list-models` lists every model the active provider advertises (use `-P` / `--provider` to inspect another configured provider).
206
+ 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.
164
207
 
165
- A legacy top-level `openrouter` config block is still accepted and is automatically migrated to `providers.openrouter` (with `provider: "openrouter"`) on load.
166
-
167
- For a hands-on demo of switching providers with `-P` on a single document, see [`examples/multi-provider`](examples/multi-provider/) (one config with `openai`, `anthropic`, `nvidia`, and `deepseek`).
208
+ 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/).
168
209
 
169
210
  ---
170
211
 
171
212
  <a id="quick-start"></a>
172
213
  ## Quick start
173
214
 
174
- <a id="workflow-1---ui-translation"></a>
175
- ### Workflow 1 - UI Translation
215
+ Configure your shell for the bare command first — see [Using the CLI](#using-the-cli).
216
+
217
+ <a id="ui-strings"></a>
218
+ ### UI strings
176
219
 
177
220
  ```bash
178
221
  # 1. Create config (default ui-markdown; plain Astro: init -t ui-astro-website)
179
- npx ai-i18n-tools init
222
+ ai-i18n-tools init [-P <provider>]
180
223
 
181
224
  # 2. Extract UI strings to strings.json
182
- npx ai-i18n-tools extract
225
+ ai-i18n-tools extract
183
226
 
184
227
  # 3. Translate to all target locales
185
- npx ai-i18n-tools translate-ui
228
+ ai-i18n-tools translate-ui
186
229
  ```
187
230
 
188
- Then wire i18next in your app using the helpers from `'ai-i18n-tools/runtime'`. See [Step 4: Wire i18next at runtime](docs/GETTING_STARTED.md#step-4-wire-i18next-at-runtime) in the Getting Started guide for the full setup.
231
+ 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.
189
232
 
190
- <a id="workflow-2---document-translation"></a>
191
- ### Workflow 2 - Document Translation
233
+ <a id="documents"></a>
234
+ ### Documents
192
235
 
193
236
  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`:
194
237
 
195
238
  ```bash
196
239
  # Docusaurus docs + optional write-translations catalog
197
- npx ai-i18n-tools init -t ui-docusaurus
240
+ ai-i18n-tools init -t ui-docusaurus [-P <provider>]
198
241
 
199
242
  # Astro Starlight documentation
200
- # npx ai-i18n-tools init -t ui-starlight
243
+ # ai-i18n-tools init -t ui-starlight [-P <provider>]
244
+
245
+ # VitePress documentation (pages + theme catalog)
246
+ # ai-i18n-tools init -t ui-vitepress [-P <provider>]
247
+
248
+ # Nextra documentation (pages + _meta.ts + theme dictionary)
249
+ # ai-i18n-tools init -t ui-nextra [-P <provider>]
250
+
251
+ # Fumadocs documentation (pages + meta.json + UI catalog)
252
+ # ai-i18n-tools init -t ui-fumadocs [-P <provider>]
201
253
 
202
254
  # Plain Astro website — UI extraction for t() in .astro; add docs[] for page HTML (see Astro below)
203
- # npx ai-i18n-tools init -t ui-astro-website
255
+ # ai-i18n-tools init -t ui-astro-website [-P <provider>]
204
256
 
205
- npx ai-i18n-tools translate-docs
206
- npx ai-i18n-tools status
207
- # npx ai-i18n-tools translate-docs --locale de # single locale
257
+ ai-i18n-tools translate-docs
258
+ ai-i18n-tools status
259
+ # ai-i18n-tools translate-docs --locale de # single locale
208
260
  ```
209
261
 
210
- 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).
262
+ 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/).
263
+
264
+ <a id="vitepress"></a>
265
+ ### VitePress
266
+
267
+ `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/).
268
+
269
+ <a id="nextra"></a>
270
+ ### Nextra
271
+
272
+ `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/).
273
+
274
+ <a id="fumadocs"></a>
275
+ ### Fumadocs
276
+
277
+ `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/).
211
278
 
212
279
  <a id="astro-plain-astro--starlight"></a>
213
280
  ### Astro (plain Astro & Starlight)
214
281
 
215
- **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)).
282
+ **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)).
216
283
 
217
- **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}/`).
284
+ **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}/`).
218
285
 
219
286
  Most teams use a **hybrid** of two pipelines:
220
287
 
221
- | Pipeline | Use for | Commands | Output |
222
- |----------|---------|----------|--------|
223
- | **Page HTML** | Headings, paragraphs, nav labels, inline arrays in the template body | `translate-docs` | `src/pages/{locale}/index.astro` per locale |
224
- | **UI strings (`t()`)** | Frontmatter data, tab labels, shared arrays | `extract` → `translate-ui` | `public/locales/{locale}.json` (English source as key) |
288
+ | Pipeline | Use for | Commands | Output |
289
+ |------------------------|----------------------------------------------------------------------|----------------------------|--------------------------------------------------------|
290
+ | **Page HTML** | Headings, paragraphs, nav labels, inline arrays in the template body | `translate-docs` | `src/pages/{locale}/index.astro` per locale |
291
+ | **UI strings (`t()`)** | Frontmatter data, tab labels, shared arrays | `extract` → `translate-ui` | `public/locales/{locale}.json` (English source as key) |
225
292
 
226
- 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`).
293
+ 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`).
227
294
 
228
- 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`.
295
+ 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`.
229
296
 
230
- <a id="combined-workflow"></a>
231
- ### Combined workflow
297
+ <a id="combined-sync"></a>
298
+ ### Combined sync
232
299
 
233
300
  ```bash
234
- npx ai-i18n-tools sync # extract → translate-ui → translate-svg → translate-docs → translate-json (per features)
301
+ ai-i18n-tools sync # extract → translate-ui → translate-svg → translate-docs → translate-json (per features)
235
302
  ```
236
303
 
237
304
  ---
@@ -246,8 +313,8 @@ The following helpers are exported from `'ai-i18n-tools/runtime'` and work in an
246
313
  | `defaultI18nInitOptions(sourceLocale)` | Standard i18next init options for key-as-default setups. |
247
314
  | `setupKeyAsDefaultT(i18n, { stringsJson, sourcePluralFlatBundle? })` | Recommended wiring: key-trim + plural `wrapT` from `strings.json`, optionally merges `translate-ui` `{sourceLocale}.json` plural keys. |
248
315
  | `wrapT(i18n, options)` | Lower-level plural-aware `t()` wrapper (usually installed by `setupKeyAsDefaultT`). |
249
- | `buildPluralIndexFromStringsJson(entries)` | Builds the plural group index `wrapT` uses from catalog rows with `"plural": true`. |
250
- | `extractInterpolationNamesForWrap(key)` | Parses `{{var}}` names from a source key for `wrapT` / key-trim fallback. |
316
+ | `buildPluralIndexFromStringsJson(entries)` | Builds the plural group index `wrapT` uses from catalog rows with `"plural": true`. |
317
+ | `extractInterpolationNamesForWrap(key)` | Parses `{{var}}` names from a source key for `wrapT` / key-trim fallback. |
251
318
  | `wrapI18nWithKeyTrim(i18n)` | Lower-level key-trim wrapper only (deprecated for app wiring; prefer `setupKeyAsDefaultT`). |
252
319
  | `makeLocaleLoadersFromManifest(uiLanguages, sourceLocale, makeLoader)` | Builds the `localeLoaders` map for `makeLoadLocale` from `ui-languages.json` (every `code` except `sourceLocale`). |
253
320
  | `makeLoadLocale(i18n, loaders, sourceLocale)` | Factory for async locale file loading. |
@@ -267,16 +334,18 @@ The following helpers are exported from `'ai-i18n-tools/runtime'` and work in an
267
334
  ai-i18n-tools version
268
335
  ai-i18n-tools check-models
269
336
  ai-i18n-tools list-models
337
+ ai-i18n-tools bench-models [--model <ids>] [--text <text>|--file <path>] [--source <locale>] [--target <locale>]
270
338
  ai-i18n-tools list-languages [search]
271
- ai-i18n-tools init [-t ui-markdown|ui-docusaurus|ui-starlight|ui-astro-website|ui-json-bundles] [-o path] [--with-translate-ignore]
339
+ 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]
272
340
  ai-i18n-tools write-heading-ids …
341
+ ai-i18n-tools mark-html [paths...] [--write]
273
342
  ai-i18n-tools extract
274
343
  ai-i18n-tools translate-docs …
275
344
  ai-i18n-tools translate-json …
276
345
  ai-i18n-tools translate-svg …
277
346
  ai-i18n-tools translate-ui …
278
347
  ai-i18n-tools sync-ui …
279
- ai-i18n-tools lint-source …
348
+ ai-i18n-tools proofread-ui …
280
349
  ai-i18n-tools check-markdown [-p|--path <path>] [-f|--file <path>] [--json] [--no-cache]
281
350
  ai-i18n-tools export-ui-xliff …
282
351
  ai-i18n-tools sync …
@@ -284,6 +353,7 @@ ai-i18n-tools status …
284
353
  ai-i18n-tools statistics …
285
354
  ai-i18n-tools cleanup …
286
355
  ai-i18n-tools clean-temp …
356
+ ai-i18n-tools purge-locale -l <code> [-l <code> …] [--dry-run] [-y|--yes] [-f|--force] [--keep-files] [--backup <path>]
287
357
  ai-i18n-tools dashboard …
288
358
  ai-i18n-tools generate-ui-languages [--master path] [--dry-run]
289
359
  ai-i18n-tools glossary-generate
@@ -291,24 +361,35 @@ ai-i18n-tools help [command]
291
361
  ```
292
362
 
293
363
 
294
- 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.
364
+ 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).
365
+
366
+ Complete per-command flag lists are in [CLI reference](docs/reference/cli-commands/). Run `ai-i18n-tools <command> --help` for built-in usage text.
295
367
 
296
- Global options on every command: `-c <config>` (default: `ai-i18n-tools.config.json`), `-v` (verbose), `-P` / `--provider <name>` (override the active LLM provider; must be configured under `providers`), 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.
368
+ 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.
369
+
370
+ <a id="tool-ui-language-logs-help-dashboard"></a>
371
+ ### Tool UI language (logs, help, dashboard)
372
+
373
+ 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.
297
374
 
298
375
  ---
299
376
 
300
377
  <a id="documentation"></a>
301
378
  ## Documentation
302
379
 
303
- - [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.
304
- - [Locale assets guide](docs/LOCALE-ASSETS-GUIDE.md) - screenshots and illustrated SVGs in translated docs (Patterns A–E, flat link rewriter, screenshot scripts).
305
- - [Package Overview](docs/PACKAGE_OVERVIEW.md) - architecture, internals, programmatic API, and extension points.
306
- - [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).
307
- - Maintainer internals for **this** repository: `dev/package-context.md` (clone-only; not on npm).
380
+ - [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.
381
+ - [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).
382
+ - [Locale assets guide](docs/guide/images-and-screenshots/) - screenshots and illustrated SVGs in translated docs (flat link rewriter, screenshot scripts).
383
+ - [Architecture](docs/reference/architecture.md) - architecture, internals, programmatic API, and extension points.
384
+ - [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).
385
+ - 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`.
308
386
 
309
387
  ---
310
388
 
311
389
  <a id="license"></a>
312
390
  ## License
313
391
 
314
- MIT © [Waldemar Scudeller Jr.](https://github.com/wsj-br)
392
+ This project is licensed under the MIT License.
393
+ See the [LICENSE](https://github.com/wsj-br/ai-i18n-tools/blob/main/LICENSE) file for details.
394
+
395
+ Copyright &copy; 2026 Waldemar Scudeller Jr.
@@ -1,6 +1,6 @@
1
1
  import type { CldrPluralForm, I18nConfig } from "../core/types.js";
2
2
  import { type BatchTranslationResult, type ChatResponse, type LlmUsageStats, type Segment, type TranslationResult } from "../core/types.js";
3
- import { type DocumentBatchResponseFormat, type DocumentPromptContentType, type LintSourceSlotResult } from "../core/prompt-builder.js";
3
+ import { type DocumentBatchResponseFormat, type DocumentPromptContentType, type ProofreadUISlotResult } from "../core/prompt-builder.js";
4
4
  import type { Logger } from "../utils/logger.js";
5
5
  /** Thrown when every model in the chain fails for {@link LlmClient.translateDocumentBatch}. */
6
6
  export declare class DocumentBatchAllModelsFailedError extends Error {
@@ -11,6 +11,10 @@ export declare class DocumentBatchAllModelsFailedError extends Error {
11
11
  lastError: unknown;
12
12
  /** HTTP response body text when the model returned content but parsing failed. */
13
13
  lastRawAssistantContent?: string;
14
+ /** Tokens spent across billed-but-discarded attempts before every model failed. */
15
+ wastedUsage?: LlmUsageStats;
16
+ /** USD cost spent across billed-but-discarded attempts (undefined when no cost was reported). */
17
+ wastedCost?: number;
14
18
  };
15
19
  constructor(message: string, details: {
16
20
  systemPrompt: string;
@@ -19,6 +23,10 @@ export declare class DocumentBatchAllModelsFailedError extends Error {
19
23
  lastError: unknown;
20
24
  /** HTTP response body text when the model returned content but parsing failed. */
21
25
  lastRawAssistantContent?: string;
26
+ /** Tokens spent across billed-but-discarded attempts before every model failed. */
27
+ wastedUsage?: LlmUsageStats;
28
+ /** USD cost spent across billed-but-discarded attempts (undefined when no cost was reported). */
29
+ wastedCost?: number;
22
30
  });
23
31
  }
24
32
  export interface LlmClientOptions {
@@ -27,7 +35,7 @@ export interface LlmClientOptions {
27
35
  apiKey?: string;
28
36
  /**
29
37
  * When set and non-empty, use this ordered model list instead of resolving from the active provider
30
- * (e.g. UI translation with `ui.preferredModel` prepended to the provider's list).
38
+ * (e.g. locale-aware UI or document translation with `uiModels` / `localeModels` prepended).
31
39
  */
32
40
  translationModels?: string[];
33
41
  /** Append request/response JSON when set. */
@@ -35,6 +43,13 @@ export interface LlmClientOptions {
35
43
  logger?: Logger;
36
44
  httpReferer?: string;
37
45
  xTitle?: string;
46
+ /**
47
+ * Fires once per billed API response (whether the response is later accepted, rejected, or
48
+ * empty) with that call's tokens/cost. Lets callers maintain a live run total that survives
49
+ * interrupts/errors, since the usage is captured the moment the provider responds rather than
50
+ * only when the enclosing batch/file completes. Must be synchronous and must not throw.
51
+ */
52
+ onApiUsage?: (usage: LlmUsageStats, cost: number | undefined) => void;
38
53
  }
39
54
  /** @deprecated Use {@link LlmClientOptions}. */
40
55
  export type OpenRouterClientOptions = LlmClientOptions;
@@ -58,6 +73,7 @@ export declare class LlmClient {
58
73
  private readonly httpReferer;
59
74
  private readonly xTitle;
60
75
  private readonly requestTimeoutMs;
76
+ private readonly onApiUsage?;
61
77
  constructor(opts: LlmClientOptions);
62
78
  private buildProvider;
63
79
  getConfiguredModels(): readonly string[];
@@ -74,17 +90,22 @@ export declare class LlmClient {
74
90
  * Enforce the target locale's ISO 15924 script subtag on a model response, throwing
75
91
  * {@link ScriptValidationError} so the model-fallback loop retries with the next model.
76
92
  *
77
- * For romanized (`*-Latn`) targets any non-Latin letter is rejected (output must be pure Latin).
78
- * For other supported scripts (`Cyrl`, `Arab`, `Deva`, `Mong`, `Han`, …) only letters from a
79
- * *different* non-Latin script are rejected — Latin text (code, URLs, brand names, placeholders)
80
- * is always allowed, so this is free of false positives. See {@link disallowedScriptLetters}.
81
- * Locales without a script subtag, and composite scripts (e.g. `Jpan`, `Kore`), are not enforced.
93
+ * Uses a statistical dominant-script check ({@link scriptValidationIssue}): Latin text
94
+ * (code, URLs, brand names, placeholders) and letter-like symbols such as `ℹ` are ignored,
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.
82
98
  */
83
99
  private assertExpectedScript;
84
100
  private appendDebugLog;
85
101
  /** Match doc-translate log lines: two-space indent, locale, filename. */
86
102
  private warnModelSwitch;
87
103
  private toOpenRouterMessages;
104
+ private static emptyDiscarded;
105
+ /** Add a billed-but-discarded attempt's tokens/cost onto the running discarded total. */
106
+ private static addDiscarded;
107
+ /** Fold discarded-attempt tokens/cost into the eventually-successful response's totals. */
108
+ private static foldDiscarded;
88
109
  /** Read OpenRouter's exact USD cost from `providerMetadata` (other providers: undefined). */
89
110
  private extractCost;
90
111
  /** Single chat-completions call for one model via the active provider (AI SDK transport). */
@@ -132,13 +153,13 @@ export declare class LlmClient {
132
153
  cost?: number;
133
154
  }>;
134
155
  /**
135
- * `lint-source`: review a batch of source-locale UI strings; model returns JSON array of `{ issues: [...] }`.
156
+ * `proofread-ui`: review a batch of source-locale UI strings; model returns JSON array of `{ issues: [...] }`.
136
157
  */
137
- lintUISourceBatch(texts: string[], languageLabel: string, options?: {
158
+ proofreadUISourceBatch(texts: string[], languageLabel: string, options?: {
138
159
  startModelIndex?: number;
139
160
  glossaryHints?: string[];
140
161
  }): Promise<{
141
- slots: LintSourceSlotResult[];
162
+ slots: ProofreadUISlotResult[];
142
163
  model: string;
143
164
  usage: LlmUsageStats;
144
165
  cost?: number;