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,682 @@
1
+ # ai-i18n-tools: 入门
2
+
3
+ `ai-i18n-tools` 提供两个独立的、可组合的工作流程:
4
+
5
+ - **工作流程 1 - UI 翻译**:从任意 JS/TS 源码中提取 `t("…")` 调用,通过 OpenRouter 进行翻译,并生成适用于 i18next 的扁平化按语言环境划分的 JSON 文件。
6
+ - **工作流程 2 - 文档翻译**:将 Markdown(MDX)和 Docusaurus JSON 标签文件翻译为任意数量的语言环境,并支持智能缓存。**SVG** 资源使用 `features.translateSVG`、顶层 `svg` 块以及 `translate-svg` 命令(参见 [CLI 参考](#cli-reference))。
7
+
8
+ 两个工作流程都使用 OpenRouter(任何兼容的 LLM)并共享一个配置文件。
9
+
10
+ <small>**以其他语言阅读:**</small>
11
+
12
+ <small id="lang-list">[en-GB](../../docs/GETTING_STARTED.md) · [de](./GETTING_STARTED.de.md) · [es](./GETTING_STARTED.es.md) · [fr](./GETTING_STARTED.fr.md) · [hi](./GETTING_STARTED.hi.md) · [ja](./GETTING_STARTED.ja.md) · [ko](./GETTING_STARTED.ko.md) · [pt-BR](./GETTING_STARTED.pt-BR.md) · [zh-CN](./GETTING_STARTED.zh-CN.md) · [zh-TW](./GETTING_STARTED.zh-TW.md)</small>
13
+
14
+ ---
15
+
16
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
17
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
18
+ **目录**
19
+
20
+ - [安装](#installation)
21
+ - [快速开始](#quick-start)
22
+ - [工作流 1 - UI 翻译](#workflow-1---ui-translation)
23
+ - [步骤 1:初始化](#step-1-initialise)
24
+ - [步骤 2:提取字符串](#step-2-extract-strings)
25
+ - [步骤 3:翻译 UI 字符串](#step-3-translate-ui-strings)
26
+ - [导出为 XLIFF 2.0(可选)](#exporting-to-xliff-20-optional)
27
+ - [步骤 4:在运行时接入 i18next](#step-4-wire-i18next-at-runtime)
28
+ - [在源代码中使用 `t()`](#using-t-in-source-code)
29
+ - [插值](#interpolation)
30
+ - [语言切换器 UI](#language-switcher-ui)
31
+ - [RTL 语言](#rtl-languages)
32
+ - [工作流 2 - 文档翻译](#workflow-2---document-translation)
33
+ - [步骤 1:初始化](#step-1-initialise-1)
34
+ - [步骤 2:翻译文档](#step-2-translate-documents)
35
+ - [缓存行为和 `translate-docs` 标志](#cache-behaviour-and-translate-docs-flags)
36
+ - [输出布局](#output-layouts)
37
+ - [组合工作流(UI + 文档)](#combined-workflow-ui--docs)
38
+ - [配置参考](#configuration-reference)
39
+ - [`sourceLocale`](#sourcelocale)
40
+ - [`targetLocales`](#targetlocales)
41
+ - [`uiLanguagesPath`(可选)](#uilanguagespath-optional)
42
+ - [`concurrency`(可选)](#concurrency-optional)
43
+ - [`batchConcurrency`(可选)](#batchconcurrency-optional)
44
+ - [`batchSize` / `maxBatchChars`(可选)](#batchsize--maxbatchchars-optional)
45
+ - [`openrouter`](#openrouter)
46
+ - [`features`](#features)
47
+ - [`ui`](#ui)
48
+ - [`cacheDir`](#cachedir)
49
+ - [`documentations`](#documentations)
50
+ - [`svg`(可选)](#svg-optional)
51
+ - [`glossary`](#glossary)
52
+ - [CLI 参考](#cli-reference)
53
+ - [环境变量](#environment-variables)
54
+
55
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
56
+
57
+ ## 安装
58
+
59
+ 发布的包是 **仅限 ESM**。在 Node.js 或您的打包工具中使用 `import`/`import()`;**不要使用 `require('ai-i18n-tools')`。**
60
+
61
+ ```bash
62
+ npm install ai-i18n-tools
63
+ # or
64
+ pnpm add ai-i18n-tools
65
+ # or
66
+ yarn add ai-i18n-tools
67
+ ```
68
+
69
+ 设置您的 OpenRouter API 密钥:
70
+
71
+ ```bash
72
+ export OPENROUTER_API_KEY=sk-or-v1-your-key-here
73
+ ```
74
+
75
+ 或者在项目根目录创建一个 `.env` 文件:
76
+
77
+ ```env
78
+ OPENROUTER_API_KEY=sk-or-v1-your-key-here
79
+ ```
80
+
81
+ ---
82
+
83
+ ## 快速开始
84
+
85
+ 默认的 `init` 模板(`ui-markdown`)仅启用 **UI** 提取和翻译。`ui-docusaurus` 模板启用 **文档** 翻译(`translate-docs`)。当您想要一个命令来运行提取、UI 翻译、可选的独立 SVG 翻译和根据您的配置进行文档翻译时,请使用 `sync`。
86
+
87
+ ```bash
88
+ # Workflow 1 - UI strings (default template enables extract + translate-ui)
89
+ npx ai-i18n-tools init
90
+ npx ai-i18n-tools extract
91
+ npx ai-i18n-tools translate-ui
92
+
93
+ # Workflow 2 - docs (Docusaurus-oriented template)
94
+ npx ai-i18n-tools init -t ui-docusaurus
95
+ npx ai-i18n-tools translate-docs
96
+
97
+ # Combined: extract UI strings, then translate UI + SVG + docs (per config features)
98
+ npx ai-i18n-tools sync
99
+
100
+ # Markdown translation status (per file × locale)
101
+ npx ai-i18n-tools status
102
+ ```
103
+
104
+ ---
105
+
106
+ ## 工作流程 1 - UI 翻译
107
+
108
+ 为任何使用 i18next 的 JS/TS 项目设计:React 应用、Next.js(客户端和服务器组件)、Node.js 服务、CLI 工具。
109
+
110
+ ### 步骤 1:初始化
111
+
112
+ ```bash
113
+ npx ai-i18n-tools init
114
+ ```
115
+
116
+ 这将使用 `ui-markdown` 模板写入 `ai-i18n-tools.config.json`。编辑它以设置:
117
+
118
+ - `sourceLocale` - 源语言的 BCP-47 代码(例如 `"en-GB"`)。**必须匹配**从运行时 i18n 设置文件(`src/i18n.ts` / `src/i18n.js`)导出的 `SOURCE_LOCALE`。
119
+ - `targetLocales` - 指向 `ui-languages.json` 清单文件的路径,或一个 BCP-47 代码数组。
120
+ - `ui.sourceRoots` - 扫描 `t("…")` 调用的目录(例如 `["src/"]`)。
121
+ - `ui.stringsJson` - 写入主目录的位置(例如 `"src/locales/strings.json"`)。
122
+ - `ui.flatOutputDir` - 写入 `de.json`、`pt-BR.json` 等文件的位置(例如 `"src/locales/"`)。
123
+ - `ui.preferredModel`(可选)- 仅用于 `translate-ui` 时**首先**尝试的 OpenRouter 模型 ID;如果失败,CLI 将继续按顺序使用 `openrouter.translationModels`(或旧的 `defaultModel` / `fallbackModel`),跳过重复项。
124
+
125
+ ### 步骤 2:提取字符串
126
+
127
+ ```bash
128
+ npx ai-i18n-tools extract
129
+ ```
130
+
131
+ 扫描 `ui.sourceRoots` 下所有 JS/TS 文件中的 `t("literal")` 和 `i18n.t("literal")` 调用。写入(或合并到)`ui.stringsJson`。
132
+
133
+ 扫描器是可配置的:可以通过 `ui.reactExtractor.funcNames` 添加自定义函数名称。
134
+
135
+ ### 步骤 3:翻译 UI 字符串
136
+
137
+ ```bash
138
+ npx ai-i18n-tools translate-ui
139
+ ```
140
+
141
+ 读取 `strings.json`,为每个目标语言环境将批次发送到 OpenRouter,将扁平 JSON 文件(`de.json`、`fr.json` 等)写入 `ui.flatOutputDir`。当设置了 `ui.preferredModel` 时,会先尝试该模型,然后再使用 `openrouter.translationModels` 中的有序列表(文档翻译和其他命令仍仅使用 `openrouter`)。
142
+
143
+ 对于每个条目,`translate-ui` 在可选的 `models` 对象中存储成功翻译每种语言的 **OpenRouter 模型 ID**(与 `translated` 相同的语言键)。在本地 `editor` 命令中编辑的字符串在该语言的 `models` 中标记为哨兵值 `user-edited`。位于 `ui.flatOutputDir` 下的每种语言的平面文件仅保留 **源字符串 → 翻译**;它们不包括 `models`(因此运行时包保持不变)。
144
+
145
+ > **关于使用缓存编辑器的注意事项:** 如果你在缓存编辑器中编辑了一个条目,需要运行 `sync --force-update`(或等效的 `translate` 命令并加上 `--force-update`)来用更新后的缓存条目重写输出文件。另外,请注意,如果稍后源文本发生变化,你的手动编辑将会丢失,因为会为新的源字符串生成一个新的缓存键(哈希值)。
146
+
147
+ ### 导出为 XLIFF 2.0(可选)
148
+
149
+ 要将 UI 字符串交付给翻译供应商、TMS 或 CAT 工具,请将目录导出为 **XLIFF 2.0** 格式(每个目标语言区域一个文件)。此命令为 **只读**:不会修改 `strings.json` 或调用任何 API。
150
+
151
+ ```bash
152
+ npx ai-i18n-tools export-ui-xliff
153
+ ```
154
+
155
+ 默认情况下,文件会写入 `ui.stringsJson` 旁边,命名为 `strings.de.xliff`、`strings.pt-BR.xliff`(目录的基本名称 + 语言区域 + `.xliff`)。使用 `-o` / `--output-dir` 可将文件写入其他位置。来自 `strings.json` 的现有翻译会出现在 `<target>` 中;缺失的语言区域会使用 `state="initial"` 且无 `<target>`,以便工具填充。使用 `--untranslated-only` 仅导出每个语言区域仍需翻译的单元(适用于供应商批量处理)。`--dry-run` 会打印路径但不写入文件。
156
+
157
+ ### 步骤 4:在运行时连接 i18next
158
+
159
+ 使用 `'ai-i18n-tools/runtime'` 导出的辅助函数创建你的 i18n 设置文件:
160
+
161
+ ```js
162
+ // src/i18n.js (or src/i18n.ts)
163
+ import i18n from 'i18next';
164
+ import { initReactI18next } from 'react-i18next';
165
+ import uiLanguages from './locales/ui-languages.json';
166
+ import {
167
+ defaultI18nInitOptions,
168
+ wrapI18nWithKeyTrim,
169
+ makeLoadLocale,
170
+ applyDirection,
171
+ } from 'ai-i18n-tools/runtime';
172
+
173
+ // Must match sourceLocale in ai-i18n-tools.config.json
174
+ export const SOURCE_LOCALE = 'en-GB';
175
+
176
+ void i18n.use(initReactI18next).init(defaultI18nInitOptions(SOURCE_LOCALE));
177
+ wrapI18nWithKeyTrim(i18n);
178
+ i18n.on('languageChanged', applyDirection);
179
+ applyDirection(i18n.language);
180
+
181
+ const localeLoaders = Object.fromEntries(
182
+ uiLanguages
183
+ .filter(({ code }) => code !== SOURCE_LOCALE)
184
+ .map(({ code }) => [code, () => import(`./locales/${code}.json`)])
185
+ );
186
+
187
+ export const loadLocale = makeLoadLocale(i18n, localeLoaders, SOURCE_LOCALE);
188
+ export default i18n;
189
+ ```
190
+
191
+ 在 React 渲染之前导入 `i18n.js`(例如,在入口文件的顶部)。当用户更改语言时,调用 `await loadLocale(code)`,然后调用 `i18n.changeLanguage(code)`。
192
+
193
+ `SOURCE_LOCALE` 被导出,因此任何其他需要它的文件(例如语言切换器)可以直接从 `'./i18n'` 导入它。
194
+
195
+ `defaultI18nInitOptions(sourceLocale)` 返回用于键作为默认设置的标准选项:
196
+
197
+ - `parseMissingKeyHandler` 返回键本身,因此未翻译的字符串显示源文本。
198
+ - `nsSeparator: false` 允许键中包含冒号。
199
+ - `interpolation.escapeValue: false` - 可以安全禁用:React 会自行转义值,且 Node.js/CLI 输出没有需要转义的 HTML。
200
+
201
+ `wrapI18nWithKeyTrim(i18n)` 包装 `i18n.t` 以便: (1) 在查找之前修剪键,匹配提取脚本存储它们的方式; (2) 当源语言返回原始键时应用 <code>{"{{var}}"}</code> 插值 - 因此 <code>{"t('Hello {{name}}', { name })"}</code> 即使对于源语言也能正确工作。
202
+
203
+ `makeLoadLocale(i18n, loaders, sourceLocale)` 返回一个异步的 `loadLocale(lang)` 函数,该函数动态导入某种语言的 JSON 包并将其注册到 i18next。
204
+
205
+ ### 在源代码中使用 `t()`
206
+
207
+ 使用**字面量字符串**调用 `t()`,以便提取脚本能够找到它:
208
+
209
+ ```jsx
210
+ import { useTranslation } from 'react-i18next';
211
+
212
+ function MyComponent() {
213
+ const { t } = useTranslation();
214
+ return <button>{t('Save')}</button>;
215
+ }
216
+ ```
217
+
218
+ 相同的模式在 React 外部(Node.js、服务器组件、CLI)也适用:
219
+
220
+ ```js
221
+ import i18n from './i18n.js';
222
+ console.log(i18n.t('Processing complete'));
223
+ ```
224
+
225
+ **规则:**
226
+
227
+ - 仅提取这些形式: `t("…")`, `t('…')`, `t(`…`)`, `i18n.t("…")`。
228
+ - 键必须是一个 **字面字符串** - 不允许使用变量或表达式作为键。
229
+ - 不要为键使用模板字面量:<code>{'t(`Hello ${name}`)'}</code> 是不可提取的。
230
+
231
+ ### 插值
232
+
233
+ 使用 i18next 的原生第二个参数插值来处理 <code>{"{{var}}"}</code> 占位符:
234
+
235
+ ```js
236
+ // i18next handles substitution natively, even in key-as-default mode
237
+ t('Hello {{name}}, you have {{count}} messages', { name, count })
238
+ // → "Hello Alice, you have 3 messages"
239
+ ```
240
+
241
+ 提取脚本忽略第二个参数 - 仅提取字面键字符串 <code>{"\"Hello {{name}}, you have {{count}} messages\""}</code> 并发送进行翻译。翻译者被指示保留 <code>{"{{...}}"}</code> 令牌。
242
+
243
+ ### 语言切换器 UI
244
+
245
+ 使用 `ui-languages.json` 清单构建语言选择器。 `ai-i18n-tools` 导出两个显示助手:
246
+
247
+ ```tsx
248
+ import { useMemo } from 'react';
249
+ import { useTranslation } from 'react-i18next';
250
+ import {
251
+ getUILanguageLabel,
252
+ getUILanguageLabelNative,
253
+ type UiLanguageEntry,
254
+ } from 'ai-i18n-tools/runtime';
255
+ import uiLanguages from './locales/ui-languages.json';
256
+ import { loadLocale } from './i18n';
257
+
258
+ function LanguageSelect({
259
+ value,
260
+ onChange,
261
+ }: {
262
+ value: string;
263
+ onChange: (code: string) => void;
264
+ }) {
265
+ const { t, i18n } = useTranslation();
266
+
267
+ const options = useMemo(
268
+ () =>
269
+ (uiLanguages as UiLanguageEntry[]).map((lang) => ({
270
+ code: lang.code,
271
+ // Settings/content dropdowns: shows translated name when available
272
+ label: getUILanguageLabel(lang, t),
273
+ // Header globe menu: shows "English / Deutsch"-style label, no t() call
274
+ nativeLabel: getUILanguageLabelNative(lang),
275
+ })),
276
+ [t]
277
+ );
278
+
279
+ const handleChange = async (code: string) => {
280
+ await loadLocale(code);
281
+ i18n.changeLanguage(code);
282
+ onChange(code);
283
+ };
284
+
285
+ return (
286
+ <select value={value} onChange={(e) => handleChange(e.target.value)}>
287
+ {options.map((row) => (
288
+ <option key={row.code} value={row.code}>
289
+ {row.label}
290
+ </option>
291
+ ))}
292
+ </select>
293
+ );
294
+ }
295
+ ```
296
+
297
+ `getUILanguageLabel(lang, t)` - 当翻译时显示 `t(englishName)`,或者当两者不同时显示 `englishName / t(englishName)`。适合设置屏幕。
298
+
299
+ `getUILanguageLabelNative(lang)` - 显示 `englishName / label`(每行不调用 `t()`)。适合希望原名可见的标题菜单。
300
+
301
+ `ui-languages.json` 清单是一个 JSON 数组,包含 <code>{"{ code, label, englishName }"}</code> 条目。示例:
302
+
303
+ ```json
304
+ [
305
+ { "code": "en-GB", "label": "English (UK)", "englishName": "English (UK)" },
306
+ { "code": "pt-BR", "label": "Português (BR)", "englishName": "Portuguese (BR)" },
307
+ { "code": "de", "label": "Deutsch", "englishName": "German" },
308
+ { "code": "fr", "label": "Français", "englishName": "French" },
309
+ { "code": "ar", "label": "العربية", "englishName": "Arabic" }
310
+ ]
311
+ ```
312
+
313
+ 在配置中设置 `targetLocales` 为此文件的路径,以便翻译命令使用相同的列表。
314
+
315
+ ### RTL 语言
316
+
317
+ `ai-i18n-tools` 导出 `getTextDirection(lng)` 和 `applyDirection(lng)`:
318
+
319
+ ```js
320
+ import { getTextDirection, applyDirection } from 'ai-i18n-tools/runtime';
321
+
322
+ getTextDirection('ar') // 'rtl'
323
+ getTextDirection('en-GB') // 'ltr'
324
+
325
+ // Applied automatically via i18n.on('languageChanged', applyDirection) - see Step 4
326
+ ```
327
+
328
+ `applyDirection` 设置 `document.documentElement.dir`(浏览器)或在 Node.js 中为无操作。传递一个可选的 `element` 参数以针对特定元素。
329
+
330
+ 对于可能包含 `→` 箭头的字符串,翻转它们以适应 RTL 布局:
331
+
332
+ ```js
333
+ import { flipUiArrowsForRtl } from 'ai-i18n-tools/runtime';
334
+ const { i18n } = useTranslation();
335
+ const isRtl = getTextDirection(i18n.language) === 'rtl';
336
+ const label = flipUiArrowsForRtl(t('Next → Step'), isRtl);
337
+ ```
338
+
339
+ ---
340
+
341
+ ## 工作流 2 - 文档翻译
342
+
343
+ 专为 Markdown 文档、Docusaurus 站点和 JSON 标签文件设计。当启用 `features.translateSVG` 并设置了顶层 `svg` 块时,独立的 SVG 资源将通过 [`translate-svg`](#cli-reference) 进行翻译——而不是通过 `documentations[].contentPaths`。
344
+
345
+ ### 步骤 1:初始化
346
+
347
+ ```bash
348
+ npx ai-i18n-tools init -t ui-docusaurus
349
+ ```
350
+
351
+ 编辑生成的 `ai-i18n-tools.config.json`:
352
+
353
+ - `sourceLocale` - 源语言(必须与 `docusaurus.config.js` 中的 `defaultLocale` 匹配)。
354
+ - `targetLocales` - 目标语言代码数组或清单文件路径。
355
+ - `cacheDir` - 所有文档流水线共享的 SQLite 缓存目录(也是 `--write-logs` 的默认日志目录)。
356
+ - `documentations` - 文档块数组。每个块包含可选的 `description`、`contentPaths`、`outputDir`、可选的 `jsonSource`、`markdownOutput`、`targetLocales`、`addFrontmatter` 等。
357
+ - `documentations[].description` - 可选的简短备注,供维护者使用(说明此块涵盖的内容)。设置后,它会显示在 `translate-docs` 的标题(`🌐 …: translating …`)和 `status` 部分的标题中。
358
+ - `documentations[].contentPaths` - Markdown/MDX 源目录或文件(另请参见 `documentations[].jsonSource` 以获取 JSON 标签)。
359
+ - `documentations[].outputDir` - 该块的翻译输出根目录。
360
+ - `documentations[].markdownOutput.style` - `"nested"`(默认)、`"docusaurus"` 或 `"flat"`(参见 [输出布局](#output-layouts))。
361
+
362
+ ### 步骤 2:翻译文档
363
+
364
+ ```bash
365
+ npx ai-i18n-tools translate-docs
366
+ ```
367
+
368
+ 这将翻译每个 `documentations` 块的 `contentPaths` 中的所有文件到所有有效的文档语言(每个块的 `targetLocales` 的并集,当未设置时为根 `targetLocales`)。已翻译的段落从 SQLite 缓存中提供 - 只有新的或更改的段落会发送到 LLM。
369
+
370
+ 要翻译单个语言环境:
371
+
372
+ ```bash
373
+ npx ai-i18n-tools translate-docs --locale de
374
+ ```
375
+
376
+ 要检查需要翻译的内容:
377
+
378
+ ```bash
379
+ npx ai-i18n-tools status
380
+ ```
381
+
382
+ #### 缓存行为和 `translate-docs` 标志
383
+
384
+ CLI 在 SQLite 中保持 **文件跟踪**(每个文件 × 语言环境的源哈希)和 **段落** 行(可翻译块的哈希 × 语言环境)。正常运行时,当跟踪的哈希与当前源匹配 **且** 输出文件已存在时,会完全跳过该文件;否则,它会处理该文件并使用段落缓存,以便未更改的文本不会调用 API。
385
+
386
+ | 旗帜 | 效果 |
387
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
388
+ | *(默认)* | 在跟踪 + 磁盘输出匹配时跳过未更改的文件;对其余部分使用段缓存。 |
389
+ | `--force-update` | 重新处理每个匹配的文件(提取、重新组装、写入输出),即使文件跟踪会跳过。 **段缓存仍然适用** - 未更改的段不会发送到 LLM。 |
390
+ | `--force` | 清除每个处理文件的文件跟踪,并且 **不读取** API 翻译的段缓存(完全重新翻译)。新结果仍然 **写入** 段缓存。 |
391
+ | `--stats` | 打印段计数、跟踪文件计数和每种语言的段总数,然后退出。 |
392
+ | `--clear-cache [locale]` | 删除缓存的翻译(和文件跟踪):所有语言,或单一语言,然后退出。 |
393
+ | `--prompt-format <mode>` | 每个 **批次** 的段如何发送到模型并解析(`xml`、`json-array` 或 `json-object`)。默认 **`xml`**。不会改变提取、占位符、验证、缓存或回退行为 — 请参见 [Batch prompt format](#batch-prompt-format)。 |
394
+
395
+ 您不能将 `--force` 与 `--force-update` 结合使用(它们是互斥的)。
396
+
397
+ #### 批量提示格式
398
+
399
+ `translate-docs` 将可翻译的段发送到 OpenRouter 以 **批次**(按 `batchSize` / `maxBatchChars` 分组)。 **`--prompt-format`** 标志仅更改该批次的 **传输格式**;段拆分、`PlaceholderHandler` 令牌、markdown AST 检查、SQLite 缓存键和当批量解析失败时的每段回退保持不变。
400
+
401
+ | 模式 | 用户消息 | 模型回复 |
402
+ | ---- | ------------ | ----------- |
403
+ | **`xml`**(默认) | 伪 XML:每个片段一个 `<seg id="N">…</seg>`(带 XML 转义)。 | 只输出 `<t id="N">…</t>` 块,每个片段索引一个。 |
404
+ | **`json-array`** | 字符串 JSON 数组,按顺序每个片段一个条目。 | 与 **相同长度**的 JSON 数组(顺序相同)。 |
405
+ | **`json-object`** | 一个以片段索引为键的 JSON 对象 `{"0":"…","1":"…",…}`。 | 具有**相同键**且值已翻译的 JSON 对象。 |
406
+
407
+ 运行头部也会打印 `Batch prompt format: …`,这样你就可以确认当前模式。JSON 标签文件(`jsonSource`)和独立 SVG 批次在这些步骤作为 `translate-docs`(或 `sync` 的 docs 阶段)的一部分运行时也使用相同设置——`sync` 不会暴露此标志;它默认为 **`xml`**。
408
+
409
+ **SQLite 中的段落去重和路径**
410
+
411
+ - 片段行按 `(source_hash, locale)` 全局键控(hash = 归一化内容)。两个文件中的相同文本共享同一行;`translations.filepath` 是元数据(最后写入者),不是每个文件各自的第二个缓存条目。
412
+ - `file_tracking.filepath` 使用命名空间键:每个 `documentations` 块对应 `doc-block:{index}:{relPath}`(`relPath` 是相对于项目根目录的 posix 路径:按收集到的 markdown 路径;**JSON 标签文件使用相对于当前工作目录的源文件路径**,例如 `docs-site/i18n/en/code.json`,这样清理时就能解析出真实文件),以及 `translate-svg` 下独立 SVG 资源对应的 `svg-assets:{relPath}`。
413
+ - `translations.filepath` 为 markdown、JSON 和 SVG 片段存储相对于当前工作目录的 posix 路径(SVG 使用与其他资源相同的路径形状;`svg-assets:…` 前缀仅**用于** `file_tracking`)。
414
+ - 运行结束后,只有**在相同翻译范围内**(考虑 `--path` 和已启用类型)但未命中的片段行的 `last_hit_at` 会被清空,因此过滤运行或仅 docs 运行不会把无关文件标记为过期。
415
+
416
+ ### 输出布局
417
+
418
+ `"nested"`(省略时的默认值)— 按源树镜像到 `{outputDir}/{locale}/` 下(例如 `docs/guide.md` → `i18n/de/docs/guide.md`)。
419
+
420
+ `"docusaurus"` — 将位于 `docsRoot` 下的文件放置到 `i18n/<locale>/docusaurus-plugin-content-docs/current/<relativeToDocsRoot>`,与常见的 Docusaurus i18n 布局一致。将 `documentations[].markdownOutput.docsRoot` 设为你的文档源根目录(例如 `"docs"`)。
421
+
422
+ ```
423
+ docs/guide.md → i18n/de/docusaurus-plugin-content-docs/current/guide.md
424
+ i18n/en/sidebar.json → i18n/de/sidebar.json (JSON label files)
425
+ ```
426
+
427
+ `"flat"` - 将翻译后的文件放在源文件旁边,并加上语言区域后缀,或者放在子目录中。页面之间的相对链接会自动重写。
428
+
429
+ ```
430
+ docs/guide.md → i18n/guide.de.md
431
+ ```
432
+
433
+ 您可以通过 `documentations[].markdownOutput.pathTemplate` 完全覆盖路径。占位符:<code>{"{outputDir}"}</code>, <code>{"{locale}"}</code>, <code>{"{LOCALE}"}</code>, <code>{"{relPath}"}</code>, <code>{"{stem}"}</code>, <code>{"{basename}"}</code>, <code>{"{extension}"}</code>, <code>{"{docsRoot}"}</code>, <code>{"{relativeToDocsRoot}"}</code>。
434
+
435
+ ---
436
+
437
+ ## 组合工作流程 (UI + 文档)
438
+
439
+ 在单个配置中启用所有功能,以同时运行两个工作流程:
440
+
441
+ ```json
442
+ {
443
+ "sourceLocale": "en-GB",
444
+ "targetLocales": "src/locales/ui-languages.json",
445
+ "features": {
446
+ "extractUIStrings": true,
447
+ "translateUIStrings": true,
448
+ "translateMarkdown": true,
449
+ "translateJSON": false,
450
+ "translateSVG": false
451
+ },
452
+ "glossary": {
453
+ "uiGlossary": "src/locales/strings.json",
454
+ "userGlossary": "glossary-user.csv"
455
+ },
456
+ "ui": {
457
+ "sourceRoots": ["src/"],
458
+ "stringsJson": "src/locales/strings.json",
459
+ "flatOutputDir": "src/locales/"
460
+ },
461
+ "cacheDir": ".translation-cache",
462
+ "documentations": [
463
+ {
464
+ "contentPaths": ["docs/"],
465
+ "outputDir": "i18n/",
466
+ "markdownOutput": { "style": "flat" }
467
+ }
468
+ ]
469
+ }
470
+ ```
471
+
472
+ `glossary.uiGlossary` 将文档翻译指向与 UI 相同的 `strings.json` 目录,以保持术语一致;`glossary.userGlossary` 为产品术语添加 CSV 覆盖。
473
+
474
+ 运行 `npx ai-i18n-tools sync` 可执行完整流水线:**提取** UI 字符串(如果启用了 `features.extractUIStrings`),**翻译 UI** 字符串(如果启用了 `features.translateUIStrings`),**翻译独立 SVG 资源**(如果设置了 `features.translateSVG` 和顶层 `svg` 块),然后 **翻译文档**(每个 `documentations` 块中配置的 Markdown/JSON 文件)。可通过 `--no-ui`、`--no-svg` 或 `--no-docs` 跳过相应步骤。文档步骤支持 `--dry-run`、`-p` / `--path`、`--force` 和 `--force-update` 参数(后两个参数仅在执行文档翻译时生效;若指定 `--no-docs` 则会被忽略)。
475
+
476
+ 在块上使用 `documentations[].targetLocales` 将该块的文件翻译为比 UI 更**小的子集**(有效的文档语言是跨块的**并集**):
477
+
478
+ ```json
479
+ {
480
+ "targetLocales": "src/locales/ui-languages.json",
481
+ "documentations": [
482
+ {
483
+ "contentPaths": ["docs/"],
484
+ "outputDir": "i18n/",
485
+ "targetLocales": ["de", "fr", "es"]
486
+ }
487
+ ]
488
+ }
489
+ ```
490
+
491
+ ---
492
+
493
+ ## 配置参考
494
+
495
+ ### `sourceLocale`
496
+
497
+ 源语言的 BCP-47 代码(例如 `"en-GB"`、`"en"`、`"pt-BR"`)。此区域不会生成翻译文件 - 关键字符串本身就是源文本。
498
+
499
+ **必须匹配** 从您的运行时 i18n 设置文件(`src/i18n.ts` / `src/i18n.js`)导出的 `SOURCE_LOCALE`。
500
+
501
+ ### `targetLocales`
502
+
503
+ 要翻译到的区域。接受:
504
+
505
+ - **字符串路径** 到 `ui-languages.json` 清单(`"src/locales/ui-languages.json"`)。该文件被加载并提取区域代码。
506
+ - **BCP-47 代码数组**(`["de", "fr", "es"]`)。
507
+ - **带路径的单元素数组**(`["src/locales/ui-languages.json"]`) - 与字符串形式具有相同的行为。
508
+
509
+ `targetLocales` 是 UI 翻译的主要区域列表,也是文档块的默认区域列表。如果您希望在此处保留显式数组,但仍希望使用清单驱动的标签和区域过滤,请同时设置 `uiLanguagesPath`。
510
+
511
+ ### `uiLanguagesPath`(可选)
512
+
513
+ 指向用于显示名称、区域过滤和语言列表后处理的 `ui-languages.json` 清单的路径。
514
+
515
+ 在以下情况下使用:
516
+
517
+ - `targetLocales` 是显式数组,但您仍希望从清单中获取英语/本地标签。
518
+ - 您希望 `markdownOutput.postProcessing.languageListBlock` 从同一清单构建区域标签。
519
+ - 仅启用 UI 翻译,您希望清单提供有效的 UI 区域列表。
520
+
521
+ ### `concurrency`(可选)
522
+
523
+ 同时翻译的最大**目标区域**(`translate-ui`、`translate-docs`、`translate-svg`,以及 `sync` 内部的匹配步骤)。如果省略,CLI 对于 UI 翻译使用**4**,对于文档翻译使用**3**(内置默认值)。通过 `-j` / `--concurrency` 在每次运行时覆盖。
524
+
525
+ ### `batchConcurrency`(可选)
526
+
527
+ **translate-docs** 和 **translate-svg**(以及 `sync` 的文档翻译步骤):每个文件的最大并行 OpenRouter **批量**请求数(每个批次可包含多个段落)。省略时默认值为 **4**。`translate-ui` 忽略此设置。使用 `-b` / `--batch-concurrency` 覆盖。在 `sync` 上,`-b` 仅适用于文档翻译步骤。
528
+
529
+ ### `batchSize` / `maxBatchChars`(可选)
530
+
531
+ 文档翻译的段落批处理:每个 API 请求包含多少段落,以及字符上限。默认值:**20** 个段落,**4096** 个字符(当省略时)。
532
+
533
+ ### `openrouter`
534
+
535
+ | 字段 | 描述 |
536
+ | ------------------ | ---------------------------------------------------------------------------------------- |
537
+ | `baseUrl` | OpenRouter API 基础 URL。默认值:`https://openrouter.ai/api/v1`。 |
538
+ | `translationModels` | 首选的模型 ID 有序列表。先尝试第一个;后续条目在出错时作为回退。仅对于 `translate-ui`**,你还可以设置 `ui.preferredModel`,先尝试一个模型再使用此列表(参见 `ui`)。 |
539
+ | `defaultModel` | 传统的单个主模型。仅在未设置 `translationModels` 或其为空时使用。 |
540
+ | `fallbackModel` | 传统的单个回退模型。在未设置 `translationModels` 或其为空时,在 `defaultModel` 之后使用。 |
541
+ | `maxTokens` | 每次请求的最大完成 token 数。默认值:`8192`。 |
542
+ | `temperature` | 采样温度。默认值:`0.2`。 |
543
+
544
+ 在你的环境或 `.env` 文件中设置 `OPENROUTER_API_KEY`。
545
+
546
+ ### `features`
547
+
548
+ | 字段 | 工作流程 | 说明 |
549
+ | -------------------- | -------- | -------------------------------------------------------------------- |
550
+ | `extractUIStrings` | 1 | 扫描源码中的 `t("…")` 调用,并写入或合并 `strings.json` 文件。 |
551
+ | `translateUIStrings` | 1 | 翻译 `strings.json` 中的条目,并生成按语言环境划分的 JSON 文件。 |
552
+ | `translateMarkdown` | 2 | 翻译 `.md` / `.mdx` 文件。 |
553
+ | `translateJSON` | 2 | 翻译 Docusaurus JSON 标签文件。 |
554
+ | `translateSVG` | 2 | 翻译独立的 `.svg` 资源(需要配置顶层 `svg` 块)。 |
555
+
556
+ 当 `features.translateSVG` 为 true 且配置了顶层 `svg` 块时,使用 `translate-svg` 翻译 **独立的** SVG 资源。`sync` 命令在两个条件均满足时执行该步骤(除非指定 `--no-svg`)。
557
+
558
+ ### `ui`
559
+
560
+ | 字段 | 说明 |
561
+ | --------------------------- | ----------------------------------------------------------------------- |
562
+ | `sourceRoots` | 用于扫描 `t("…")` 调用的目录(相对于当前工作目录)。 |
563
+ | `stringsJson` | 主语言目录文件的路径。由 `extract` 命令更新。 |
564
+ | `flatOutputDir` | 按语言环境生成的 JSON 文件所在目录(如 `de.json` 等)。 |
565
+ | `preferredModel` | 可选。`translate-ui` 命令优先尝试的 OpenRouter 模型 ID;若未指定,则按顺序尝试 `openrouter.translationModels`(或旧版模型),且不重复此 ID。 |
566
+ | `reactExtractor.funcNames` | 额外要扫描的函数名(默认值:`["t", "i18n.t"]`)。 |
567
+ | `reactExtractor.extensions` | 要包含的文件扩展名(默认值:`[".js", ".jsx", ".ts", ".tsx"]`)。 |
568
+ | `reactExtractor.includePackageDescription` | 当值为 `true`(默认)时,若存在 `package.json`,`extract` 命令也会将其 `description` 字段作为 UI 字符串包含进来。 |
569
+ | `reactExtractor.packageJsonPath` | 用于提取描述信息的自定义 `package.json` 文件路径。 |
570
+
571
+ ### `cacheDir`
572
+
573
+ | 字段 | 描述 |
574
+ | ---------- | ----------------------------------------------------------------------------- |
575
+ | `cacheDir` | SQLite 缓存目录(由所有 `documentations` 块共享)。跨运行重用。 |
576
+
577
+ ### `documentations`
578
+
579
+ 文档管道块的数组。`translate-docs` 和 `sync` 过程的文档阶段 **逐个** 处理每个块。
580
+
581
+ | 字段 | 说明 |
582
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
583
+ | `description` | 此区块的可选人工可读备注(不用于翻译)。设置后,会在 `translate-docs` 的 `🌐` 标题前加上前缀;也会显示在 `status` 部分的标题中。 |
584
+ | `contentPaths` | 需要翻译的 Markdown/MDX 源文件(`translate-docs` 会扫描这些路径下的 `.md` / `.mdx` 文件)。JSON 标签来自此区块的 `jsonSource`。 |
585
+ | `outputDir` | 此区块翻译输出的根目录。 |
586
+ | `sourceFiles` | 可选别名,在加载时合并到 `contentPaths` 中。 |
587
+ | `targetLocales` | 仅针对此区块的可选语言子集(否则使用根级 `targetLocales`)。实际生效的文档语言是所有区块的并集。 |
588
+ | `jsonSource` | 此区块的 Docusaurus JSON 标签文件的源目录(例如 `"i18n/en"`)。 |
589
+ | `markdownOutput.style` | `"nested"`(默认)、`"docusaurus"` 或 `"flat"`。 |
590
+ | `markdownOutput.docsRoot` | Docusaurus 布局的源文档根目录(例如 `"docs"`)。 |
591
+ | `markdownOutput.pathTemplate` | 自定义 Markdown 输出路径。占位符: <code>{"{outputDir}"}</code>、<code>{"{locale}"}</code>、<code>{"{LOCALE}"}</code>、<code>{"{relPath}"}</code>、<code>{"{stem}"}</code>、<code>{"{basename}"}</code>、<code>{"{extension}"}</code>、<code>{"{docsRoot}"}</code>、<code>{"{relativeToDocsRoot}"}</code>。 |
592
+ | `markdownOutput.jsonPathTemplate` | 自定义 JSON 输出路径用于标签文件。支持与 `pathTemplate` 相同的占位符。 |
593
+ | `markdownOutput.flatPreserveRelativeDir` | 对于 `flat` 风格,保留源子目录以避免同名文件冲突。 |
594
+ | `markdownOutput.rewriteRelativeLinks` | 在翻译后重写相对链接(`flat` 风格下自动启用)。 |
595
+ | `markdownOutput.linkRewriteDocsRoot` | 计算扁平化链接重写前缀时使用的仓库根目录。通常保留为 `"."`,除非你的翻译文档位于不同的项目根目录下。 |
596
+ | `markdownOutput.postProcessing` | 对翻译后的 Markdown **正文** 的可选转换(YAML front matter 会被保留)。在段落重新组装和扁平链接重写之后、`addFrontmatter` 之前执行。 |
597
+ | `markdownOutput.postProcessing.regexAdjustments` | 有序的 `{ "description"?, "search", "replace" }` 列表。`search` 是正则表达式模式(纯字符串默认使用 `g` 标志,或 `/pattern/flags`)。`replace` 支持占位符,如 `${translatedLocale}`、`${sourceLocale}`、`${sourceFullPath}`、`${translatedFullPath}`、`${sourceFilename}`、`${translatedFilename}`、`${sourceBasedir}`、`${translatedBasedir}`(概念与参考中的 `additional-adjustments` 相同)。 |
598
+ | `markdownOutput.postProcessing.languageListBlock` | `{ "start", "end", "separator" }` — 翻译器会查找包含 `start` 的第一行和匹配的 `end` 行,然后将该部分替换为标准语言切换器。链接基于翻译文件的相对路径构建;标签来自配置的 `uiLanguagesPath` / `ui-languages.json`,若未配置则来自 `localeDisplayNames` 和语言代码。 |
599
+ | `addFrontmatter` | 当为 `true` 时(默认值,省略时即为 true),翻译后的 Markdown 文件将包含以下 YAML 键:`translation_last_updated`、`source_file_mtime`、`source_file_hash`、`translation_language`、`source_file_path`,以及当至少一个段落包含模型元数据时,还包括 `translation_models`(使用的 OpenRouter 模型 ID 的排序列表)。设为 `false` 可跳过。 |
600
+
601
+ 示例(平面 README 管道 — 截图路径 + 可选语言列表包装):
602
+
603
+ ```json
604
+ "markdownOutput": {
605
+ "style": "flat",
606
+ "postProcessing": {
607
+ "regexAdjustments": [
608
+ {
609
+ "description": "Per-locale screenshot folders",
610
+ "search": "images/screenshots/[^/]+/",
611
+ "replace": "images/screenshots/${translatedLocale}/"
612
+ }
613
+ ],
614
+ "languageListBlock": {
615
+ "start": "<small id=\"lang-list\">",
616
+ "end": "</small>",
617
+ "separator": " · "
618
+ }
619
+ }
620
+ }
621
+ ```
622
+
623
+ ### `svg`(可选)
624
+
625
+ 用于独立 SVG 资源的顶层路径和布局。仅当 **`features.translateSVG`** 为 true 时才会执行翻译(通过 `translate-svg` 或 `sync` 的 SVG 阶段)。
626
+
627
+ | 字段 | 描述 |
628
+ | --------------------------- | ----------- |
629
+ | `sourcePath` | 一个目录或一个递归扫描 `.svg` 文件的目录数组。 |
630
+ | `outputDir` | 翻译后的 SVG 输出的根目录。 |
631
+ | `style` | 当 `pathTemplate` 未设置时为 `"flat"` 或 `"nested"`。 |
632
+ | `pathTemplate` | 自定义 SVG 输出路径。占位符:<code>{"{outputDir}"}</code>, <code>{"{locale}"}</code>, <code>{"{LOCALE}"}</code>, <code>{"{relPath}"}</code>, <code>{"{stem}"}</code>, <code>{"{basename}"}</code>, <code>{"{extension}"}</code>, <code>{"{relativeToSourceRoot}"}</code>. |
633
+ | `svgExtractor.forceLowercase` | SVG 重新组装时的翻译文本小写。对于依赖全小写标签的设计非常有用。 |
634
+
635
+ ### `glossary`
636
+
637
+ | 字段 | 说明 |
638
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
639
+ | `uiGlossary` | 指向 `strings.json` 的路径 — 自动从现有翻译中构建术语表。 |
640
+ | `userGlossary` | 指向一个 CSV 文件的路径,该文件包含列 `Original language string`(或 `en`)、`locale`、`Translation` — 每个源术语和目标语言环境对应一行(`locale` 可为 `*` 表示所有目标)。 |
641
+
642
+ 遗留键 `uiGlossaryFromStringsJson` 仍然被接受,并在加载配置时映射到 `uiGlossary`。
643
+
644
+ 生成一个空的词汇表 CSV:
645
+
646
+ ```bash
647
+ npx ai-i18n-tools glossary-generate
648
+ ```
649
+
650
+ ---
651
+
652
+ ## CLI 参考
653
+
654
+ | 命令 | 说明 |
655
+ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
656
+ | `init [-t ui-markdown|ui-docusaurus] [-o path] [--with-translate-ignore]` | 生成一个初始配置文件(包含 `concurrency`、`batchConcurrency`、`batchSize`、`maxBatchChars` 和 `documentations[].addFrontmatter`)。`--with-translate-ignore` 会创建一个初始的 `.translate-ignore`。 |
657
+ | `extract` | 扫描源码中的 `t("…")` 调用并更新 `strings.json`。需要 `features.extractUIStrings`。 |
658
+ | `translate-docs …` | 为每个 `documentations` 块(`contentPaths`,可选 `jsonSource`)翻译 Markdown/MDX 和 JSON。`-j`:最大并行语言区域数;`-b`:每个文件最大并行批处理 API 调用数。`--prompt-format`:批处理传输格式(`xml` \| `json-array` \| `json-object`)。参见 [缓存行为和 `translate-docs` 标志](#cache-behaviour-and-translate-docs-flags) 和 [批处理提示格式](#batch-prompt-format)。 |
659
+ | `translate-svg …` | 翻译在 `config.svg` 中配置的独立 SVG 资源(与文档分离)。需要 `features.translateSVG`。与文档共享相同的缓存机制;支持 `--no-cache` 以跳过本次运行的 SQLite 读写操作。支持 `-j`、`-b`、`--force`、`--force-update`、`-p` / `--path`、`--dry-run`。 |
660
+ | `translate-ui [--locale <code>] [--force] [--dry-run] [-j <n>]` | 仅翻译 UI 字符串。`--force`:重新翻译每个语言区域的所有条目(忽略现有翻译)。`--dry-run`:不写入文件,不调用 API。`-j`:最大并行语言区域数。需要 `features.translateUIStrings`。 |
661
+ | `export-ui-xliff [-l <codes>] [-o <dir>] [--untranslated-only] [--dry-run]` | 将 `strings.json` 导出为 XLIFF 2.0 格式(每个目标语言区域一个 `.xliff`)。`-o` / `--output-dir`:输出目录(默认:与目录文件同目录)。`--untranslated-only`:仅导出缺少该语言区域翻译的单元。只读操作;不调用 API。 |
662
+ | `sync …` | 启用时先提取,然后进行 UI 翻译,接着当 `features.translateSVG` 和 `config.svg` 设置后执行 `translate-svg`,最后进行文档翻译——除非通过 `--no-ui`、`--no-svg` 或 `--no-docs` 跳过。共享标志:`-l`、`-p`、`--dry-run`、`-j`、`-b`(仅限文档批处理)、`--force` / `--force-update`(仅限文档;文档运行时互斥)。 |
663
+ | `status` | 显示每个文件 × 语言区域的 Markdown 翻译状态(无 `--locale` 过滤;语言区域来自配置)。 |
664
+ | `cleanup [--dry-run] [--no-backup] [--backup <path>]` | 首先运行 `sync --force-update`(提取、UI、SVG、文档),然后移除陈旧的段落行(null `last_hit_at` / 空文件路径);删除其解析后源路径在磁盘上不存在的 `file_tracking` 行;移除其 `filepath` 元数据指向缺失文件的翻译行。记录三个计数(陈旧、孤立的 `file_tracking`、孤立的翻译)。除非使用 `--no-backup`,否则在缓存目录下创建带时间戳的 SQLite 备份。 |
665
+ | `editor [-p <port>] [--no-open]` | 启动本地 Web 编辑器以编辑缓存、`strings.json` 和术语表 CSV。`--no-open`:不自动打开默认浏览器。<br><br>**注意:** 如果在缓存编辑器中编辑了条目,必须运行 `sync --force-update` 才能将更新后的缓存条目重写到输出文件中。此外,如果源文本之后发生变化,手动编辑的内容将会丢失,因为会生成新的缓存密钥。 |
666
+ | `glossary-generate [-o <path>]` | 生成一个空的 `glossary-user.csv` 模板。`-o`:覆盖输出路径(默认:来自配置的 `glossary.userGlossary`,或 `glossary-user.csv`)。 |
667
+
668
+ 所有命令都接受 `-c <path>` 来指定非默认配置文件,`-v` 用于详细输出,以及 `-w` / `--write-logs [path]` 将控制台输出记录到日志文件(默认路径:在根 `cacheDir` 下)。
669
+
670
+ ---
671
+
672
+ ## 环境变量
673
+
674
+ | 变量 | 描述 |
675
+ | ------------------------ | -------------------------------------------------------- |
676
+ | `OPENROUTER_API_KEY` | **必需。** 您的 OpenRouter API 密钥。 |
677
+ | `OPENROUTER_BASE_URL` | 覆盖 API 基础 URL。 |
678
+ | `I18N_SOURCE_LOCALE` | 在运行时覆盖 `sourceLocale`。 |
679
+ | `I18N_TARGET_LOCALES` | 逗号分隔的区域代码,用于覆盖 `targetLocales`。 |
680
+ | `I18N_LOG_LEVEL` | 日志级别(`debug`,`info`,`warn`,`error`,`silent`)。 |
681
+ | `NO_COLOR` | 当 `1` 时,禁用日志输出中的 ANSI 颜色。 |
682
+ | `I18N_LOG_SESSION_MAX` | 每个日志会话保留的最大行数(默认 `5000`)。 |