@babelize/engine 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/LICENSE +202 -0
  2. package/dist/BabelizeEngine.d.ts +12 -0
  3. package/dist/BabelizeEngine.js +47 -0
  4. package/dist/analysis/analyze.d.ts +106 -0
  5. package/dist/analysis/analyze.js +323 -0
  6. package/dist/config/env.d.ts +9 -0
  7. package/dist/config/env.js +16 -0
  8. package/dist/config.d.ts +74 -0
  9. package/dist/config.js +47 -0
  10. package/dist/detection/stack.d.ts +42 -0
  11. package/dist/detection/stack.js +277 -0
  12. package/dist/django/catalog.d.ts +92 -0
  13. package/dist/django/catalog.js +677 -0
  14. package/dist/django/extract.d.ts +20 -0
  15. package/dist/django/extract.js +79 -0
  16. package/dist/django/inject-file.d.ts +6 -0
  17. package/dist/django/inject-file.js +52 -0
  18. package/dist/django/inject.d.ts +11 -0
  19. package/dist/django/inject.js +205 -0
  20. package/dist/django/keys.d.ts +21 -0
  21. package/dist/django/keys.js +63 -0
  22. package/dist/django/locale-names.d.ts +13 -0
  23. package/dist/django/locale-names.js +60 -0
  24. package/dist/django/mo-file.d.ts +6 -0
  25. package/dist/django/mo-file.js +60 -0
  26. package/dist/django/plural-forms.d.ts +34 -0
  27. package/dist/django/plural-forms.js +216 -0
  28. package/dist/django/po-file.d.ts +38 -0
  29. package/dist/django/po-file.js +273 -0
  30. package/dist/django/python-extract.d.ts +34 -0
  31. package/dist/django/python-extract.js +619 -0
  32. package/dist/django/python-lexer.d.ts +65 -0
  33. package/dist/django/python-lexer.js +398 -0
  34. package/dist/django/template-extract.d.ts +32 -0
  35. package/dist/django/template-extract.js +680 -0
  36. package/dist/django/types.d.ts +49 -0
  37. package/dist/django/types.js +15 -0
  38. package/dist/index.d.ts +59 -0
  39. package/dist/index.js +140 -0
  40. package/dist/injection/InjectionStrategy.d.ts +62 -0
  41. package/dist/injection/InjectionStrategy.js +2 -0
  42. package/dist/injection/StrategyFactory.d.ts +4 -0
  43. package/dist/injection/StrategyFactory.js +29 -0
  44. package/dist/injection/html/html-injector.d.ts +11 -0
  45. package/dist/injection/html/html-injector.js +173 -0
  46. package/dist/injection/strategies/NextIntlStrategy.d.ts +25 -0
  47. package/dist/injection/strategies/NextIntlStrategy.js +141 -0
  48. package/dist/injection/strategies/ReactI18nextStrategy.d.ts +19 -0
  49. package/dist/injection/strategies/ReactI18nextStrategy.js +116 -0
  50. package/dist/injection/strategies/VanillaStrategy.d.ts +11 -0
  51. package/dist/injection/strategies/VanillaStrategy.js +73 -0
  52. package/dist/injection/strategies/VueI18nStrategy.d.ts +11 -0
  53. package/dist/injection/strategies/VueI18nStrategy.js +84 -0
  54. package/dist/local/formatter.d.ts +26 -0
  55. package/dist/local/formatter.js +105 -0
  56. package/dist/local/git.d.ts +23 -0
  57. package/dist/local/git.js +136 -0
  58. package/dist/local/hosted-provider.d.ts +68 -0
  59. package/dist/local/hosted-provider.js +215 -0
  60. package/dist/local/index.d.ts +23 -0
  61. package/dist/local/index.js +75 -0
  62. package/dist/local/provider.d.ts +56 -0
  63. package/dist/local/provider.js +119 -0
  64. package/dist/local/pseudo-provider.d.ts +18 -0
  65. package/dist/local/pseudo-provider.js +208 -0
  66. package/dist/local/report.d.ts +177 -0
  67. package/dist/local/report.js +186 -0
  68. package/dist/local/translate-core.d.ts +218 -0
  69. package/dist/local/translate-core.js +499 -0
  70. package/dist/local/translate-messages.d.ts +49 -0
  71. package/dist/local/translate-messages.js +89 -0
  72. package/dist/local/workspace-copy.d.ts +91 -0
  73. package/dist/local/workspace-copy.js +355 -0
  74. package/dist/modules/extractor/extract.d.ts +20 -0
  75. package/dist/modules/extractor/extract.js +709 -0
  76. package/dist/modules/extractor/index.d.ts +2 -0
  77. package/dist/modules/extractor/index.js +63 -0
  78. package/dist/modules/finalization/django-finalize.d.ts +28 -0
  79. package/dist/modules/finalization/django-finalize.js +412 -0
  80. package/dist/modules/finalization/finalize.d.ts +38 -0
  81. package/dist/modules/finalization/finalize.js +745 -0
  82. package/dist/modules/finalization/index.d.ts +1 -0
  83. package/dist/modules/finalization/index.js +5 -0
  84. package/dist/modules/finalization/nextjs-finalize.d.ts +40 -0
  85. package/dist/modules/finalization/nextjs-finalize.js +328 -0
  86. package/dist/modules/finalization/static-runtime.d.ts +9 -0
  87. package/dist/modules/finalization/static-runtime.js +261 -0
  88. package/dist/modules/finalization/writes.d.ts +29 -0
  89. package/dist/modules/finalization/writes.js +78 -0
  90. package/dist/modules/format-injection/index.d.ts +19 -0
  91. package/dist/modules/format-injection/index.js +273 -0
  92. package/dist/modules/format-injection/inject-formats.d.ts +31 -0
  93. package/dist/modules/format-injection/inject-formats.js +208 -0
  94. package/dist/modules/glossary/index.d.ts +2 -0
  95. package/dist/modules/glossary/index.js +16 -0
  96. package/dist/modules/glossary/rules.d.ts +81 -0
  97. package/dist/modules/glossary/rules.js +244 -0
  98. package/dist/modules/glossary/types.d.ts +22 -0
  99. package/dist/modules/glossary/types.js +2 -0
  100. package/dist/modules/injection/index.d.ts +1 -0
  101. package/dist/modules/injection/index.js +6 -0
  102. package/dist/modules/injection/inject.d.ts +29 -0
  103. package/dist/modules/injection/inject.js +1050 -0
  104. package/dist/modules/keygen/index.d.ts +1 -0
  105. package/dist/modules/keygen/index.js +11 -0
  106. package/dist/modules/keygen/keygen.d.ts +4 -0
  107. package/dist/modules/keygen/keygen.js +123 -0
  108. package/dist/modules/keygen/locale-data.d.ts +7 -0
  109. package/dist/modules/keygen/locale-data.js +73 -0
  110. package/dist/modules/keygen/public.d.ts +2 -0
  111. package/dist/modules/keygen/public.js +12 -0
  112. package/dist/modules/locale-builder/index.d.ts +1 -0
  113. package/dist/modules/locale-builder/index.js +7 -0
  114. package/dist/modules/locale-builder/locales.d.ts +13 -0
  115. package/dist/modules/locale-builder/locales.js +130 -0
  116. package/dist/modules/lockfile/hasher.d.ts +7 -0
  117. package/dist/modules/lockfile/hasher.js +13 -0
  118. package/dist/modules/lockfile/index.d.ts +3 -0
  119. package/dist/modules/lockfile/index.js +22 -0
  120. package/dist/modules/lockfile/lockfile-manager.d.ts +99 -0
  121. package/dist/modules/lockfile/lockfile-manager.js +497 -0
  122. package/dist/modules/lockfile/schema.d.ts +196 -0
  123. package/dist/modules/lockfile/schema.js +56 -0
  124. package/dist/modules/parser/index.d.ts +2 -0
  125. package/dist/modules/parser/index.js +9 -0
  126. package/dist/modules/summary/index.d.ts +1 -0
  127. package/dist/modules/summary/index.js +7 -0
  128. package/dist/modules/summary/summary.d.ts +9 -0
  129. package/dist/modules/summary/summary.js +195 -0
  130. package/dist/modules/sync-detector/index.d.ts +2 -0
  131. package/dist/modules/sync-detector/index.js +7 -0
  132. package/dist/modules/sync-detector/reporter.d.ts +3 -0
  133. package/dist/modules/sync-detector/reporter.js +16 -0
  134. package/dist/modules/sync-detector/sync-detector.d.ts +39 -0
  135. package/dist/modules/sync-detector/sync-detector.js +374 -0
  136. package/dist/modules/translator/batch-optimizer.d.ts +12 -0
  137. package/dist/modules/translator/batch-optimizer.js +30 -0
  138. package/dist/modules/translator/client/byok.provider.d.ts +64 -0
  139. package/dist/modules/translator/client/byok.provider.js +319 -0
  140. package/dist/modules/translator/client/direct-translator.client.d.ts +12 -0
  141. package/dist/modules/translator/client/direct-translator.client.js +71 -0
  142. package/dist/modules/translator/client/gemini.provider.d.ts +1 -0
  143. package/dist/modules/translator/client/gemini.provider.js +110 -0
  144. package/dist/modules/translator/client/groq.provider.d.ts +1 -0
  145. package/dist/modules/translator/client/groq.provider.js +111 -0
  146. package/dist/modules/translator/client/request.types.d.ts +1 -0
  147. package/dist/modules/translator/client/request.types.js +2 -0
  148. package/dist/modules/translator/client/retry.d.ts +29 -0
  149. package/dist/modules/translator/client/retry.js +94 -0
  150. package/dist/modules/translator/client/test-utils/mock.client.d.ts +31 -0
  151. package/dist/modules/translator/client/test-utils/mock.client.js +59 -0
  152. package/dist/modules/translator/client/translator.interface.d.ts +1 -0
  153. package/dist/modules/translator/client/translator.interface.js +2 -0
  154. package/dist/modules/translator/icu-branches.d.ts +138 -0
  155. package/dist/modules/translator/icu-branches.js +393 -0
  156. package/dist/modules/translator/language-codes/errors.d.ts +10 -0
  157. package/dist/modules/translator/language-codes/errors.js +18 -0
  158. package/dist/modules/translator/language-codes/flores-map.d.ts +1 -0
  159. package/dist/modules/translator/language-codes/flores-map.js +107 -0
  160. package/dist/modules/translator/language-codes/index.d.ts +11 -0
  161. package/dist/modules/translator/language-codes/index.js +24 -0
  162. package/dist/modules/translator/language-codes/language-mapper.d.ts +25 -0
  163. package/dist/modules/translator/language-codes/language-mapper.js +132 -0
  164. package/dist/modules/translator/language-codes/rtl-languages.d.ts +2 -0
  165. package/dist/modules/translator/language-codes/rtl-languages.js +9 -0
  166. package/dist/modules/translator/memory/memory-matcher.d.ts +8 -0
  167. package/dist/modules/translator/memory/memory-matcher.js +218 -0
  168. package/dist/modules/translator/memory/types.d.ts +22 -0
  169. package/dist/modules/translator/memory/types.js +2 -0
  170. package/dist/modules/translator/translate.d.ts +74 -0
  171. package/dist/modules/translator/translate.js +1047 -0
  172. package/dist/modules/vue/migrate.d.ts +13 -0
  173. package/dist/modules/vue/migrate.js +40 -0
  174. package/dist/modules/vue/script-inject.d.ts +16 -0
  175. package/dist/modules/vue/script-inject.js +115 -0
  176. package/dist/modules/vue/sfc.d.ts +11 -0
  177. package/dist/modules/vue/sfc.js +51 -0
  178. package/dist/modules/vue/template-rewrite.d.ts +25 -0
  179. package/dist/modules/vue/template-rewrite.js +76 -0
  180. package/dist/modules/vue/template-scan.d.ts +29 -0
  181. package/dist/modules/vue/template-scan.js +264 -0
  182. package/dist/modules/workspace/index.d.ts +1 -0
  183. package/dist/modules/workspace/index.js +5 -0
  184. package/dist/modules/workspace/validate.d.ts +13 -0
  185. package/dist/modules/workspace/validate.js +333 -0
  186. package/dist/orchestrator.d.ts +13 -0
  187. package/dist/orchestrator.js +727 -0
  188. package/dist/providers/default-translation-provider.d.ts +20 -0
  189. package/dist/providers/default-translation-provider.js +30 -0
  190. package/dist/providers/index.d.ts +2 -0
  191. package/dist/providers/index.js +21 -0
  192. package/dist/providers/types.d.ts +153 -0
  193. package/dist/providers/types.js +2 -0
  194. package/dist/qa/check-project.d.ts +50 -0
  195. package/dist/qa/check-project.js +193 -0
  196. package/dist/qa/gettext-files.d.ts +15 -0
  197. package/dist/qa/gettext-files.js +115 -0
  198. package/dist/qa/locale-files.d.ts +44 -0
  199. package/dist/qa/locale-files.js +159 -0
  200. package/dist/qa/locale-qa.d.ts +61 -0
  201. package/dist/qa/locale-qa.js +342 -0
  202. package/dist/qa/review.d.ts +43 -0
  203. package/dist/qa/review.js +175 -0
  204. package/dist/runtime/config.d.ts +8 -0
  205. package/dist/runtime/config.js +37 -0
  206. package/dist/runtime/index.d.ts +3 -0
  207. package/dist/runtime/index.js +8 -0
  208. package/dist/runtime/types.d.ts +427 -0
  209. package/dist/runtime/types.js +38 -0
  210. package/dist/types.d.ts +134 -0
  211. package/dist/types.js +12 -0
  212. package/dist/utils/ast-helpers.d.ts +57 -0
  213. package/dist/utils/ast-helpers.js +608 -0
  214. package/dist/utils/cache-keys.d.ts +8 -0
  215. package/dist/utils/cache-keys.js +18 -0
  216. package/dist/utils/circuit-breaker.d.ts +43 -0
  217. package/dist/utils/circuit-breaker.js +119 -0
  218. package/dist/utils/concurrent-map.d.ts +4 -0
  219. package/dist/utils/concurrent-map.js +21 -0
  220. package/dist/utils/detect-format.d.ts +5 -0
  221. package/dist/utils/detect-format.js +125 -0
  222. package/dist/utils/extraction-options.d.ts +29 -0
  223. package/dist/utils/extraction-options.js +33 -0
  224. package/dist/utils/file-utils.d.ts +33 -0
  225. package/dist/utils/file-utils.js +267 -0
  226. package/dist/utils/glossary-match.d.ts +9 -0
  227. package/dist/utils/glossary-match.js +25 -0
  228. package/dist/utils/html-extractor.d.ts +8 -0
  229. package/dist/utils/html-extractor.js +317 -0
  230. package/dist/utils/i18next-icu.d.ts +38 -0
  231. package/dist/utils/i18next-icu.js +159 -0
  232. package/dist/utils/icu-message.d.ts +65 -0
  233. package/dist/utils/icu-message.js +346 -0
  234. package/dist/utils/icu-spans.d.ts +10 -0
  235. package/dist/utils/icu-spans.js +126 -0
  236. package/dist/utils/jsx-message.d.ts +93 -0
  237. package/dist/utils/jsx-message.js +628 -0
  238. package/dist/utils/key-usage.d.ts +35 -0
  239. package/dist/utils/key-usage.js +247 -0
  240. package/dist/utils/logger.d.ts +24 -0
  241. package/dist/utils/logger.js +33 -0
  242. package/dist/utils/metadata.d.ts +82 -0
  243. package/dist/utils/metadata.js +261 -0
  244. package/dist/utils/module-constants.d.ts +30 -0
  245. package/dist/utils/module-constants.js +265 -0
  246. package/dist/utils/not-injected.d.ts +6 -0
  247. package/dist/utils/not-injected.js +38 -0
  248. package/dist/utils/placeholder-protection.d.ts +74 -0
  249. package/dist/utils/placeholder-protection.js +181 -0
  250. package/dist/utils/pseudo-markers.d.ts +9 -0
  251. package/dist/utils/pseudo-markers.js +16 -0
  252. package/dist/utils/string-utils.d.ts +52 -0
  253. package/dist/utils/string-utils.js +270 -0
  254. package/dist/utils/translation-quality.d.ts +34 -0
  255. package/dist/utils/translation-quality.js +156 -0
  256. package/dist/utils/translation-sanitizer.d.ts +11 -0
  257. package/dist/utils/translation-sanitizer.js +71 -0
  258. package/dist/utils/translator-caller.d.ts +32 -0
  259. package/dist/utils/translator-caller.js +217 -0
  260. package/dist/utils/translator-load-balancer.d.ts +73 -0
  261. package/dist/utils/translator-load-balancer.js +297 -0
  262. package/dist/utils/ui-copy.d.ts +32 -0
  263. package/dist/utils/ui-copy.js +126 -0
  264. package/dist/utils/untranslated.d.ts +15 -0
  265. package/dist/utils/untranslated.js +34 -0
  266. package/dist/utils/workspace.d.ts +14 -0
  267. package/dist/utils/workspace.js +89 -0
  268. package/package.json +64 -4
  269. package/schemas/translate-report.schema.json +297 -0
  270. package/README.md +0 -3
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReviewError = void 0;
4
+ exports.reviewLocaleChanges = reviewLocaleChanges;
5
+ /**
6
+ * Review the locale changes in a git diff: QA only the entries that changed
7
+ * (same checks as checkProjectLocales), plus source strings whose
8
+ * translations weren't updated and new source keys a locale is missing.
9
+ * Read-only; compares the working tree with HEAD or a base ref.
10
+ */
11
+ const child_process_1 = require("child_process");
12
+ const util_1 = require("util");
13
+ const git_1 = require("../local/git");
14
+ const check_project_1 = require("./check-project");
15
+ const locale_files_1 = require("./locale-files");
16
+ const locale_qa_1 = require("./locale-qa");
17
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
18
+ class ReviewError extends Error {
19
+ constructor(message, code) {
20
+ super(message);
21
+ this.code = code;
22
+ this.name = 'ReviewError';
23
+ }
24
+ }
25
+ exports.ReviewError = ReviewError;
26
+ /** Git's empty tree: the base of a repository without commits. */
27
+ const EMPTY_TREE = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
28
+ async function git(cwd, args) {
29
+ const { stdout } = await execFileAsync('git', args, { cwd, maxBuffer: 256 * 1024 * 1024 });
30
+ return stdout;
31
+ }
32
+ async function resolveBase(projectDir, base) {
33
+ if (base !== undefined) {
34
+ // A ref, never an option (`--output=…`) or a range.
35
+ if (!/^[\w./@^~{}-]+$/.test(base) || base.startsWith('-') || base.includes('..')) {
36
+ throw new ReviewError(`"${base}" is not a valid git ref.`, 'bad_base');
37
+ }
38
+ try {
39
+ const sha = (await git(projectDir, ['rev-parse', '--verify', '--quiet', '--end-of-options', `${base}^{commit}`])).trim();
40
+ return { ref: sha, label: base };
41
+ }
42
+ catch {
43
+ throw new ReviewError(`Unknown git ref "${base}". Use a branch, tag or commit (e.g. main, origin/main, HEAD~1).`, 'bad_base');
44
+ }
45
+ }
46
+ try {
47
+ const sha = (await git(projectDir, ['rev-parse', '--verify', '--quiet', 'HEAD^{commit}'])).trim();
48
+ return { ref: sha, label: 'HEAD' };
49
+ }
50
+ catch {
51
+ return { ref: EMPTY_TREE, label: 'empty repository' };
52
+ }
53
+ }
54
+ const lines = (out) => out.split('\n').map((l) => l.trim()).filter(Boolean);
55
+ function diffKeys(now, old) {
56
+ const added = [];
57
+ const changed = [];
58
+ const removed = [];
59
+ for (const [k, v] of Object.entries(now)) {
60
+ if (!(k in old))
61
+ added.push(k);
62
+ else if (old[k] !== v)
63
+ changed.push(k);
64
+ }
65
+ for (const k of Object.keys(old))
66
+ if (!(k in now))
67
+ removed.push(k);
68
+ return { added, changed, removed };
69
+ }
70
+ function clip(text, max = 60) {
71
+ const one = text.replace(/\s+/g, ' ').trim();
72
+ return one.length > max ? `${one.slice(0, max - 1)}…` : one;
73
+ }
74
+ async function reviewLocaleChanges(projectDir, options = {}) {
75
+ try {
76
+ await (0, git_1.assertGitRepo)(projectDir);
77
+ }
78
+ catch (err) {
79
+ const code = err.code === 'git_missing' ? 'git_missing' : 'not_a_repo';
80
+ throw new ReviewError(err.message, code);
81
+ }
82
+ const { ref, label } = await resolveBase(projectDir, options.base);
83
+ // Paths relative to projectDir (--relative also limits the diff to it).
84
+ const changed = new Set([
85
+ ...lines(await git(projectDir, ['diff', '--name-only', '--relative', ref, '--', '.'])),
86
+ ...lines(await git(projectDir, ['ls-files', '--others', '--exclude-standard', '--', '.'])),
87
+ ]);
88
+ const readOld = async (rel) => {
89
+ if (ref === EMPTY_TREE)
90
+ return null;
91
+ try {
92
+ return await git(projectDir, ['show', `${ref}:./${rel}`]);
93
+ }
94
+ catch {
95
+ return null; // new file
96
+ }
97
+ };
98
+ const config = await (0, check_project_1.readProjectLocaleConfig)(projectDir);
99
+ // Sets from the working tree and the base, so deleted locale files count.
100
+ const basePaths = ref === EMPTY_TREE ? [] : lines(await git(projectDir, ['ls-tree', '-r', '--name-only', ref, '--', '.']));
101
+ const nowSets = await (0, locale_files_1.findLocaleFileSets)(projectDir, { ignoreDirs: options.ignoreDirs });
102
+ const sets = (0, locale_files_1.groupLocaleFiles)([...nowSets.flatMap((st) => Object.values(st.files).flat()), ...basePaths], { ignoreDirs: options.ignoreDirs });
103
+ const sourceLocale = options.sourceLocale ?? config.sourceLocale ?? 'en';
104
+ const glossary = options.glossary ?? config.glossary;
105
+ const localeFiles = new Set(sets.flatMap((st) => Object.values(st.files).flat()));
106
+ const reviews = [];
107
+ for (const set of sets) {
108
+ const setFiles = Object.values(set.files).flat();
109
+ if (!set.files[sourceLocale] || !setFiles.some((f) => changed.has(f)))
110
+ continue;
111
+ const srcNow = (await (0, locale_files_1.loadLocale)(projectDir, set, sourceLocale)).messages;
112
+ const srcOld = (await (0, locale_files_1.loadLocale)(projectDir, set, sourceLocale, readOld)).messages;
113
+ const source = diffKeys(srcNow, srcOld);
114
+ const locales = [];
115
+ const targets = Object.keys(set.files)
116
+ .filter((l) => l !== sourceLocale && (!options.locales?.length || options.locales.includes(l)))
117
+ .sort();
118
+ for (const locale of targets) {
119
+ const nowLoaded = await (0, locale_files_1.loadLocale)(projectDir, set, locale);
120
+ const tgtNow = nowLoaded.messages;
121
+ const tgtOld = (await (0, locale_files_1.loadLocale)(projectDir, set, locale, readOld)).messages;
122
+ const fileDeleted = set.files[locale].every((f) => changed.has(f)) && Object.keys(tgtNow).length === 0 && nowLoaded.errors.length === 0 && Object.keys(tgtOld).length > 0;
123
+ const changedTarget = Object.keys(tgtNow).filter((k) => tgtNow[k] !== tgtOld[k]);
124
+ // Deleted translations whose source key still exists.
125
+ const removedTarget = Object.keys(tgtOld).filter((k) => !(k in tgtNow) && k in srcNow);
126
+ const removed = new Set(removedTarget);
127
+ const keys = fileDeleted
128
+ ? new Set(Object.keys(srcNow))
129
+ : new Set([...changedTarget, ...source.added, ...source.changed, ...source.removed.filter((k) => k in tgtNow)].filter((k) => !removed.has(k)));
130
+ if (keys.size === 0 && removedTarget.length === 0)
131
+ continue;
132
+ const report = (0, locale_qa_1.checkLocalePair)(srcNow, tgtNow, locale, { sourceLocale, glossary, keys: [...keys] });
133
+ if (fileDeleted) {
134
+ report.issues.unshift({
135
+ locale,
136
+ key: '*',
137
+ code: 'translation_removed',
138
+ severity: 'error',
139
+ message: `The ${locale} locale file was deleted: all ${Object.keys(srcNow).length} source keys are now missing in ${locale}.`,
140
+ });
141
+ }
142
+ else {
143
+ for (const key of removedTarget) {
144
+ report.issues.push({
145
+ locale,
146
+ key,
147
+ code: 'translation_removed',
148
+ severity: 'error',
149
+ message: `Translation "${clip(tgtOld[key])}" was deleted but the source still has this key; the app falls back to the source text.`,
150
+ });
151
+ }
152
+ }
153
+ for (const key of source.changed) {
154
+ if (tgtNow[key] !== undefined && tgtNow[key] === tgtOld[key]) {
155
+ report.issues.push({
156
+ locale,
157
+ key,
158
+ code: 'stale_translation',
159
+ severity: 'warning',
160
+ message: `Source changed from "${clip(srcOld[key])}" to "${clip(srcNow[key])}" but this translation wasn't updated.`,
161
+ });
162
+ }
163
+ }
164
+ locales.push({ ...(0, locale_qa_1.tallyLocaleIssues)(report), changedKeys: changedTarget.length, removedKeys: removedTarget.length, fileDeleted });
165
+ }
166
+ reviews.push({ dir: set.dir, layout: set.layout, sourceLocale, source, locales });
167
+ }
168
+ const changedLocaleFiles = [...changed].filter((f) => localeFiles.has(f)).sort();
169
+ return {
170
+ base: label,
171
+ changedLocaleFiles,
172
+ changedOtherFiles: changed.size - changedLocaleFiles.length,
173
+ sets: reviews,
174
+ };
175
+ }
@@ -0,0 +1,8 @@
1
+ import type { EngineConfigV2 } from './types';
2
+ /**
3
+ * Runtime config validation for the backend engine (v2).
4
+ *
5
+ * This mirrors the existing backend contract used by workers/controllers.
6
+ * (A separate Zod schema exists for the internal v2 modules.)
7
+ */
8
+ export declare function validateEngineConfigV2(config: Partial<EngineConfigV2>): EngineConfigV2;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateEngineConfigV2 = validateEngineConfigV2;
4
+ /**
5
+ * Runtime config validation for the backend engine (v2).
6
+ *
7
+ * This mirrors the existing backend contract used by workers/controllers.
8
+ * (A separate Zod schema exists for the internal v2 modules.)
9
+ */
10
+ function validateEngineConfigV2(config) {
11
+ if (!config.workspacePath) {
12
+ throw new Error('workspacePath is required');
13
+ }
14
+ const confidenceThreshold = config.confidenceThreshold ?? 80;
15
+ if (confidenceThreshold < 0 || confidenceThreshold > 100) {
16
+ throw new Error(`confidenceThreshold must be between 0 and 100, got ${confidenceThreshold}`);
17
+ }
18
+ return {
19
+ workspacePath: config.workspacePath,
20
+ inputType: config.inputType || 'github',
21
+ sourceLocale: config.sourceLocale || 'en',
22
+ targetLocales: config.targetLocales || [],
23
+ framework: config.framework,
24
+ confidenceThreshold,
25
+ dryRun: config.dryRun ?? false,
26
+ forceTranslation: config.forceTranslation ?? false,
27
+ onProgress: config.onProgress,
28
+ signal: config.signal,
29
+ projectId: config.projectId,
30
+ filesToProcess: config.filesToProcess,
31
+ byok: config.byok,
32
+ providers: config.providers,
33
+ formatConfig: config.formatConfig,
34
+ staticRuntime: config.staticRuntime,
35
+ installDependencies: config.installDependencies,
36
+ };
37
+ }
@@ -0,0 +1,3 @@
1
+ export type { EngineConfigV2, EngineResultV2 } from './types';
2
+ export { validateEngineConfigV2 } from './config';
3
+ export { runEngineV2 } from '../orchestrator';
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runEngineV2 = exports.validateEngineConfigV2 = void 0;
4
+ var config_1 = require("./config");
5
+ Object.defineProperty(exports, "validateEngineConfigV2", { enumerable: true, get: function () { return config_1.validateEngineConfigV2; } });
6
+ // `runEngineV2` is implemented in `../orchestrator` as the single wiring point.
7
+ var orchestrator_1 = require("../orchestrator");
8
+ Object.defineProperty(exports, "runEngineV2", { enumerable: true, get: function () { return orchestrator_1.runEngineV2; } });
@@ -0,0 +1,427 @@
1
+ /**
2
+ * Engine v2 runtime types (backend-facing).
3
+ *
4
+ * This file intentionally keeps the operational contract that the backend
5
+ * workers/controllers already rely on (stats/locales/injections/diffs), while
6
+ * allowing the v2 engine to add richer metrics needed for Target.md.
7
+ */
8
+ import type { ByokProvider, FormatLocaleConfig, Framework } from '@babelize/types';
9
+ import type { EngineProviders, TranslationFailure } from '../providers/types';
10
+ import type { EngineLogger } from '../utils/logger';
11
+ export interface EngineConfigV2 {
12
+ /** Path to the workspace directory */
13
+ workspacePath: string;
14
+ /** Source input type */
15
+ inputType: 'github' | 'zip';
16
+ /** Source locale code (e.g., 'en') */
17
+ sourceLocale: string;
18
+ /** Target locale codes (e.g., ['fr', 'de', 'es']) */
19
+ targetLocales: string[];
20
+ /** Detected or specified framework */
21
+ framework?: Framework;
22
+ /** Minimum confidence level for AST injection (0-100) */
23
+ confidenceThreshold: number;
24
+ /** If true, don't write any files - just return what would be changed */
25
+ dryRun: boolean;
26
+ /** Force translation even if locale files exist */
27
+ forceTranslation?: boolean;
28
+ /** Project ID for scoping lockfile/memory */
29
+ projectId?: string;
30
+ /**
31
+ * Directory holding `.babelize/<projectId>/babelize-lock.json`. Default:
32
+ * $BABELIZE_DATA_DIR, else the process cwd (the cloud backend). Local runs
33
+ * pass their temp copy, so concurrent runs never share global state.
34
+ */
35
+ dataDir?: string;
36
+ /** Logger for this run only (default: the one set with setEngineLogger). */
37
+ logger?: EngineLogger;
38
+ /**
39
+ * Stops the run cooperatively: checked at every progress report, so the
40
+ * pipeline ends at the next phase or translation batch after it fires. The
41
+ * result then has `aborted` set to the signal's reason (e.g. 'cancelled',
42
+ * 'timeout').
43
+ */
44
+ signal?: AbortSignal;
45
+ /** Callback for progress updates */
46
+ onProgress?: (message: string, step?: string, status?: 'running' | 'success' | 'error' | 'warning') => void;
47
+ /**
48
+ * Infrastructure the engine uses (translator, translation memory, project
49
+ * glossary/brand voice, cache). Anything omitted is simply not used.
50
+ */
51
+ providers?: EngineProviders;
52
+ /** BYOK (Bring Your Own Key) translation provider config */
53
+ byok?: {
54
+ provider: ByokProvider;
55
+ apiKey: string;
56
+ /** Optional model override (used by OpenRouter, Groq, etc.) */
57
+ model?: string;
58
+ };
59
+ /**
60
+ * Run the package manager when finalization adds i18n dependencies to
61
+ * package.json. Default true; local CLI runs pass false and tell the user
62
+ * to install instead.
63
+ */
64
+ installDependencies?: boolean;
65
+ /** Optional: only scan and process these specific files (e.g. for sync-triggered runs) */
66
+ filesToProcess?: string[];
67
+ /** Format localization config. null/undefined = skip format localization entirely. */
68
+ formatConfig?: FormatLocaleConfig;
69
+ /**
70
+ * What the run's translator can do. `icu`: translate ICU plural messages
71
+ * (an LLM provider). Defaults to true with BYOK or an injected translator,
72
+ * false for the NLLB service — then JSX sentences keep a plain `{count}`.
73
+ */
74
+ capabilities?: {
75
+ icu?: boolean;
76
+ };
77
+ /** Static HTML runtime options */
78
+ staticRuntime?: {
79
+ injectSwitcher?: boolean;
80
+ wrapMixedContent?: boolean;
81
+ };
82
+ }
83
+ export interface WorkspaceInfo {
84
+ path: string;
85
+ framework: Framework;
86
+ sourceDirs: string[];
87
+ hasLocalesDir: boolean;
88
+ existingLocales: string[];
89
+ publicDir: string;
90
+ packageJson?: Record<string, any>;
91
+ /** Output locale file format (e.g., 'json' for React, 'po' for Django) */
92
+ localeFormat: 'json' | 'po' | 'arb';
93
+ /**
94
+ * Django: the project root (the directory holding manage.py, i.e. BASE_DIR),
95
+ * relative to the workspace. For gettext projects `publicDir` is the locale
96
+ * directory (LOCALE_PATHS), relative to the workspace.
97
+ */
98
+ djangoRoot?: string;
99
+ /** Format localization config propagated from EngineConfigV2 */
100
+ formatConfig?: FormatLocaleConfig;
101
+ /** Static HTML runtime options propagated from EngineConfigV2 */
102
+ staticRuntime?: {
103
+ injectSwitcher?: boolean;
104
+ wrapMixedContent?: boolean;
105
+ };
106
+ }
107
+ /**
108
+ * Frameworks that the engine pipeline will actively process.
109
+ * Runtime detection may produce other identifiers (e.g. 'unknown');
110
+ * those are validated against this list and rejected if not supported.
111
+ *
112
+ * @deprecated Import SUPPORTED_FRAMEWORKS from @babelize/types instead
113
+ */
114
+ export type FormatClass = 'currency' | 'date' | 'number' | 'unknown';
115
+ export interface DetectedFormat {
116
+ class: FormatClass;
117
+ rawValue: string;
118
+ originalString: string;
119
+ confidence: number;
120
+ detectedCurrency?: string;
121
+ dateOptions?: Intl.DateTimeFormatOptions;
122
+ dateSource?: string;
123
+ numberOptions?: Intl.NumberFormatOptions;
124
+ }
125
+ export interface ExtractedString {
126
+ text: string;
127
+ file: string;
128
+ line: number;
129
+ column: number;
130
+ context: 'jsx-text' | 'jsx-attr' | 'string-literal' | 'template-literal' | 'hardcoded-date' | 'hardcoded-currency' | 'format-string';
131
+ id: string;
132
+ variables?: string[];
133
+ parentContext?: string;
134
+ siblingGroupId?: string;
135
+ detectedFormat?: DetectedFormat;
136
+ /**
137
+ * A whole JSX sentence (text + `{expressions}` + inline elements) as one ICU
138
+ * message with named placeholders, rich-text tags and possibly a plural.
139
+ * Keys are named from its words only, and the message carries its own
140
+ * plural forms (no `_one`/`_other` key variants).
141
+ */
142
+ icuMessage?: boolean;
143
+ /**
144
+ * Source offsets of every node this id stands for. Injection replaces
145
+ * exactly these nodes (not others with the same text on the same line).
146
+ */
147
+ offsets?: number[];
148
+ /**
149
+ * gettext message (Django): `text` is the msgid. Keys are derived from the
150
+ * message itself, and the PO catalogue — not a key — is what code looks up.
151
+ */
152
+ gettext?: GettextInfo;
153
+ }
154
+ export interface GettextInfo {
155
+ msgid: string;
156
+ msgidPlural?: string;
157
+ msgctxt?: string;
158
+ /** PO flags (python-format, python-brace-format). */
159
+ flags: string[];
160
+ /** Translator comments from the code (`# Translators: …`). */
161
+ comments: string[];
162
+ /** Already marked in the code (gettext call / trans tag); otherwise the injector wraps it. */
163
+ wrapped: boolean;
164
+ /**
165
+ * Translate a plural message as one ICU plural (an LLM can produce every
166
+ * CLDR category); otherwise singular and plural are translated separately.
167
+ */
168
+ icu?: boolean;
169
+ }
170
+ export interface ExtractionResult {
171
+ strings: ExtractedString[];
172
+ filesAnalyzed: string[];
173
+ skippedFiles: SkippedFile[];
174
+ warnings?: Warning[];
175
+ /** UI strings found but deliberately not extracted (e.g. module-level constants that are also data). */
176
+ notInjected?: NotInjectedString[];
177
+ }
178
+ export interface GeneratedKey {
179
+ key: string;
180
+ namespace: string;
181
+ value: string;
182
+ extractionId: string;
183
+ isPlural?: boolean;
184
+ pluralCategory?: 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
185
+ parentContext?: string;
186
+ extractionContext?: string;
187
+ /** gettext message this key translates, and which part of it (Django). */
188
+ gettext?: GettextInfo & {
189
+ form: 'single' | 'icu' | 'singular' | 'plural';
190
+ };
191
+ }
192
+ export interface KeyGenerationResult {
193
+ keys: GeneratedKey[];
194
+ duplicates: Array<{
195
+ original: string;
196
+ renamed: string;
197
+ }>;
198
+ }
199
+ export interface LocaleData {
200
+ [key: string]: string | LocaleData;
201
+ }
202
+ export interface LocaleFile {
203
+ locale: string;
204
+ path: string;
205
+ data: LocaleData;
206
+ isNew: boolean;
207
+ isRTL?: boolean;
208
+ /**
209
+ * Exact file content to write at `path` (e.g. a merged .po catalogue).
210
+ * When set, hosts write this instead of serializing `data` as JSON.
211
+ */
212
+ content?: string;
213
+ /**
214
+ * There is no file for this locale (the source language of a gettext
215
+ * project: the code itself is the catalogue). Hosts write nothing.
216
+ */
217
+ noFile?: boolean;
218
+ /** Further files generated for this locale (e.g. a compiled .mo next to the .po). */
219
+ extraFiles?: Array<{
220
+ path: string;
221
+ content: string;
222
+ encoding: 'utf8' | 'base64';
223
+ }>;
224
+ }
225
+ export interface LocaleHandlingResult {
226
+ sourceLocale: LocaleFile;
227
+ newKeysCount: number;
228
+ existingKeysCount: number;
229
+ }
230
+ export interface TranslationEntry {
231
+ key: string;
232
+ value: string;
233
+ confidence?: number;
234
+ engine?: string;
235
+ warnings?: string[];
236
+ parentContext?: string;
237
+ extractionContext?: string;
238
+ }
239
+ export interface TranslationRequest {
240
+ sourceLang: string;
241
+ targetLang: string;
242
+ entries: TranslationEntry[];
243
+ }
244
+ export interface TranslationResponse {
245
+ translations: TranslationEntry[];
246
+ metadata?: {
247
+ dictVersion?: string;
248
+ processingTimeMs?: number;
249
+ dictionaryHits?: number;
250
+ modelTranslations?: number;
251
+ };
252
+ }
253
+ export interface TranslationResult {
254
+ locales: LocaleFile[];
255
+ errors: Array<{
256
+ locale: string;
257
+ error: string;
258
+ }>;
259
+ /** Keys with no translation for a locale this run (not written, retried next run). */
260
+ failures?: TranslationFailure[];
261
+ }
262
+ export interface InjectionLocation {
263
+ file: string;
264
+ line: number;
265
+ column: number;
266
+ original: string;
267
+ replacement: string;
268
+ key: string;
269
+ confidence: number;
270
+ formatClass?: string;
271
+ }
272
+ export interface FileInjection {
273
+ file: string;
274
+ originalContent: string;
275
+ modifiedContent: string;
276
+ injections: InjectionLocation[];
277
+ importAdded: boolean;
278
+ hookAdded: boolean;
279
+ /** The run created this file (it was not in the project before), e.g. a generated i18n config. */
280
+ created?: boolean;
281
+ }
282
+ export interface InjectionResult {
283
+ modifiedFiles: FileInjection[];
284
+ skippedFiles: SkippedFile[];
285
+ totalInjections: number;
286
+ }
287
+ export interface SkippedFile {
288
+ file: string;
289
+ reason: 'syntax-error' | 'low-confidence' | 'already-translated' | 'binary' | 'excluded' | 'parse-error' | 'format-injection-failed' | 'format-ast-build-failed';
290
+ details?: string;
291
+ }
292
+ export interface Warning {
293
+ type: 'low-confidence' | 'duplicate-key' | 'complex-expression' | 'dynamic-string' | 'malformed-ast' | 'parse-warning' | 'hardcoded-currency' | 'hardcoded-date' | 'hardcoded-plural' | 'ambiguous-format' | 'overflow' | 'rtl';
294
+ file: string;
295
+ line?: number;
296
+ key?: string;
297
+ locale?: string;
298
+ severity?: 'warning' | 'critical';
299
+ dismissible?: boolean;
300
+ message: string;
301
+ }
302
+ export interface DiffMetadata {
303
+ file: string;
304
+ changeType: 'created' | 'modified';
305
+ linesAdded: number;
306
+ linesRemoved: number;
307
+ diff?: string;
308
+ }
309
+ export interface ProcessingStats {
310
+ filesScanned: number;
311
+ filesWithStrings: number;
312
+ stringsExtracted: number;
313
+ keysGenerated: number;
314
+ filesModified: number;
315
+ injectionsApplied: number;
316
+ localeFilesCreated: number;
317
+ processingTimeMs: number;
318
+ stringsNeedingTranslation?: number;
319
+ translationsFromMemory?: number;
320
+ translationsFromLockfile?: number;
321
+ translationsFromHF?: number;
322
+ /** True when engine ran with a project-scoped lockfile */
323
+ lockfileUsed?: boolean;
324
+ /** Keys sent to the translator (new + source-changed + missing locale) */
325
+ lockfileKeysTranslated?: number;
326
+ /** Keys whose source text changed vs the lockfile */
327
+ lockfileKeysChanged?: number;
328
+ /**
329
+ * (key, locale) translations the translator actually produced this run —
330
+ * the billable work. Excludes lockfile, translation-memory and cache reuse,
331
+ * and failed keys.
332
+ */
333
+ translatedThisRun?: number;
334
+ }
335
+ export interface NotInjectedString {
336
+ file: string;
337
+ key: string;
338
+ text: string;
339
+ reason: string;
340
+ }
341
+ export interface KeyUsageReport {
342
+ /**
343
+ * Source-locale keys that no code references any more (and weren't
344
+ * extracted this run). Not translated, not deleted.
345
+ */
346
+ unused: string[];
347
+ /**
348
+ * Files with translation calls whose key isn't a literal. When non-empty,
349
+ * every key is treated as live and `unused` is empty.
350
+ */
351
+ dynamicFiles: string[];
352
+ }
353
+ export interface EngineResultV2 {
354
+ success: boolean;
355
+ error?: string;
356
+ /** Set when the run was stopped through `signal`: the abort reason. */
357
+ aborted?: string;
358
+ /**
359
+ * Only with `aborted`: strings freshly translated (and written to the
360
+ * translation cache and TM) before the stop. A later run reuses them for
361
+ * free, so the caller bills them rather than refunding the whole run.
362
+ */
363
+ partialTranslations?: number;
364
+ /**
365
+ * Keys that could not be translated, per locale, with the reason. They are
366
+ * absent from the locale files and the lockfile, so the next run retries
367
+ * them. Never filled with source text.
368
+ */
369
+ translationFailures?: TranslationFailure[];
370
+ stats: ProcessingStats;
371
+ locales: LocaleFile[];
372
+ injections: InjectionResult;
373
+ warnings: Warning[];
374
+ skipped: SkippedFile[];
375
+ diffs: DiffMetadata[];
376
+ /**
377
+ * Human-readable notes from finalization (dependencies added to
378
+ * package.json, i18n config created, ...) that need the user's attention.
379
+ */
380
+ notes?: string[];
381
+ /** Which source keys are still used (lockfile runs only). */
382
+ keyUsage?: KeyUsageReport;
383
+ /**
384
+ * Strings that were extracted and got a key (and translations) but could
385
+ * not be wired into the code, so the literal is still there.
386
+ */
387
+ notInjected?: NotInjectedString[];
388
+ results?: {
389
+ source: Record<string, string>;
390
+ targets: Record<string, Record<string, string>>;
391
+ metadata?: Record<string, Record<string, any>>;
392
+ };
393
+ }
394
+ export interface Phase0Result {
395
+ workspace: WorkspaceInfo;
396
+ }
397
+ export interface Phase1Result {
398
+ extraction: ExtractionResult;
399
+ }
400
+ export interface Phase2Result {
401
+ keyGeneration: KeyGenerationResult;
402
+ }
403
+ export interface Phase3Result {
404
+ localeHandling: LocaleHandlingResult;
405
+ }
406
+ export interface Phase4Result {
407
+ translation: TranslationResult;
408
+ }
409
+ export interface Phase5Result {
410
+ injection: InjectionResult;
411
+ }
412
+ export interface Phase6Result {
413
+ stats: ProcessingStats;
414
+ warnings: Warning[];
415
+ diffs: DiffMetadata[];
416
+ }
417
+ export interface Phase7Result {
418
+ finalization: {
419
+ dependenciesInvalid: boolean;
420
+ configFileCreated: boolean;
421
+ entryPointModified: boolean;
422
+ warnings: string[];
423
+ };
424
+ }
425
+ export declare const SUPPORTED_EXTENSIONS: string[];
426
+ export declare const EXCLUDED_PATTERNS: string[];
427
+ export declare const JSX_TRANSLATABLE_ATTRS: string[];