@babelize/engine 0.0.0-stage → 0.1.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 (246) 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/index.d.ts +54 -0
  11. package/dist/index.js +126 -0
  12. package/dist/injection/InjectionStrategy.d.ts +62 -0
  13. package/dist/injection/InjectionStrategy.js +2 -0
  14. package/dist/injection/StrategyFactory.d.ts +4 -0
  15. package/dist/injection/StrategyFactory.js +29 -0
  16. package/dist/injection/django/django-template-injector.d.ts +29 -0
  17. package/dist/injection/django/django-template-injector.js +216 -0
  18. package/dist/injection/html/html-injector.d.ts +11 -0
  19. package/dist/injection/html/html-injector.js +173 -0
  20. package/dist/injection/strategies/NextIntlStrategy.d.ts +25 -0
  21. package/dist/injection/strategies/NextIntlStrategy.js +141 -0
  22. package/dist/injection/strategies/ReactI18nextStrategy.d.ts +19 -0
  23. package/dist/injection/strategies/ReactI18nextStrategy.js +116 -0
  24. package/dist/injection/strategies/VanillaStrategy.d.ts +11 -0
  25. package/dist/injection/strategies/VanillaStrategy.js +73 -0
  26. package/dist/injection/strategies/VueI18nStrategy.d.ts +11 -0
  27. package/dist/injection/strategies/VueI18nStrategy.js +84 -0
  28. package/dist/local/formatter.d.ts +26 -0
  29. package/dist/local/formatter.js +105 -0
  30. package/dist/local/git.d.ts +23 -0
  31. package/dist/local/git.js +136 -0
  32. package/dist/local/hosted-provider.d.ts +68 -0
  33. package/dist/local/hosted-provider.js +215 -0
  34. package/dist/local/index.d.ts +23 -0
  35. package/dist/local/index.js +75 -0
  36. package/dist/local/provider.d.ts +56 -0
  37. package/dist/local/provider.js +119 -0
  38. package/dist/local/pseudo-provider.d.ts +18 -0
  39. package/dist/local/pseudo-provider.js +207 -0
  40. package/dist/local/report.d.ts +177 -0
  41. package/dist/local/report.js +186 -0
  42. package/dist/local/translate-core.d.ts +215 -0
  43. package/dist/local/translate-core.js +461 -0
  44. package/dist/local/translate-messages.d.ts +49 -0
  45. package/dist/local/translate-messages.js +91 -0
  46. package/dist/local/workspace-copy.d.ts +91 -0
  47. package/dist/local/workspace-copy.js +355 -0
  48. package/dist/modules/extractor/extract.d.ts +20 -0
  49. package/dist/modules/extractor/extract.js +701 -0
  50. package/dist/modules/extractor/index.d.ts +2 -0
  51. package/dist/modules/extractor/index.js +63 -0
  52. package/dist/modules/finalization/django-finalize.d.ts +13 -0
  53. package/dist/modules/finalization/django-finalize.js +275 -0
  54. package/dist/modules/finalization/finalize.d.ts +36 -0
  55. package/dist/modules/finalization/finalize.js +717 -0
  56. package/dist/modules/finalization/index.d.ts +1 -0
  57. package/dist/modules/finalization/index.js +5 -0
  58. package/dist/modules/finalization/nextjs-finalize.d.ts +37 -0
  59. package/dist/modules/finalization/nextjs-finalize.js +329 -0
  60. package/dist/modules/finalization/static-runtime.d.ts +9 -0
  61. package/dist/modules/finalization/static-runtime.js +261 -0
  62. package/dist/modules/format-injection/index.d.ts +19 -0
  63. package/dist/modules/format-injection/index.js +272 -0
  64. package/dist/modules/format-injection/inject-formats.d.ts +31 -0
  65. package/dist/modules/format-injection/inject-formats.js +208 -0
  66. package/dist/modules/glossary/glossary.d.ts +9 -0
  67. package/dist/modules/glossary/glossary.js +27 -0
  68. package/dist/modules/glossary/index.d.ts +3 -0
  69. package/dist/modules/glossary/index.js +8 -0
  70. package/dist/modules/glossary/masker.d.ts +23 -0
  71. package/dist/modules/glossary/masker.js +56 -0
  72. package/dist/modules/glossary/types.d.ts +22 -0
  73. package/dist/modules/glossary/types.js +2 -0
  74. package/dist/modules/injection/index.d.ts +1 -0
  75. package/dist/modules/injection/index.js +6 -0
  76. package/dist/modules/injection/inject.d.ts +29 -0
  77. package/dist/modules/injection/inject.js +1066 -0
  78. package/dist/modules/keygen/index.d.ts +1 -0
  79. package/dist/modules/keygen/index.js +11 -0
  80. package/dist/modules/keygen/keygen.d.ts +4 -0
  81. package/dist/modules/keygen/keygen.js +116 -0
  82. package/dist/modules/keygen/locale-data.d.ts +7 -0
  83. package/dist/modules/keygen/locale-data.js +73 -0
  84. package/dist/modules/keygen/public.d.ts +2 -0
  85. package/dist/modules/keygen/public.js +12 -0
  86. package/dist/modules/locale-builder/index.d.ts +1 -0
  87. package/dist/modules/locale-builder/index.js +7 -0
  88. package/dist/modules/locale-builder/locales.d.ts +13 -0
  89. package/dist/modules/locale-builder/locales.js +107 -0
  90. package/dist/modules/locale-builder/po.d.ts +33 -0
  91. package/dist/modules/locale-builder/po.js +131 -0
  92. package/dist/modules/lockfile/hasher.d.ts +7 -0
  93. package/dist/modules/lockfile/hasher.js +13 -0
  94. package/dist/modules/lockfile/index.d.ts +3 -0
  95. package/dist/modules/lockfile/index.js +21 -0
  96. package/dist/modules/lockfile/lockfile-manager.d.ts +90 -0
  97. package/dist/modules/lockfile/lockfile-manager.js +482 -0
  98. package/dist/modules/lockfile/schema.d.ts +154 -0
  99. package/dist/modules/lockfile/schema.js +45 -0
  100. package/dist/modules/parser/index.d.ts +2 -0
  101. package/dist/modules/parser/index.js +9 -0
  102. package/dist/modules/summary/index.d.ts +1 -0
  103. package/dist/modules/summary/index.js +7 -0
  104. package/dist/modules/summary/summary.d.ts +9 -0
  105. package/dist/modules/summary/summary.js +195 -0
  106. package/dist/modules/sync-detector/index.d.ts +2 -0
  107. package/dist/modules/sync-detector/index.js +7 -0
  108. package/dist/modules/sync-detector/reporter.d.ts +3 -0
  109. package/dist/modules/sync-detector/reporter.js +16 -0
  110. package/dist/modules/sync-detector/sync-detector.d.ts +39 -0
  111. package/dist/modules/sync-detector/sync-detector.js +374 -0
  112. package/dist/modules/translator/batch-optimizer.d.ts +12 -0
  113. package/dist/modules/translator/batch-optimizer.js +30 -0
  114. package/dist/modules/translator/client/byok.provider.d.ts +64 -0
  115. package/dist/modules/translator/client/byok.provider.js +319 -0
  116. package/dist/modules/translator/client/direct-translator.client.d.ts +12 -0
  117. package/dist/modules/translator/client/direct-translator.client.js +71 -0
  118. package/dist/modules/translator/client/gemini.provider.d.ts +1 -0
  119. package/dist/modules/translator/client/gemini.provider.js +110 -0
  120. package/dist/modules/translator/client/groq.provider.d.ts +1 -0
  121. package/dist/modules/translator/client/groq.provider.js +111 -0
  122. package/dist/modules/translator/client/request.types.d.ts +1 -0
  123. package/dist/modules/translator/client/request.types.js +2 -0
  124. package/dist/modules/translator/client/retry.d.ts +29 -0
  125. package/dist/modules/translator/client/retry.js +94 -0
  126. package/dist/modules/translator/client/test-utils/mock.client.d.ts +31 -0
  127. package/dist/modules/translator/client/test-utils/mock.client.js +59 -0
  128. package/dist/modules/translator/client/translator.interface.d.ts +1 -0
  129. package/dist/modules/translator/client/translator.interface.js +2 -0
  130. package/dist/modules/translator/language-codes/errors.d.ts +10 -0
  131. package/dist/modules/translator/language-codes/errors.js +18 -0
  132. package/dist/modules/translator/language-codes/flores-map.d.ts +1 -0
  133. package/dist/modules/translator/language-codes/flores-map.js +107 -0
  134. package/dist/modules/translator/language-codes/index.d.ts +11 -0
  135. package/dist/modules/translator/language-codes/index.js +24 -0
  136. package/dist/modules/translator/language-codes/language-mapper.d.ts +25 -0
  137. package/dist/modules/translator/language-codes/language-mapper.js +132 -0
  138. package/dist/modules/translator/language-codes/rtl-languages.d.ts +2 -0
  139. package/dist/modules/translator/language-codes/rtl-languages.js +9 -0
  140. package/dist/modules/translator/memory/memory-matcher.d.ts +8 -0
  141. package/dist/modules/translator/memory/memory-matcher.js +218 -0
  142. package/dist/modules/translator/memory/types.d.ts +22 -0
  143. package/dist/modules/translator/memory/types.js +2 -0
  144. package/dist/modules/translator/translate.d.ts +38 -0
  145. package/dist/modules/translator/translate.js +932 -0
  146. package/dist/modules/translator/translator.d.ts +5 -0
  147. package/dist/modules/translator/translator.js +46 -0
  148. package/dist/modules/vue/migrate.d.ts +13 -0
  149. package/dist/modules/vue/migrate.js +40 -0
  150. package/dist/modules/vue/script-inject.d.ts +16 -0
  151. package/dist/modules/vue/script-inject.js +115 -0
  152. package/dist/modules/vue/sfc.d.ts +11 -0
  153. package/dist/modules/vue/sfc.js +51 -0
  154. package/dist/modules/vue/template-rewrite.d.ts +25 -0
  155. package/dist/modules/vue/template-rewrite.js +76 -0
  156. package/dist/modules/vue/template-scan.d.ts +29 -0
  157. package/dist/modules/vue/template-scan.js +264 -0
  158. package/dist/modules/workspace/index.d.ts +1 -0
  159. package/dist/modules/workspace/index.js +5 -0
  160. package/dist/modules/workspace/validate.d.ts +13 -0
  161. package/dist/modules/workspace/validate.js +298 -0
  162. package/dist/orchestrator.d.ts +8 -0
  163. package/dist/orchestrator.js +616 -0
  164. package/dist/providers/default-translation-provider.d.ts +20 -0
  165. package/dist/providers/default-translation-provider.js +30 -0
  166. package/dist/providers/index.d.ts +2 -0
  167. package/dist/providers/index.js +21 -0
  168. package/dist/providers/types.d.ts +145 -0
  169. package/dist/providers/types.js +2 -0
  170. package/dist/qa/check-project.d.ts +49 -0
  171. package/dist/qa/check-project.js +150 -0
  172. package/dist/qa/locale-files.d.ts +44 -0
  173. package/dist/qa/locale-files.js +159 -0
  174. package/dist/qa/locale-qa.d.ts +61 -0
  175. package/dist/qa/locale-qa.js +325 -0
  176. package/dist/qa/review.d.ts +43 -0
  177. package/dist/qa/review.js +175 -0
  178. package/dist/runtime/config.d.ts +8 -0
  179. package/dist/runtime/config.js +36 -0
  180. package/dist/runtime/index.d.ts +3 -0
  181. package/dist/runtime/index.js +8 -0
  182. package/dist/runtime/types.d.ts +363 -0
  183. package/dist/runtime/types.js +38 -0
  184. package/dist/types.d.ts +134 -0
  185. package/dist/types.js +12 -0
  186. package/dist/utils/ast-helpers.d.ts +57 -0
  187. package/dist/utils/ast-helpers.js +608 -0
  188. package/dist/utils/cache-keys.d.ts +8 -0
  189. package/dist/utils/cache-keys.js +18 -0
  190. package/dist/utils/circuit-breaker.d.ts +43 -0
  191. package/dist/utils/circuit-breaker.js +119 -0
  192. package/dist/utils/concurrent-map.d.ts +4 -0
  193. package/dist/utils/concurrent-map.js +21 -0
  194. package/dist/utils/detect-format.d.ts +5 -0
  195. package/dist/utils/detect-format.js +125 -0
  196. package/dist/utils/dtl-mask.d.ts +25 -0
  197. package/dist/utils/dtl-mask.js +58 -0
  198. package/dist/utils/extraction-options.d.ts +23 -0
  199. package/dist/utils/extraction-options.js +23 -0
  200. package/dist/utils/file-utils.d.ts +33 -0
  201. package/dist/utils/file-utils.js +262 -0
  202. package/dist/utils/glossary-match.d.ts +9 -0
  203. package/dist/utils/glossary-match.js +25 -0
  204. package/dist/utils/html-extractor.d.ts +8 -0
  205. package/dist/utils/html-extractor.js +317 -0
  206. package/dist/utils/i18next-icu.d.ts +38 -0
  207. package/dist/utils/i18next-icu.js +159 -0
  208. package/dist/utils/icu-message.d.ts +65 -0
  209. package/dist/utils/icu-message.js +346 -0
  210. package/dist/utils/icu-spans.d.ts +10 -0
  211. package/dist/utils/icu-spans.js +126 -0
  212. package/dist/utils/jsx-message.d.ts +93 -0
  213. package/dist/utils/jsx-message.js +621 -0
  214. package/dist/utils/key-usage.d.ts +35 -0
  215. package/dist/utils/key-usage.js +247 -0
  216. package/dist/utils/logger.d.ts +24 -0
  217. package/dist/utils/logger.js +33 -0
  218. package/dist/utils/metadata.d.ts +80 -0
  219. package/dist/utils/metadata.js +255 -0
  220. package/dist/utils/module-constants.d.ts +30 -0
  221. package/dist/utils/module-constants.js +265 -0
  222. package/dist/utils/not-injected.d.ts +6 -0
  223. package/dist/utils/not-injected.js +36 -0
  224. package/dist/utils/placeholder-protection.d.ts +57 -0
  225. package/dist/utils/placeholder-protection.js +136 -0
  226. package/dist/utils/pseudo-markers.d.ts +9 -0
  227. package/dist/utils/pseudo-markers.js +16 -0
  228. package/dist/utils/py-extractor.d.ts +14 -0
  229. package/dist/utils/py-extractor.js +285 -0
  230. package/dist/utils/string-utils.d.ts +52 -0
  231. package/dist/utils/string-utils.js +270 -0
  232. package/dist/utils/translation-quality.d.ts +34 -0
  233. package/dist/utils/translation-quality.js +156 -0
  234. package/dist/utils/translation-sanitizer.d.ts +10 -0
  235. package/dist/utils/translation-sanitizer.js +57 -0
  236. package/dist/utils/translator-caller.d.ts +32 -0
  237. package/dist/utils/translator-caller.js +217 -0
  238. package/dist/utils/translator-load-balancer.d.ts +73 -0
  239. package/dist/utils/translator-load-balancer.js +293 -0
  240. package/dist/utils/ui-copy.d.ts +32 -0
  241. package/dist/utils/ui-copy.js +126 -0
  242. package/dist/utils/workspace.d.ts +14 -0
  243. package/dist/utils/workspace.js +89 -0
  244. package/package.json +64 -4
  245. package/schemas/translate-report.schema.json +297 -0
  246. package/README.md +0 -3
@@ -0,0 +1,374 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectSyncIssues = detectSyncIssues;
37
+ const path = __importStar(require("path"));
38
+ const fs = __importStar(require("fs-extra"));
39
+ const logger_1 = require("../../utils/logger");
40
+ const extract_1 = require("../extractor/extract");
41
+ const lockfile_manager_1 = require("../lockfile/lockfile-manager");
42
+ const types_1 = require("../../runtime/types");
43
+ const extraction_options_1 = require("../../utils/extraction-options");
44
+ /**
45
+ * Extraction options the project's engine runs use (sentence grouping, ICU
46
+ * plurals, data-literal rules), so a sync check compares like with like and
47
+ * doesn't report every sentence as new.
48
+ */
49
+ async function runExtractionOptions(workspacePath, options) {
50
+ let packageJson;
51
+ try {
52
+ packageJson = (await fs.readJson(path.join(workspacePath, 'package.json'))) ?? undefined;
53
+ }
54
+ catch {
55
+ packageJson = undefined;
56
+ }
57
+ const deps = { ...packageJson?.dependencies, ...packageJson?.devDependencies };
58
+ const framework = options.framework ?? (deps.next ? 'nextjs' : deps.react ? 'react' : deps.vue ? 'vue' : 'unknown');
59
+ const workspace = {
60
+ path: workspacePath, framework, sourceDirs: ['.'], hasLocalesDir: false, existingLocales: [], publicDir: 'public',
61
+ localeFormat: 'json', packageJson,
62
+ };
63
+ const icu = await (0, extraction_options_1.resolveIcuOptions)(workspace, !!options.llmTranslator);
64
+ return { framework, sentences: icu.sentences, plurals: icu.plurals };
65
+ }
66
+ // ─────────────────────────────────────────────────────────────
67
+ // File filtering
68
+ // ─────────────────────────────────────────────────────────────
69
+ /**
70
+ * Patterns for files that should never trigger sync detection.
71
+ * Covers config, test, build, and documentation files.
72
+ */
73
+ const SYNC_IGNORE_PATTERNS = [
74
+ ...types_1.EXCLUDED_PATTERNS.map(p => typeof p === 'string' ? new RegExp(p.replace(/\*/g, '.*')) : p),
75
+ // Additional patterns specific to sync detection
76
+ /\.test\.(ts|tsx|js|jsx)$/,
77
+ /\.spec\.(ts|tsx|js|jsx)$/,
78
+ /\.stories\.(ts|tsx|js|jsx)$/,
79
+ /__tests__\//,
80
+ /__mocks__\//,
81
+ /\.config\.(ts|js|mjs|cjs)$/,
82
+ /jest\.config/,
83
+ /playwright\.config/,
84
+ /next\.config/,
85
+ /tailwind\.config/,
86
+ /postcss\.config/,
87
+ /tsconfig/,
88
+ /\.eslintrc/,
89
+ /\.prettierrc/,
90
+ /\.env/,
91
+ /package\.json$/,
92
+ /package-lock\.json$/,
93
+ /bun\.lock$/,
94
+ /yarn\.lock$/,
95
+ /README\.md$/i,
96
+ /CHANGELOG\.md$/i,
97
+ /LICENSE/i,
98
+ /\.d\.ts$/,
99
+ /\.map$/,
100
+ /dist\//,
101
+ /build\//,
102
+ /\.next\//,
103
+ /node_modules\//,
104
+ // Locale files themselves should not trigger detection
105
+ /locales?\//,
106
+ /i18n\//,
107
+ /translations?\//,
108
+ /lang\//,
109
+ /messages\//,
110
+ ];
111
+ /**
112
+ * Check if a file path matches supported source extensions
113
+ * and is not excluded by ignore patterns.
114
+ */
115
+ function isSourceFile(filePath) {
116
+ const ext = path.extname(filePath).toLowerCase();
117
+ if (!types_1.SUPPORTED_EXTENSIONS.includes(ext))
118
+ return false;
119
+ for (const pattern of SYNC_IGNORE_PATTERNS) {
120
+ if (pattern.test(filePath))
121
+ return false;
122
+ }
123
+ return true;
124
+ }
125
+ // ─────────────────────────────────────────────────────────────
126
+ // Core detection
127
+ // ─────────────────────────────────────────────────────────────
128
+ /**
129
+ * Detect sync issues by comparing extracted strings from changed files
130
+ * against the lockfile and locale files.
131
+ *
132
+ * @param projectId The project identifier (for lockfile lookup)
133
+ * @param workspacePath Absolute path to the project workspace
134
+ * @param changedFiles List of file paths changed in the push (relative to repo root)
135
+ * @param commitSha The commit SHA that triggered this check
136
+ * @param targetLocales Target locales configured for this project
137
+ */
138
+ async function detectSyncIssues(projectId, workspacePath, changedFiles, commitSha, targetLocales, options = {}) {
139
+ const now = new Date().toISOString();
140
+ const filesChecked = [];
141
+ const filesSkipped = [];
142
+ // 1. Filter changed files to only source files
143
+ const sourceFiles = [];
144
+ for (const file of changedFiles) {
145
+ if (isSourceFile(file)) {
146
+ sourceFiles.push(file);
147
+ }
148
+ else {
149
+ filesSkipped.push(file);
150
+ }
151
+ }
152
+ if (sourceFiles.length === 0) {
153
+ logger_1.engineLogger.info('No source files in push, skipping sync check', {
154
+ projectId,
155
+ commitSha,
156
+ totalChanged: changedFiles.length,
157
+ });
158
+ return {
159
+ newStrings: [],
160
+ changedStrings: [],
161
+ missingLocaleKeys: [],
162
+ totalIssues: 0,
163
+ commitSha,
164
+ checkedAt: now,
165
+ filesChecked: [],
166
+ filesSkipped,
167
+ };
168
+ }
169
+ // 2. Extract strings from changed source files, the way runs do
170
+ const extractedStrings = {};
171
+ const extractOptions = await runExtractionOptions(workspacePath, options);
172
+ for (const relativePath of sourceFiles) {
173
+ const absolutePath = path.join(workspacePath, relativePath);
174
+ try {
175
+ if (!(await fs.pathExists(absolutePath))) {
176
+ filesSkipped.push(relativePath);
177
+ continue;
178
+ }
179
+ const content = await fs.readFile(absolutePath, 'utf-8');
180
+ const { strings } = await (0, extract_1.extractStringsFromFile)(content, relativePath, false, extractOptions);
181
+ for (const s of strings) {
182
+ // Use the text as both key and value since we're doing pre-keygen detection
183
+ const key = s.id || `${relativePath}:${s.line}:${s.text}`;
184
+ extractedStrings[key] = s.text;
185
+ }
186
+ filesChecked.push(relativePath);
187
+ }
188
+ catch (err) {
189
+ logger_1.engineLogger.warn('Failed to extract strings from file during sync check', {
190
+ projectId,
191
+ file: relativePath,
192
+ error: err instanceof Error ? err.message : String(err),
193
+ });
194
+ filesSkipped.push(relativePath);
195
+ }
196
+ }
197
+ if (Object.keys(extractedStrings).length === 0) {
198
+ logger_1.engineLogger.info('No translatable strings found in changed files', {
199
+ projectId,
200
+ commitSha,
201
+ filesChecked: filesChecked.length,
202
+ });
203
+ return {
204
+ newStrings: [],
205
+ changedStrings: [],
206
+ missingLocaleKeys: [],
207
+ totalIssues: 0,
208
+ commitSha,
209
+ checkedAt: now,
210
+ filesChecked,
211
+ filesSkipped,
212
+ };
213
+ }
214
+ // 3. Load lockfile and partition keys
215
+ const lockfile = await (0, lockfile_manager_1.loadLockfile)(projectId);
216
+ const partition = (0, lockfile_manager_1.partitionKeys)(lockfile, extractedStrings, targetLocales);
217
+ // 4. Build detection results
218
+ const newStrings = partition.newKeys.map(k => ({
219
+ key: k.key,
220
+ value: k.value,
221
+ file: k.key.split(':')[0] || 'unknown',
222
+ line: parseInt(k.key.split(':')[1] || '0', 10),
223
+ }));
224
+ const changedStrings = partition.changed.map(k => ({
225
+ key: k.key,
226
+ value: k.value,
227
+ file: k.key.split(':')[0] || 'unknown',
228
+ line: parseInt(k.key.split(':')[1] || '0', 10),
229
+ }));
230
+ // 5. Detect missing locale keys
231
+ const missingLocaleKeys = partition.missingLocales.map(k => ({
232
+ key: k.key,
233
+ presentIn: targetLocales.filter(loc => !k.missingLocales.includes(loc)),
234
+ missingFrom: k.missingLocales,
235
+ }));
236
+ // 6. Also check locale files on disk for cross-locale missing keys
237
+ const diskMissing = await detectMissingLocaleKeysFromDisk(workspacePath, targetLocales);
238
+ for (const m of diskMissing) {
239
+ // Only add if not already detected via lockfile
240
+ if (!missingLocaleKeys.some(existing => existing.key === m.key)) {
241
+ missingLocaleKeys.push(m);
242
+ }
243
+ }
244
+ const totalIssues = newStrings.length + changedStrings.length + missingLocaleKeys.length;
245
+ logger_1.engineLogger.info('Sync detection complete', {
246
+ projectId,
247
+ commitSha,
248
+ newStrings: newStrings.length,
249
+ changedStrings: changedStrings.length,
250
+ missingLocaleKeys: missingLocaleKeys.length,
251
+ totalIssues,
252
+ filesChecked: filesChecked.length,
253
+ });
254
+ return {
255
+ newStrings,
256
+ changedStrings,
257
+ missingLocaleKeys,
258
+ totalIssues,
259
+ commitSha,
260
+ checkedAt: now,
261
+ filesChecked,
262
+ filesSkipped,
263
+ };
264
+ }
265
+ // ─────────────────────────────────────────────────────────────
266
+ // Locale file disk check
267
+ // ─────────────────────────────────────────────────────────────
268
+ /**
269
+ * Scan locale JSON files on disk to detect keys present in the
270
+ * base locale (en) but missing from target locales.
271
+ */
272
+ async function detectMissingLocaleKeysFromDisk(workspacePath, targetLocales) {
273
+ const missing = [];
274
+ // Common locale directory patterns
275
+ const localeDirCandidates = [
276
+ 'locales',
277
+ 'locale',
278
+ 'i18n',
279
+ 'translations',
280
+ 'lang',
281
+ 'messages',
282
+ 'public/locales',
283
+ 'src/locales',
284
+ 'src/i18n',
285
+ ];
286
+ let localeDir = null;
287
+ for (const candidate of localeDirCandidates) {
288
+ const dirPath = path.join(workspacePath, candidate);
289
+ if (await fs.pathExists(dirPath)) {
290
+ localeDir = dirPath;
291
+ break;
292
+ }
293
+ }
294
+ if (!localeDir)
295
+ return missing;
296
+ try {
297
+ // Load the base locale file (en.json)
298
+ const baseLocalePath = path.join(localeDir, 'en.json');
299
+ if (!(await fs.pathExists(baseLocalePath)))
300
+ return missing;
301
+ const baseLocale = await fs.readJson(baseLocalePath);
302
+ const baseKeys = flattenKeys(baseLocale);
303
+ // Check each target locale
304
+ for (const locale of targetLocales) {
305
+ if (locale === 'en')
306
+ continue;
307
+ const localeFilePath = path.join(localeDir, `${locale}.json`);
308
+ if (!(await fs.pathExists(localeFilePath))) {
309
+ // Entire locale file missing — all keys are missing
310
+ for (const key of Object.keys(baseKeys)) {
311
+ const existing = missing.find(m => m.key === key);
312
+ if (existing) {
313
+ if (!existing.missingFrom.includes(locale)) {
314
+ existing.missingFrom.push(locale);
315
+ }
316
+ }
317
+ else {
318
+ missing.push({
319
+ key,
320
+ presentIn: ['en'],
321
+ missingFrom: [locale],
322
+ });
323
+ }
324
+ }
325
+ continue;
326
+ }
327
+ const localeData = await fs.readJson(localeFilePath);
328
+ const localeKeys = flattenKeys(localeData);
329
+ for (const key of Object.keys(baseKeys)) {
330
+ if (!(key in localeKeys)) {
331
+ const existing = missing.find(m => m.key === key);
332
+ if (existing) {
333
+ if (!existing.missingFrom.includes(locale)) {
334
+ existing.missingFrom.push(locale);
335
+ }
336
+ if (!existing.presentIn.includes('en')) {
337
+ existing.presentIn.push('en');
338
+ }
339
+ }
340
+ else {
341
+ missing.push({
342
+ key,
343
+ presentIn: ['en'],
344
+ missingFrom: [locale],
345
+ });
346
+ }
347
+ }
348
+ }
349
+ }
350
+ }
351
+ catch (err) {
352
+ logger_1.engineLogger.warn('Failed to check locale files on disk', {
353
+ error: err instanceof Error ? err.message : String(err),
354
+ });
355
+ }
356
+ return missing;
357
+ }
358
+ /**
359
+ * Flatten a nested JSON object into a flat key map.
360
+ * e.g., { "a": { "b": "hello" } } → { "a.b": "hello" }
361
+ */
362
+ function flattenKeys(obj, prefix = '') {
363
+ const result = {};
364
+ for (const [key, value] of Object.entries(obj)) {
365
+ const fullKey = prefix ? `${prefix}.${key}` : key;
366
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
367
+ Object.assign(result, flattenKeys(value, fullKey));
368
+ }
369
+ else {
370
+ result[fullKey] = String(value);
371
+ }
372
+ }
373
+ return result;
374
+ }
@@ -0,0 +1,12 @@
1
+ import type { TranslationUnit } from '../../types';
2
+ export interface Batch {
3
+ units: TranslationUnit[];
4
+ }
5
+ /**
6
+ * Groups translation units into batches that respect a rough token limit and
7
+ * ensures we do NOT make parallel HTTP calls to the HF Space.
8
+ */
9
+ export declare function createBatches(units: TranslationUnit[], options?: {
10
+ maxBatchSize?: number;
11
+ maxCharsPerBatch?: number;
12
+ }): Batch[];
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBatches = createBatches;
4
+ /**
5
+ * Groups translation units into batches that respect a rough token limit and
6
+ * ensures we do NOT make parallel HTTP calls to the HF Space.
7
+ */
8
+ function createBatches(units, options = {}) {
9
+ const maxBatchSize = options.maxBatchSize ?? 50;
10
+ const maxCharsPerBatch = options.maxCharsPerBatch ?? 2000;
11
+ const batches = [];
12
+ let current = [];
13
+ let currentChars = 0;
14
+ for (const unit of units) {
15
+ const textLen = unit.sourceText.length;
16
+ const wouldExceedSize = current.length >= maxBatchSize;
17
+ const wouldExceedChars = currentChars + textLen > maxCharsPerBatch;
18
+ if (current.length > 0 && (wouldExceedSize || wouldExceedChars)) {
19
+ batches.push({ units: current });
20
+ current = [];
21
+ currentChars = 0;
22
+ }
23
+ current.push(unit);
24
+ currentChars += textLen;
25
+ }
26
+ if (current.length > 0) {
27
+ batches.push({ units: current });
28
+ }
29
+ return batches;
30
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * BYOK (Bring Your Own Key) Translation Provider
3
+ *
4
+ * Direct integration with Anthropic, OpenAI, Gemini, Groq and OpenRouter for
5
+ * users who provide their own API keys. Zero Babelize AI credits consumed.
6
+ *
7
+ * Contract: returns a translation only for entries the model actually
8
+ * translated. An entry it skipped or garbled is simply absent from the
9
+ * response, and the engine reports it as a failed key — never as the source
10
+ * text.
11
+ */
12
+ import type { HFTranslateRequest, HFTranslateResponse } from './request.types';
13
+ import type { TranslatorClient } from './translator.interface';
14
+ import type { ByokProvider } from '@babelize/types';
15
+ interface BYOKProviderConfig {
16
+ provider: ByokProvider;
17
+ apiKey: string;
18
+ /** Optional model override */
19
+ model?: string;
20
+ }
21
+ interface PromptEntry {
22
+ id: string;
23
+ text: string;
24
+ context?: string;
25
+ element?: string;
26
+ note?: string;
27
+ }
28
+ export declare class BYOKTranslatorClient implements TranslatorClient {
29
+ private readonly config;
30
+ constructor(config: BYOKProviderConfig);
31
+ health(): Promise<boolean>;
32
+ warmup(): Promise<boolean>;
33
+ translate(req: HFTranslateRequest, signal?: AbortSignal): Promise<HFTranslateResponse>;
34
+ private getModel;
35
+ private getBaseUrl;
36
+ private getProviderDisplayName;
37
+ /** Anthropic: force a tool call so the output is schema-shaped JSON. */
38
+ private callAnthropic;
39
+ private callOpenAICompatible;
40
+ private httpError;
41
+ private formatApiError;
42
+ private redactApiKey;
43
+ /**
44
+ * Estimate token count and cost for a translation request
45
+ */
46
+ static estimateCost(entries: Array<{
47
+ value: string;
48
+ }>, provider: ByokProvider): {
49
+ estimatedTokens: number;
50
+ estimatedCost: number | null;
51
+ currency: 'USD';
52
+ };
53
+ }
54
+ export declare function buildSystemPrompt(req: HFTranslateRequest, entries: PromptEntry[]): string;
55
+ /**
56
+ * Pull `{translations:[{id, value}]}` out of a tool input object or a text
57
+ * reply (tolerating code fences and surrounding prose). Returns only entries
58
+ * whose id was requested and whose value is a string; nothing is invented.
59
+ */
60
+ export declare function parseTranslations(content: unknown, requestedIds: Set<string>): Array<{
61
+ key: string;
62
+ value: string;
63
+ }>;
64
+ export {};