ai-i18n-tools 1.0.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 (260) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/dist/api/openrouter.d.ts +115 -0
  4. package/dist/api/openrouter.d.ts.map +1 -0
  5. package/dist/api/openrouter.js +399 -0
  6. package/dist/api/openrouter.js.map +1 -0
  7. package/dist/cli/doc-translate.d.ts +90 -0
  8. package/dist/cli/doc-translate.d.ts.map +1 -0
  9. package/dist/cli/doc-translate.js +1153 -0
  10. package/dist/cli/doc-translate.js.map +1 -0
  11. package/dist/cli/export-ui-xliff.d.ts +32 -0
  12. package/dist/cli/export-ui-xliff.d.ts.map +1 -0
  13. package/dist/cli/export-ui-xliff.js +153 -0
  14. package/dist/cli/export-ui-xliff.js.map +1 -0
  15. package/dist/cli/extract-strings.d.ts +12 -0
  16. package/dist/cli/extract-strings.d.ts.map +1 -0
  17. package/dist/cli/extract-strings.js +80 -0
  18. package/dist/cli/extract-strings.js.map +1 -0
  19. package/dist/cli/file-utils.d.ts +10 -0
  20. package/dist/cli/file-utils.d.ts.map +1 -0
  21. package/dist/cli/file-utils.js +77 -0
  22. package/dist/cli/file-utils.js.map +1 -0
  23. package/dist/cli/format.d.ts +21 -0
  24. package/dist/cli/format.d.ts.map +1 -0
  25. package/dist/cli/format.js +75 -0
  26. package/dist/cli/format.js.map +1 -0
  27. package/dist/cli/helpers.d.ts +27 -0
  28. package/dist/cli/helpers.d.ts.map +1 -0
  29. package/dist/cli/helpers.js +84 -0
  30. package/dist/cli/helpers.js.map +1 -0
  31. package/dist/cli/index.d.ts +3 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/cli/index.js +772 -0
  34. package/dist/cli/index.js.map +1 -0
  35. package/dist/cli/log-output.d.ts +13 -0
  36. package/dist/cli/log-output.d.ts.map +1 -0
  37. package/dist/cli/log-output.js +75 -0
  38. package/dist/cli/log-output.js.map +1 -0
  39. package/dist/cli/translate-svg.d.ts +7 -0
  40. package/dist/cli/translate-svg.d.ts.map +1 -0
  41. package/dist/cli/translate-svg.js +167 -0
  42. package/dist/cli/translate-svg.js.map +1 -0
  43. package/dist/cli/translate-ui-strings.d.ts +27 -0
  44. package/dist/cli/translate-ui-strings.d.ts.map +1 -0
  45. package/dist/cli/translate-ui-strings.js +357 -0
  46. package/dist/cli/translate-ui-strings.js.map +1 -0
  47. package/dist/core/cache-tracking-keys.d.ts +12 -0
  48. package/dist/core/cache-tracking-keys.d.ts.map +1 -0
  49. package/dist/core/cache-tracking-keys.js +20 -0
  50. package/dist/core/cache-tracking-keys.js.map +1 -0
  51. package/dist/core/cache.d.ts +153 -0
  52. package/dist/core/cache.d.ts.map +1 -0
  53. package/dist/core/cache.js +546 -0
  54. package/dist/core/cache.js.map +1 -0
  55. package/dist/core/config.d.ts +58 -0
  56. package/dist/core/config.d.ts.map +1 -0
  57. package/dist/core/config.js +392 -0
  58. package/dist/core/config.js.map +1 -0
  59. package/dist/core/doc-file-tracking.d.ts +8 -0
  60. package/dist/core/doc-file-tracking.d.ts.map +1 -0
  61. package/dist/core/doc-file-tracking.js +27 -0
  62. package/dist/core/doc-file-tracking.js.map +1 -0
  63. package/dist/core/errors.d.ts +19 -0
  64. package/dist/core/errors.d.ts.map +1 -0
  65. package/dist/core/errors.js +23 -0
  66. package/dist/core/errors.js.map +1 -0
  67. package/dist/core/locale-utils.d.ts +20 -0
  68. package/dist/core/locale-utils.d.ts.map +1 -0
  69. package/dist/core/locale-utils.js +75 -0
  70. package/dist/core/locale-utils.js.map +1 -0
  71. package/dist/core/output-paths.d.ts +22 -0
  72. package/dist/core/output-paths.d.ts.map +1 -0
  73. package/dist/core/output-paths.js +130 -0
  74. package/dist/core/output-paths.js.map +1 -0
  75. package/dist/core/prompt-builder.d.ts +62 -0
  76. package/dist/core/prompt-builder.d.ts.map +1 -0
  77. package/dist/core/prompt-builder.js +232 -0
  78. package/dist/core/prompt-builder.js.map +1 -0
  79. package/dist/core/prompts.d.ts +27 -0
  80. package/dist/core/prompts.d.ts.map +1 -0
  81. package/dist/core/prompts.js +57 -0
  82. package/dist/core/prompts.js.map +1 -0
  83. package/dist/core/svg-asset-paths.d.ts +40 -0
  84. package/dist/core/svg-asset-paths.d.ts.map +1 -0
  85. package/dist/core/svg-asset-paths.js +107 -0
  86. package/dist/core/svg-asset-paths.js.map +1 -0
  87. package/dist/core/types.d.ts +388 -0
  88. package/dist/core/types.d.ts.map +1 -0
  89. package/dist/core/types.js +265 -0
  90. package/dist/core/types.js.map +1 -0
  91. package/dist/core/ui-languages.d.ts +66 -0
  92. package/dist/core/ui-languages.d.ts.map +1 -0
  93. package/dist/core/ui-languages.js +277 -0
  94. package/dist/core/ui-languages.js.map +1 -0
  95. package/dist/core/user-edited-model.d.ts +3 -0
  96. package/dist/core/user-edited-model.d.ts.map +1 -0
  97. package/dist/core/user-edited-model.js +3 -0
  98. package/dist/core/user-edited-model.js.map +1 -0
  99. package/dist/edit-cache-app/app.js +1326 -0
  100. package/dist/edit-cache-app/index.html +287 -0
  101. package/dist/edit-cache-app/styles.css +664 -0
  102. package/dist/extractors/base-extractor.d.ts +15 -0
  103. package/dist/extractors/base-extractor.d.ts.map +1 -0
  104. package/dist/extractors/base-extractor.js +23 -0
  105. package/dist/extractors/base-extractor.js.map +1 -0
  106. package/dist/extractors/classify-segment.d.ts +6 -0
  107. package/dist/extractors/classify-segment.d.ts.map +1 -0
  108. package/dist/extractors/classify-segment.js +20 -0
  109. package/dist/extractors/classify-segment.js.map +1 -0
  110. package/dist/extractors/json-extractor.d.ts +16 -0
  111. package/dist/extractors/json-extractor.d.ts.map +1 -0
  112. package/dist/extractors/json-extractor.js +128 -0
  113. package/dist/extractors/json-extractor.js.map +1 -0
  114. package/dist/extractors/markdown-extractor.d.ts +15 -0
  115. package/dist/extractors/markdown-extractor.d.ts.map +1 -0
  116. package/dist/extractors/markdown-extractor.js +205 -0
  117. package/dist/extractors/markdown-extractor.js.map +1 -0
  118. package/dist/extractors/svg-extractor.d.ts +19 -0
  119. package/dist/extractors/svg-extractor.d.ts.map +1 -0
  120. package/dist/extractors/svg-extractor.js +132 -0
  121. package/dist/extractors/svg-extractor.js.map +1 -0
  122. package/dist/extractors/ui-string-extractor.d.ts +40 -0
  123. package/dist/extractors/ui-string-extractor.d.ts.map +1 -0
  124. package/dist/extractors/ui-string-extractor.js +146 -0
  125. package/dist/extractors/ui-string-extractor.js.map +1 -0
  126. package/dist/extractors/ui-string-locations.d.ts +23 -0
  127. package/dist/extractors/ui-string-locations.d.ts.map +1 -0
  128. package/dist/extractors/ui-string-locations.js +138 -0
  129. package/dist/extractors/ui-string-locations.js.map +1 -0
  130. package/dist/glossary/glossary.d.ts +34 -0
  131. package/dist/glossary/glossary.d.ts.map +1 -0
  132. package/dist/glossary/glossary.js +260 -0
  133. package/dist/glossary/glossary.js.map +1 -0
  134. package/dist/glossary/matcher.d.ts +10 -0
  135. package/dist/glossary/matcher.d.ts.map +1 -0
  136. package/dist/glossary/matcher.js +12 -0
  137. package/dist/glossary/matcher.js.map +1 -0
  138. package/dist/index.d.ts +44 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +44 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/processors/admonition-placeholders.d.ts +8 -0
  143. package/dist/processors/admonition-placeholders.d.ts.map +1 -0
  144. package/dist/processors/admonition-placeholders.js +59 -0
  145. package/dist/processors/admonition-placeholders.js.map +1 -0
  146. package/dist/processors/anchor-placeholders.d.ts +8 -0
  147. package/dist/processors/anchor-placeholders.d.ts.map +1 -0
  148. package/dist/processors/anchor-placeholders.js +37 -0
  149. package/dist/processors/anchor-placeholders.js.map +1 -0
  150. package/dist/processors/batch-processor.d.ts +10 -0
  151. package/dist/processors/batch-processor.d.ts.map +1 -0
  152. package/dist/processors/batch-processor.js +33 -0
  153. package/dist/processors/batch-processor.js.map +1 -0
  154. package/dist/processors/bold-code-placeholders.d.ts +14 -0
  155. package/dist/processors/bold-code-placeholders.d.ts.map +1 -0
  156. package/dist/processors/bold-code-placeholders.js +116 -0
  157. package/dist/processors/bold-code-placeholders.js.map +1 -0
  158. package/dist/processors/doc-postprocess.d.ts +51 -0
  159. package/dist/processors/doc-postprocess.d.ts.map +1 -0
  160. package/dist/processors/doc-postprocess.js +215 -0
  161. package/dist/processors/doc-postprocess.js.map +1 -0
  162. package/dist/processors/emphasis-placeholders.d.ts +6 -0
  163. package/dist/processors/emphasis-placeholders.d.ts.map +1 -0
  164. package/dist/processors/emphasis-placeholders.js +262 -0
  165. package/dist/processors/emphasis-placeholders.js.map +1 -0
  166. package/dist/processors/flat-link-rewrite.d.ts +32 -0
  167. package/dist/processors/flat-link-rewrite.d.ts.map +1 -0
  168. package/dist/processors/flat-link-rewrite.js +90 -0
  169. package/dist/processors/flat-link-rewrite.js.map +1 -0
  170. package/dist/processors/glossary-force-placeholders.d.ts +12 -0
  171. package/dist/processors/glossary-force-placeholders.d.ts.map +1 -0
  172. package/dist/processors/glossary-force-placeholders.js +58 -0
  173. package/dist/processors/glossary-force-placeholders.js.map +1 -0
  174. package/dist/processors/inline-code-placeholders.d.ts +11 -0
  175. package/dist/processors/inline-code-placeholders.d.ts.map +1 -0
  176. package/dist/processors/inline-code-placeholders.js +87 -0
  177. package/dist/processors/inline-code-placeholders.js.map +1 -0
  178. package/dist/processors/placeholder-handler.d.ts +38 -0
  179. package/dist/processors/placeholder-handler.d.ts.map +1 -0
  180. package/dist/processors/placeholder-handler.js +55 -0
  181. package/dist/processors/placeholder-handler.js.map +1 -0
  182. package/dist/processors/translation-placeholder-leaks.d.ts +2 -0
  183. package/dist/processors/translation-placeholder-leaks.d.ts.map +1 -0
  184. package/dist/processors/translation-placeholder-leaks.js +9 -0
  185. package/dist/processors/translation-placeholder-leaks.js.map +1 -0
  186. package/dist/processors/url-placeholders.d.ts +10 -0
  187. package/dist/processors/url-placeholders.d.ts.map +1 -0
  188. package/dist/processors/url-placeholders.js +29 -0
  189. package/dist/processors/url-placeholders.js.map +1 -0
  190. package/dist/processors/validator.d.ts +23 -0
  191. package/dist/processors/validator.d.ts.map +1 -0
  192. package/dist/processors/validator.js +186 -0
  193. package/dist/processors/validator.js.map +1 -0
  194. package/dist/runtime/i18next-helpers.d.ts +146 -0
  195. package/dist/runtime/i18next-helpers.d.ts.map +1 -0
  196. package/dist/runtime/i18next-helpers.js +192 -0
  197. package/dist/runtime/i18next-helpers.js.map +1 -0
  198. package/dist/runtime/index.d.ts +4 -0
  199. package/dist/runtime/index.d.ts.map +1 -0
  200. package/dist/runtime/index.js +4 -0
  201. package/dist/runtime/index.js.map +1 -0
  202. package/dist/runtime/template.d.ts +21 -0
  203. package/dist/runtime/template.d.ts.map +1 -0
  204. package/dist/runtime/template.js +28 -0
  205. package/dist/runtime/template.js.map +1 -0
  206. package/dist/runtime/ui-language-display.d.ts +16 -0
  207. package/dist/runtime/ui-language-display.d.ts.map +1 -0
  208. package/dist/runtime/ui-language-display.js +26 -0
  209. package/dist/runtime/ui-language-display.js.map +1 -0
  210. package/dist/server/translation-editor.d.ts +25 -0
  211. package/dist/server/translation-editor.d.ts.map +1 -0
  212. package/dist/server/translation-editor.js +583 -0
  213. package/dist/server/translation-editor.js.map +1 -0
  214. package/dist/utils/concurrency.d.ts +31 -0
  215. package/dist/utils/concurrency.d.ts.map +1 -0
  216. package/dist/utils/concurrency.js +103 -0
  217. package/dist/utils/concurrency.js.map +1 -0
  218. package/dist/utils/hash.d.ts +5 -0
  219. package/dist/utils/hash.d.ts.map +1 -0
  220. package/dist/utils/hash.js +9 -0
  221. package/dist/utils/hash.js.map +1 -0
  222. package/dist/utils/ignore-parser.d.ts +7 -0
  223. package/dist/utils/ignore-parser.d.ts.map +1 -0
  224. package/dist/utils/ignore-parser.js +26 -0
  225. package/dist/utils/ignore-parser.js.map +1 -0
  226. package/dist/utils/logger.d.ts +45 -0
  227. package/dist/utils/logger.d.ts.map +1 -0
  228. package/dist/utils/logger.js +158 -0
  229. package/dist/utils/logger.js.map +1 -0
  230. package/docs/GETTING_STARTED.md +697 -0
  231. package/docs/PACKAGE_OVERVIEW.md +427 -0
  232. package/docs/ai-i18n-tools-context.md +481 -0
  233. package/package.json +117 -0
  234. package/translated-docs/README.de.md +157 -0
  235. package/translated-docs/README.es.md +157 -0
  236. package/translated-docs/README.fr.md +157 -0
  237. package/translated-docs/README.hi.md +157 -0
  238. package/translated-docs/README.ja.md +157 -0
  239. package/translated-docs/README.ko.md +157 -0
  240. package/translated-docs/README.pt-BR.md +157 -0
  241. package/translated-docs/README.zh-CN.md +157 -0
  242. package/translated-docs/README.zh-TW.md +157 -0
  243. package/translated-docs/docs/GETTING_STARTED.de.md +682 -0
  244. package/translated-docs/docs/GETTING_STARTED.es.md +682 -0
  245. package/translated-docs/docs/GETTING_STARTED.fr.md +682 -0
  246. package/translated-docs/docs/GETTING_STARTED.hi.md +682 -0
  247. package/translated-docs/docs/GETTING_STARTED.ja.md +682 -0
  248. package/translated-docs/docs/GETTING_STARTED.ko.md +682 -0
  249. package/translated-docs/docs/GETTING_STARTED.pt-BR.md +682 -0
  250. package/translated-docs/docs/GETTING_STARTED.zh-CN.md +682 -0
  251. package/translated-docs/docs/GETTING_STARTED.zh-TW.md +682 -0
  252. package/translated-docs/docs/PACKAGE_OVERVIEW.de.md +428 -0
  253. package/translated-docs/docs/PACKAGE_OVERVIEW.es.md +428 -0
  254. package/translated-docs/docs/PACKAGE_OVERVIEW.fr.md +428 -0
  255. package/translated-docs/docs/PACKAGE_OVERVIEW.hi.md +428 -0
  256. package/translated-docs/docs/PACKAGE_OVERVIEW.ja.md +428 -0
  257. package/translated-docs/docs/PACKAGE_OVERVIEW.ko.md +428 -0
  258. package/translated-docs/docs/PACKAGE_OVERVIEW.pt-BR.md +428 -0
  259. package/translated-docs/docs/PACKAGE_OVERVIEW.zh-CN.md +428 -0
  260. package/translated-docs/docs/PACKAGE_OVERVIEW.zh-TW.md +428 -0
@@ -0,0 +1,427 @@
1
+ # ai-i18n-tools: Package Overview
2
+
3
+ This document describes the internal architecture of `ai-i18n-tools`, how each component fits together, and how the two core workflows are implemented.
4
+
5
+ For practical usage instructions, see [GETTING_STARTED.md](./GETTING_STARTED.md).
6
+
7
+ <small>**Read in other languages:** </small>
8
+ <small id="lang-list">[en-GB](./PACKAGE_OVERVIEW.md) · [de](../translated-docs/docs/PACKAGE_OVERVIEW.de.md) · [es](../translated-docs/docs/PACKAGE_OVERVIEW.es.md) · [fr](../translated-docs/docs/PACKAGE_OVERVIEW.fr.md) · [hi](../translated-docs/docs/PACKAGE_OVERVIEW.hi.md) · [ja](../translated-docs/docs/PACKAGE_OVERVIEW.ja.md) · [ko](../translated-docs/docs/PACKAGE_OVERVIEW.ko.md) · [pt-BR](../translated-docs/docs/PACKAGE_OVERVIEW.pt-BR.md) · [zh-CN](../translated-docs/docs/PACKAGE_OVERVIEW.zh-CN.md) · [zh-TW](../translated-docs/docs/PACKAGE_OVERVIEW.zh-TW.md)</small>
9
+
10
+ ---
11
+
12
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
13
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
14
+ **Table of Contents**
15
+
16
+ - [Architecture overview](#architecture-overview)
17
+ - [Source tree](#source-tree)
18
+ - [Workflow 1 - UI Translation internals](#workflow-1---ui-translation-internals)
19
+ - [`UIStringExtractor`](#uistringextractor)
20
+ - [`strings.json`](#stringsjson)
21
+ - [Flat locale files](#flat-locale-files)
22
+ - [UI Translation prompts](#ui-translation-prompts)
23
+ - [Workflow 2 - Document Translation internals](#workflow-2---document-translation-internals)
24
+ - [Extractors](#extractors)
25
+ - [Placeholder protection](#placeholder-protection)
26
+ - [Cache (`TranslationCache`)](#cache-translationcache)
27
+ - [Output path resolution](#output-path-resolution)
28
+ - [Flat link rewriting](#flat-link-rewriting)
29
+ - [Shared infrastructure](#shared-infrastructure)
30
+ - [`OpenRouterClient`](#openrouterclient)
31
+ - [Config loading](#config-loading)
32
+ - [Logger](#logger)
33
+ - [Runtime helpers API](#runtime-helpers-api)
34
+ - [RTL helpers](#rtl-helpers)
35
+ - [i18next setup factories](#i18next-setup-factories)
36
+ - [Display helpers](#display-helpers)
37
+ - [String helpers](#string-helpers)
38
+ - [Programmatic API](#programmatic-api)
39
+ - [Extension points](#extension-points)
40
+ - [Custom function names (UI extraction)](#custom-function-names-ui-extraction)
41
+ - [Custom extractors](#custom-extractors)
42
+ - [Custom output paths](#custom-output-paths)
43
+
44
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
45
+
46
+ ---
47
+
48
+ ## Architecture overview
49
+
50
+ ```
51
+ ai-i18n-tools
52
+ ├── CLI (src/cli/) - commands: init, extract, translate-docs, translate-svg, translate-ui, sync, status, …
53
+ ├── Core (src/core/) - config, types, cache, prompts, output paths, UI languages
54
+ ├── Extractors (src/extractors/) - segment extraction from JS/TS, markdown, JSON, SVG
55
+ ├── Processors (src/processors/) - placeholders, batching, validation, link rewriting
56
+ ├── API (src/api/) - OpenRouter HTTP client
57
+ ├── Glossary (src/glossary/) - glossary loading and term matching
58
+ ├── Runtime (src/runtime/) - i18next helpers, display helpers (no i18next import)
59
+ ├── Server (src/server/) - local Express web editor for cache / glossary
60
+ └── Utils (src/utils/) - logger, hash, ignore parser
61
+ ```
62
+
63
+ Everything that consumers may need programmatically is re-exported from `src/index.ts`.
64
+
65
+ ---
66
+
67
+ ## Source tree
68
+
69
+ ```
70
+ src/
71
+ ├── index.ts Public API re-exports
72
+ │
73
+ ├── cli/
74
+ │ ├── index.ts CLI entry point (commander)
75
+ │ ├── extract-strings.ts `extract` command implementation
76
+ │ ├── translate-ui-strings.ts `translate-ui` command implementation
77
+ │ ├── doc-translate.ts `translate-docs` command (documentation files only)
78
+ │ ├── translate-svg.ts `translate-svg` command (standalone assets from `config.svg`)
79
+ │ ├── helpers.ts Shared CLI utilities
80
+ │ └── file-utils.ts File collection helpers
81
+ │
82
+ ├── core/
83
+ │ ├── types.ts Zod schemas + TypeScript types for all config shapes
84
+ │ ├── config.ts Config loading, merging, validation, init templates
85
+ │ ├── cache.ts SQLite translation cache (node:sqlite)
86
+ │ ├── prompt-builder.ts LLM prompt construction for docs and UI strings
87
+ │ ├── output-paths.ts Docusaurus / flat output path resolution
88
+ │ ├── ui-languages.ts ui-languages.json loading and locale resolution
89
+ │ ├── locale-utils.ts BCP-47 normalization and locale list parsing
90
+ │ └── errors.ts Typed error classes
91
+ │
92
+ ├── extractors/
93
+ │ ├── base-extractor.ts Abstract base class for all extractors
94
+ │ ├── ui-string-extractor.ts JS/TS source scanner (i18next-scanner)
95
+ │ ├── classify-segment.ts Heuristic segment type classification
96
+ │ ├── markdown-extractor.ts Markdown / MDX segment extraction
97
+ │ ├── json-extractor.ts JSON label file extraction
98
+ │ └── svg-extractor.ts SVG text extraction
99
+ │
100
+ ├── processors/
101
+ │ ├── placeholder-handler.ts Chain: admonitions → anchors → URLs
102
+ │ ├── url-placeholders.ts Markdown URL protection/restore
103
+ │ ├── admonition-placeholders.ts Docusaurus admonition protection/restore
104
+ │ ├── anchor-placeholders.ts HTML anchor / heading ID protection/restore
105
+ │ ├── batch-processor.ts Segment → batch grouping (count + char limits)
106
+ │ ├── validator.ts Post-translation structural checks
107
+ │ └── flat-link-rewrite.ts Relative link rewriting for flat output
108
+ │
109
+ ├── api/
110
+ │ └── openrouter.ts OpenRouter HTTP client with model fallback chain
111
+ │
112
+ ├── glossary/
113
+ │ ├── glossary.ts Glossary loading (CSV + auto-build from strings.json)
114
+ │ └── matcher.ts Term hint extraction for prompts
115
+ │
116
+ ├── runtime/
117
+ │ ├── index.ts Runtime re-exports
118
+ │ ├── template.ts interpolateTemplate, flipUiArrowsForRtl
119
+ │ ├── ui-language-display.ts getUILanguageLabel, getUILanguageLabelNative
120
+ │ └── i18next-helpers.ts RTL detection, i18next setup factories
121
+ │
122
+ ├── server/
123
+ │ └── translation-editor.ts Express app for cache / strings.json / glossary editor
124
+ │
125
+ └── utils/
126
+ ├── logger.ts Leveled logger with ANSI support
127
+ ├── hash.ts Segment hash (SHA-256 first 16 hex)
128
+ └── ignore-parser.ts .translate-ignore file parser
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Workflow 1 - UI Translation internals
134
+
135
+ ```
136
+ source files (JS/TS)
137
+ │
138
+ ▼ UIStringExtractor (i18next-scanner Parser)
139
+ strings.json ─────────────────── master catalog
140
+ │ { hash: { source, translated, models?, locations? } }
141
+ ▼
142
+ OpenRouterClient.translateUIBatch()
143
+ │ sends JSON array of source strings, receives JSON array of translations (+ model id per batch)
144
+ ▼
145
+ de.json, pt-BR.json … ─────────── per-locale flat maps: source → translation (no model metadata)
146
+ ```
147
+
148
+ ### `UIStringExtractor`
149
+
150
+ Uses `i18next-scanner`'s `Parser.parseFuncFromString` to find `t("literal")` and `i18n.t("literal")` calls in any JS/TS file. Function names and file extensions are configurable, and extraction can also include the project `package.json` `description` when `reactExtractor.includePackageDescription` is enabled. Segment hashes are **MD5 first 8 hex chars** of the trimmed source string - these become the keys in `strings.json`.
151
+
152
+ ### `strings.json`
153
+
154
+ The master catalog has the shape:
155
+
156
+ ```json
157
+ {
158
+ "<md5-8>": {
159
+ "source": "The English string",
160
+ "translated": {
161
+ "de": "Der deutsche Text",
162
+ "pt-BR": "O texto em português"
163
+ },
164
+ "models": {
165
+ "de": "anthropic/claude-3.5-haiku",
166
+ "pt-BR": "openai/gpt-4o"
167
+ },
168
+ "locations": [{ "file": "src/app/page.tsx", "line": 51 }]
169
+ }
170
+ }
171
+ ```
172
+
173
+ `models` (optional) — per locale, which model produced that translation after the last successful `translate-ui` run for that locale (or `user-edited` if the text was saved from the `editor` web UI). `locations` (optional) — where `extract` found the string.
174
+
175
+ `extract` adds new keys and preserves existing `translated` / `models` data for keys still present in the scan. `translate-ui` fills missing `translated` entries, updates `models` for locales it translates, and writes flat locale files.
176
+
177
+ ### Flat locale files
178
+
179
+ Each target locale gets a flat JSON file (`de.json`) mapping source string → translation (no `models` field):
180
+
181
+ ```json
182
+ {
183
+ "The English string": "Der deutsche Text",
184
+ "Save": "Speichern"
185
+ }
186
+ ```
187
+
188
+ i18next loads these as resource bundles and looks up translations by the source string (key-as-default model).
189
+
190
+ ### UI Translation prompts
191
+
192
+ `buildUIPromptMessages` constructs system + user messages that:
193
+ - Identify the source and target languages (by display name from `localeDisplayNames` or `ui-languages.json`).
194
+ - Send a JSON array of strings and request a JSON array of translations in return.
195
+ - Include glossary hints when available.
196
+
197
+ `OpenRouterClient.translateUIBatch` tries each model in order, falling back on parse or network errors. The CLI builds that list from `openrouter.translationModels` (or legacy default/fallback); for `translate-ui`, optional `ui.preferredModel` is prepended when set (deduplicated against the rest).
198
+
199
+ ---
200
+
201
+ ## Workflow 2 - Document Translation internals
202
+
203
+ ```
204
+ markdown/MDX/JSON files (`translate-docs`)
205
+ │
206
+ ▼ MarkdownExtractor / JsonExtractor
207
+ segments[] ─────────────────── typed segments with hash + content
208
+ │
209
+ ▼ PlaceholderHandler
210
+ protected text ──────────────── URLs, admonitions, anchors replaced with tokens
211
+ │
212
+ ▼ splitTranslatableIntoBatches
213
+ batches[] ───────────────────── grouped by count + char limit
214
+ │
215
+ ▼ TranslationCache lookup
216
+ cache hit → skip, miss → OpenRouterClient.translateDocumentBatch
217
+ │
218
+ ▼ PlaceholderHandler.restoreAfterTranslation
219
+ final text ──────────────────── placeholders restored
220
+ │
221
+ ▼ resolveDocumentationOutputPath
222
+ output file ─────────────────── Docusaurus layout or flat layout
223
+ ```
224
+
225
+ ### Extractors
226
+
227
+ All extractors extend `BaseExtractor` and implement `extract(content, filepath): Segment[]`.
228
+
229
+ - `MarkdownExtractor` - splits markdown into typed segments: `frontmatter`, `heading`, `paragraph`, `code`, `admonition`. Non-translatable segments (code blocks, raw HTML) are preserved verbatim.
230
+ - `JsonExtractor` - extracts string values from Docusaurus JSON label files.
231
+ - `SvgExtractor` - extracts `<text>`, `<title>`, and `<desc>` content from SVG (used by `translate-svg` for assets under `config.svg`, not by `translate-docs`).
232
+
233
+ ### Placeholder protection
234
+
235
+ Before translation, sensitive syntax is replaced with opaque tokens to prevent LLM corruption:
236
+
237
+ 1. **Admonition markers** (`:::note`, `:::`) - restored with exact original text.
238
+ 2. **Doc anchors** (HTML `<a id="…">`, Docusaurus heading `{#…}`) - preserved verbatim.
239
+ 3. **Markdown URLs** (`](url)`, `src="…"`) - restored from a map after translation.
240
+
241
+ ### Cache (`TranslationCache`)
242
+
243
+ SQLite database (via `node:sqlite`) stores rows keyed by `(source_hash, locale)` with `translated_text`, `model`, `filepath`, `last_hit_at`, and related fields. The hash is SHA-256 first 16 hex chars of normalized content (whitespace collapsed).
244
+
245
+ On each run, segments are looked up by hash × locale. Only cache misses go to the LLM. After translation, `last_hit_at` is reset for segment rows in the current translate scope that were not hit. `cleanup` runs `sync --force-update` first, then removes stale segment rows (null `last_hit_at` / empty filepath), prunes `file_tracking` keys when the resolved source path is missing on disk (`doc-block:…`, `svg-assets:…`, etc.), and removes translation rows whose metadata filepath points at a missing file; it backs up `cache.db` first unless `--no-backup` is passed.
246
+
247
+ The `translate-docs` command also uses **file tracking** so unchanged sources with existing outputs can skip work entirely. `--force-update` re-runs file processing while still using segment cache; `--force` clears file tracking and bypasses segment cache reads for API translation. See [Getting Started](./GETTING_STARTED.md#cache-behaviour-and-translate-docs-flags) for the full flag table.
248
+
249
+ **Batch prompt format:** `translate-docs --prompt-format` selects XML (`<seg>` / `<t>`) or JSON array/object shapes for `OpenRouterClient.translateDocumentBatch` only; extraction, placeholders, and validation are unchanged. See [Batch prompt format](./GETTING_STARTED.md#batch-prompt-format).
250
+
251
+ ### Output path resolution
252
+
253
+ `resolveDocumentationOutputPath(config, cwd, locale, relPath, kind)` maps a source-relative path to the output path:
254
+
255
+ - `nested` style (default): `{outputDir}/{locale}/{relPath}` for markdown.
256
+ - `docusaurus` style: under `docsRoot`, outputs use `{outputDir}/{locale}/docusaurus-plugin-content-docs/current/{relativeToDocsRoot}`; paths outside `docsRoot` fall back to the nested layout.
257
+ - `flat` style: `{outputDir}/{stem}.{locale}{extension}`. When `flatPreserveRelativeDir` is `true`, source subdirectories are kept under `outputDir`.
258
+ - **Custom** `pathTemplate`: any markdown layout using `{outputDir}`, `{locale}`, `{LOCALE}`, `{relPath}`, `{stem}`, `{basename}`, `{extension}`, `{docsRoot}`, `{relativeToDocsRoot}`.
259
+ - **Custom** `jsonPathTemplate`: separate custom layout for JSON label files, using the same placeholders.
260
+ - `linkRewriteDocsRoot` helps the flat-link rewriter compute correct prefixes when translated output is rooted somewhere other than the default project root.
261
+
262
+ ### Flat link rewriting
263
+
264
+ When `markdownOutput.style === "flat"`, translated markdown files are placed alongside the source with locale suffixes. Relative links between pages are rewritten so that `[Guide](./guide.md)` in `readme.de.md` points to `guide.de.md`. Controlled by `rewriteRelativeLinks` (auto-enabled for flat style without a custom `pathTemplate`).
265
+
266
+ ---
267
+
268
+ ## Shared infrastructure
269
+
270
+ ### `OpenRouterClient`
271
+
272
+ Wraps the OpenRouter chat completions API. Key behaviours:
273
+
274
+ - **Model fallback**: tries each model in the resolved list in order; falls back on HTTP errors or parse failures. UI translation resolves `ui.preferredModel` first when present, then `openrouter` models.
275
+ - **Rate limiting**: detects 429 responses, waits `retry-after` (or 2s), retries once.
276
+ - **Prompt caching**: system message is sent with `cache_control: { type: "ephemeral" }` to enable prompt caching on supported models.
277
+ - **Debug traffic log**: if `debugTrafficFilePath` is set, appends request and response JSON to a file.
278
+
279
+ ### Config loading
280
+
281
+ `loadI18nConfigFromFile(configPath, cwd)` pipeline:
282
+
283
+ 1. Read and parse `ai-i18n-tools.config.json` (JSON).
284
+ 2. `mergeWithDefaults` - deep-merge with `defaultI18nConfigPartial`, and merge any `documentations[].sourceFiles` entries into `contentPaths`.
285
+ 3. `expandTargetLocalesFileReferenceInRawInput` - if `targetLocales` is a file path, load the manifest and expand to locale codes; set `uiLanguagesPath`.
286
+ 4. `expandDocumentationTargetLocalesInRawInput` - same for each `documentations[].targetLocales` entry.
287
+ 5. `parseI18nConfig` - Zod validation + `validateI18nBusinessRules`.
288
+ 6. `applyEnvOverrides` - apply `OPENROUTER_API_KEY`, `I18N_SOURCE_LOCALE`, etc.
289
+ 7. `augmentConfigWithUiLanguagesFile` - attach manifest display names.
290
+
291
+ ### Logger
292
+
293
+ `Logger` supports `debug`, `info`, `warn`, `error` levels with ANSI colour output. Verbose mode (`-v`) enables `debug`. When `logFilePath` is set, log lines are also written to that file.
294
+
295
+ ---
296
+
297
+ ## Runtime helpers API
298
+
299
+ These are exported from `'ai-i18n-tools/runtime'` and work in any JavaScript environment (browser, Node.js, Deno, Edge). They do **not** import from `i18next` or `react-i18next`.
300
+
301
+ ### RTL helpers
302
+
303
+ ```ts
304
+ RTL_LANGS: ReadonlySet<string>
305
+ getTextDirection(lng: string): 'ltr' | 'rtl'
306
+ applyDirection(lng: string, element?: Element): void
307
+ ```
308
+
309
+ ### i18next setup factories
310
+
311
+ ```ts
312
+ defaultI18nInitOptions(sourceLocale?: string): i18nextInitOptions
313
+ wrapI18nWithKeyTrim(i18n: I18nLike): void
314
+ makeLoadLocale(
315
+ i18n: I18nWithResources,
316
+ localeLoaders: Record<string, () => Promise<unknown>>,
317
+ sourceLocale?: string
318
+ ): (lang: string) => Promise<void>
319
+ ```
320
+
321
+ ### Display helpers
322
+
323
+ ```ts
324
+ getUILanguageLabel(lang: UiLanguageEntry, t: TranslateFn): string
325
+ getUILanguageLabelNative(lang: UiLanguageEntry): string
326
+ ```
327
+
328
+ ### String helpers
329
+
330
+ ```ts
331
+ interpolateTemplate(str: string, vars: Record<string, string | number | boolean>): string
332
+ flipUiArrowsForRtl(text: string | null | undefined, isRtl: boolean): string | null | undefined
333
+ ```
334
+
335
+ ---
336
+
337
+ ## Programmatic API
338
+
339
+ All public types and classes are exported from the package root. Example: running the translate-UI step from Node.js without the CLI:
340
+
341
+ ```ts
342
+ import { loadI18nConfigFromFile, runTranslateUI } from 'ai-i18n-tools';
343
+
344
+ // Config must have features.translateUIStrings: true (and valid targetLocales, etc.).
345
+ const config = loadI18nConfigFromFile('ai-i18n-tools.config.json');
346
+
347
+ const summary = await runTranslateUI(config, {
348
+ cwd: process.cwd(),
349
+ locales: config.targetLocales,
350
+ force: false,
351
+ dryRun: false,
352
+ verbose: false,
353
+ });
354
+ console.log(
355
+ `Updated ${summary.stringsUpdated} string(s); locales touched: ${summary.localesTouched.join(', ')}`
356
+ );
357
+ ```
358
+
359
+ Key exports:
360
+
361
+ | Export | Description |
362
+ |---|---|
363
+ | `loadI18nConfigFromFile` | Load, merge, validate config from a JSON file. |
364
+ | `parseI18nConfig` | Validate a raw config object. |
365
+ | `TranslationCache` | SQLite cache - instantiate with a `cacheDir` path. |
366
+ | `UIStringExtractor` | Extract `t("…")` strings from JS/TS source. |
367
+ | `MarkdownExtractor` | Extract translatable segments from markdown. |
368
+ | `JsonExtractor` | Extract from Docusaurus JSON label files. |
369
+ | `SvgExtractor` | Extract from SVG files. |
370
+ | `OpenRouterClient` | Make translation requests to OpenRouter. |
371
+ | `PlaceholderHandler` | Protect/restore markdown syntax around translation. |
372
+ | `splitTranslatableIntoBatches` | Group segments into LLM-sized batches. |
373
+ | `validateTranslation` | Structural checks after translation. |
374
+ | `resolveDocumentationOutputPath` | Resolve output file path for a translated document. |
375
+ | `Glossary` / `GlossaryMatcher` | Load and apply translation glossaries. |
376
+ | `runTranslateUI` | Programmatic translate-UI entry point. |
377
+
378
+ ---
379
+
380
+ ## Extension points
381
+
382
+ ### Custom function names (UI extraction)
383
+
384
+ Add non-standard translation function names via config:
385
+
386
+ ```json
387
+ {
388
+ "ui": {
389
+ "reactExtractor": {
390
+ "funcNames": ["t", "i18n.t", "translate", "i18n.translate"]
391
+ }
392
+ }
393
+ }
394
+ ```
395
+
396
+ ### Custom extractors
397
+
398
+ Implement `ContentExtractor` from the package:
399
+
400
+ ```ts
401
+ import { BaseExtractor, type Segment } from 'ai-i18n-tools';
402
+
403
+ class MyExtractor extends BaseExtractor {
404
+ readonly name = 'my-format';
405
+ canHandle(filepath: string) { return filepath.endsWith('.myext'); }
406
+ extract(content: string): Segment[] { /* … */ }
407
+ reassemble(segments: Segment[], translations: Map<string, string>): string { /* … */ }
408
+ }
409
+ ```
410
+
411
+ Pass it to the doc-translate pipeline by importing `doc-translate.ts` utilities programmatically.
412
+
413
+ ### Custom output paths
414
+
415
+ Use `markdownOutput.pathTemplate` for any file layout:
416
+
417
+ ```json
418
+ {
419
+ "documentations": [
420
+ {
421
+ "markdownOutput": {
422
+ "pathTemplate": "{outputDir}/{locale}/{relativeToDocsRoot}"
423
+ }
424
+ }
425
+ ]
426
+ }
427
+ ```