@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,262 @@
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.clearFindFilesCache = clearFindFilesCache;
37
+ exports.readFile = readFile;
38
+ exports.writeFile = writeFile;
39
+ exports.pathExists = pathExists;
40
+ exports.isDirectory = isDirectory;
41
+ exports.findFiles = findFiles;
42
+ exports.getRelativePath = getRelativePath;
43
+ exports.readJsonFile = readJsonFile;
44
+ exports.writeJsonFile = writeJsonFile;
45
+ exports.localeExtension = localeExtension;
46
+ exports.localeFilePath = localeFilePath;
47
+ exports.ensureDir = ensureDir;
48
+ exports.copyFile = copyFile;
49
+ exports.deleteFile = deleteFile;
50
+ exports.readDir = readDir;
51
+ exports.concurrentMap = concurrentMap;
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ const string_utils_1 = require("./string-utils");
55
+ /**
56
+ * Engine v2 file utils.
57
+ *
58
+ * This is a direct carry-over of the v1 utilities to preserve behavior.
59
+ */
60
+ const findFilesCache = new Map();
61
+ function clearFindFilesCache() {
62
+ findFilesCache.clear();
63
+ }
64
+ function findFilesCacheKey(dir, extensions, excludePatterns) {
65
+ return `${dir}::${extensions.sort().join(',')}::${excludePatterns.sort().join(',')}`;
66
+ }
67
+ async function readFile(filePath) {
68
+ return fs.promises.readFile(filePath, 'utf-8');
69
+ }
70
+ async function writeFile(filePath, content, allowedBaseDir) {
71
+ const resolved = path.resolve(filePath);
72
+ if (allowedBaseDir && !resolved.startsWith(path.resolve(allowedBaseDir))) {
73
+ throw new Error(`Path traversal detected: ${filePath} is outside ${allowedBaseDir}`);
74
+ }
75
+ const dir = path.dirname(resolved);
76
+ await fs.promises.mkdir(dir, { recursive: true });
77
+ // Atomic write: write to tmp, then rename to prevent partial/corrupt files on crash
78
+ const tmpPath = resolved + '.tmp';
79
+ await fs.promises.writeFile(tmpPath, content, 'utf-8'); // codeql[js/http-to-file-access] — engine writes translation output to workspace; callers authenticate + authorize
80
+ await fs.promises.rename(tmpPath, resolved);
81
+ }
82
+ async function pathExists(filePath) {
83
+ try {
84
+ await fs.promises.access(filePath);
85
+ return true;
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ }
91
+ async function isDirectory(filePath) {
92
+ try {
93
+ const stats = await fs.promises.stat(filePath);
94
+ return stats.isDirectory();
95
+ }
96
+ catch {
97
+ return false;
98
+ }
99
+ }
100
+ async function findFiles(dir, extensions, excludePatterns = []) {
101
+ const key = findFilesCacheKey(dir, extensions, excludePatterns);
102
+ const cached = findFilesCache.get(key);
103
+ if (cached)
104
+ return cached;
105
+ const promise = findFilesUncached(dir, extensions, excludePatterns);
106
+ findFilesCache.set(key, promise);
107
+ return promise;
108
+ }
109
+ /** Exclusion names that usually mean generated output (but not inside SOURCE_TREE_DIRS). */
110
+ const OUTPUT_DIR_NAMES = new Set(['build', 'dist', 'coverage', 'lib', 'libs', 'vendor']);
111
+ /** Directories whose subdirectories are routes or components, never build output. */
112
+ const SOURCE_TREE_DIRS = new Set(['app', 'pages', 'components']);
113
+ function isInsideSourceTree(walkRoot, relativePath) {
114
+ const ancestors = [path.basename(walkRoot), ...relativePath.split(path.sep).slice(0, -1)];
115
+ return ancestors.some((segment) => SOURCE_TREE_DIRS.has(segment));
116
+ }
117
+ async function findFilesUncached(dir, extensions, excludePatterns = []) {
118
+ const results = [];
119
+ async function walk(currentDir) {
120
+ const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });
121
+ for (const entry of entries) {
122
+ const fullPath = path.join(currentDir, entry.name);
123
+ const relativePath = path.relative(dir, fullPath);
124
+ const shouldExclude = excludePatterns.some((pattern) => {
125
+ if (pattern.includes('*')) {
126
+ const escaped = (0, string_utils_1.escapeRegex)(pattern).replace(/\*/g, '.*');
127
+ const regex = new RegExp(escaped);
128
+ return regex.test(entry.name) || regex.test(relativePath);
129
+ }
130
+ // Whole path segments only: a substring match excluded files like
131
+ // `components/Calibrate.tsx` ("lib") or `app/distance/page.tsx` ("dist").
132
+ if (pattern.includes('/')) {
133
+ const rel = relativePath.split(path.sep).join('/');
134
+ return rel === pattern || rel.startsWith(`${pattern}/`);
135
+ }
136
+ if (entry.name !== pattern)
137
+ return false;
138
+ // Build-output names are real source inside routing/component trees
139
+ // (a Next.js route `app/build/page.tsx`); output never lives there.
140
+ if (OUTPUT_DIR_NAMES.has(pattern) && isInsideSourceTree(dir, relativePath))
141
+ return false;
142
+ return true;
143
+ });
144
+ if (shouldExclude)
145
+ continue;
146
+ if (entry.isDirectory()) {
147
+ await walk(fullPath);
148
+ }
149
+ else if (entry.isFile()) {
150
+ const ext = path.extname(entry.name).toLowerCase();
151
+ if (extensions.includes(ext)) {
152
+ results.push(fullPath);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ await walk(dir);
158
+ return results;
159
+ }
160
+ function getRelativePath(workspacePath, filePath) {
161
+ return path.relative(workspacePath, filePath).replace(/\\/g, '/');
162
+ }
163
+ async function readJsonFile(filePath) {
164
+ try {
165
+ const content = await readFile(filePath);
166
+ return JSON.parse(content);
167
+ }
168
+ catch {
169
+ return null;
170
+ }
171
+ }
172
+ async function writeJsonFile(filePath, data, sortKeys = true) {
173
+ let content;
174
+ if (sortKeys && typeof data === 'object' && data !== null) {
175
+ content = JSON.stringify(sortObjectKeys(data), null, 2);
176
+ }
177
+ else {
178
+ content = JSON.stringify(data, null, 2);
179
+ }
180
+ await writeFile(filePath, content + '\n');
181
+ }
182
+ function sortObjectKeys(obj) {
183
+ if (Array.isArray(obj)) {
184
+ return obj.map(sortObjectKeys);
185
+ }
186
+ if (typeof obj === 'object' && obj !== null) {
187
+ const sorted = {};
188
+ const keys = Object.keys(obj).sort();
189
+ for (const key of keys) {
190
+ sorted[key] = sortObjectKeys(obj[key]);
191
+ }
192
+ return sorted;
193
+ }
194
+ return obj;
195
+ }
196
+ function localeExtension(format) {
197
+ return format === 'po' ? '.po' : '.json';
198
+ }
199
+ /**
200
+ * Compute the locale directory and file path for a given target locale.
201
+ * Django uses `locale/<lang>/LC_MESSAGES/django.po`,
202
+ * other frameworks use `<publicDir>/locales/<lang>.json`.
203
+ */
204
+ function localeFilePath(workspacePath, publicDir, localeFormat, targetLocale) {
205
+ if (localeFormat === 'po') {
206
+ const dir = path.join(workspacePath, 'locale', targetLocale, 'LC_MESSAGES');
207
+ const file = path.join(dir, 'django.po');
208
+ const relativeFile = path.join('locale', targetLocale, 'LC_MESSAGES', 'django.po');
209
+ return { dir, file, relativeFile };
210
+ }
211
+ const ext = localeExtension(localeFormat);
212
+ const dir = path.join(workspacePath, publicDir, 'locales');
213
+ const file = path.join(dir, `${targetLocale}${ext}`);
214
+ const relativeFile = path.join(publicDir, 'locales', `${targetLocale}${ext}`);
215
+ return { dir, file, relativeFile };
216
+ }
217
+ async function ensureDir(dirPath) {
218
+ await fs.promises.mkdir(dirPath, { recursive: true });
219
+ }
220
+ async function copyFile(src, dest) {
221
+ await ensureDir(path.dirname(dest));
222
+ await fs.promises.copyFile(src, dest);
223
+ }
224
+ async function deleteFile(filePath) {
225
+ try {
226
+ await fs.promises.unlink(filePath);
227
+ }
228
+ catch {
229
+ // ignore
230
+ }
231
+ }
232
+ async function readDir(dirPath) {
233
+ try {
234
+ return await fs.promises.readdir(dirPath);
235
+ }
236
+ catch {
237
+ return [];
238
+ }
239
+ }
240
+ /**
241
+ * Process items with bounded concurrency.
242
+ * Unlike Promise.all, this limits the number of concurrent operations.
243
+ * Errors are collected and returned alongside results.
244
+ */
245
+ async function concurrentMap(items, fn, concurrency) {
246
+ const results = new Array(items.length);
247
+ const errors = [];
248
+ let nextIndex = 0;
249
+ const worker = async () => {
250
+ while (nextIndex < items.length) {
251
+ const i = nextIndex++;
252
+ try {
253
+ results[i] = await fn(items[i], i);
254
+ }
255
+ catch (err) {
256
+ errors.push(err instanceof Error ? err : new Error(String(err)));
257
+ }
258
+ }
259
+ };
260
+ await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
261
+ return { results, errors };
262
+ }
@@ -0,0 +1,9 @@
1
+ /** Shared by the translate phase and locale QA so both judge glossary use the same way. */
2
+ /**
3
+ * Whether the required glossary translation appears in the output, allowing
4
+ * for inflection: each of its words must match a word in the output on a
5
+ * shared stem (Polish "obszar roboczy" → "obszarem roboczym", German
6
+ * "Arbeitsbereich" → "Arbeitsbereichs"). Scripts without spaces fall back to a
7
+ * substring check.
8
+ */
9
+ export declare function containsGlossaryTranslation(translated: string, required: string): boolean;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /** Shared by the translate phase and locale QA so both judge glossary use the same way. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.containsGlossaryTranslation = containsGlossaryTranslation;
5
+ /**
6
+ * Whether the required glossary translation appears in the output, allowing
7
+ * for inflection: each of its words must match a word in the output on a
8
+ * shared stem (Polish "obszar roboczy" → "obszarem roboczym", German
9
+ * "Arbeitsbereich" → "Arbeitsbereichs"). Scripts without spaces fall back to a
10
+ * substring check.
11
+ */
12
+ function containsGlossaryTranslation(translated, required) {
13
+ const out = translated.toLowerCase();
14
+ const want = required.toLowerCase();
15
+ if (out.includes(want))
16
+ return true;
17
+ const outWords = out.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
18
+ const wantWords = want.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
19
+ if (wantWords.length === 0)
20
+ return true;
21
+ return wantWords.every((w) => {
22
+ const stem = w.slice(0, Math.max(3, w.length - 3));
23
+ return outWords.some((o) => o.startsWith(stem));
24
+ });
25
+ }
@@ -0,0 +1,8 @@
1
+ import type { ExtractedString, Warning } from '../runtime/types';
2
+ import type { Node } from 'node-html-parser';
3
+ export declare function extractStringsFromHtml(content: string, filePath: string, detectFormats?: boolean): Promise<{
4
+ strings: ExtractedString[];
5
+ warnings: Warning[];
6
+ }>;
7
+ export declare function isTextNode(node: Node): boolean;
8
+ export declare function isElementNode(node: Node): boolean;
@@ -0,0 +1,317 @@
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.extractStringsFromHtml = extractStringsFromHtml;
37
+ exports.isTextNode = isTextNode;
38
+ exports.isElementNode = isElementNode;
39
+ const string_utils_1 = require("./string-utils");
40
+ const detect_format_1 = require("./detect-format");
41
+ // Void elements that can't have text content
42
+ const VOID_ELEMENTS = new Set([
43
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
44
+ 'link', 'meta', 'param', 'source', 'track', 'wbr',
45
+ ]);
46
+ // Tags to skip entirely (already handled by SKIP_TAGS + void elements)
47
+ // Everything else is considered potentially translatable
48
+ const TRANSLATABLE_ATTRS = new Set([
49
+ 'alt', 'title', 'placeholder', 'aria-label', 'aria-description',
50
+ ]);
51
+ async function extractStringsFromHtml(content, filePath, detectFormats) {
52
+ const { parse } = await Promise.resolve().then(() => __importStar(require('node-html-parser')));
53
+ const ctx = {
54
+ filePath,
55
+ fileContent: content,
56
+ strings: [],
57
+ seenTexts: new Set(),
58
+ };
59
+ const warnings = [];
60
+ const root = parse(content, {
61
+ comment: false,
62
+ blockTextElements: { script: true, style: true, noscript: true, pre: true },
63
+ });
64
+ walkNode(root, ctx, 1, warnings, detectFormats);
65
+ extractHeadStrings(root, ctx, warnings);
66
+ return { strings: ctx.strings, warnings };
67
+ }
68
+ function isTextNode(node) {
69
+ return node.nodeType === 3;
70
+ }
71
+ function isElementNode(node) {
72
+ return node.nodeType === 1;
73
+ }
74
+ function asElement(node) {
75
+ return node.nodeType === 1 ? node : null;
76
+ }
77
+ /**
78
+ * Whether any element at or above `node` carries a `data-i18n` attribute.
79
+ * `node` is the parent of the text node being considered.
80
+ */
81
+ function hasDataI18nAncestor(node) {
82
+ let current = node;
83
+ while (current) {
84
+ if (isElementNode(current)) {
85
+ const el = asElement(current);
86
+ if (el && el.getAttribute('data-i18n'))
87
+ return true;
88
+ }
89
+ current = current.parentNode;
90
+ }
91
+ return false;
92
+ }
93
+ function walkNode(node, ctx, depth, warnings, detectFormats) {
94
+ if (depth > 100)
95
+ return;
96
+ if (isTextNode(node)) {
97
+ const text = node.text.trim();
98
+ if (!text || text.length < 2)
99
+ return;
100
+ const parent = node.parentNode;
101
+ if (!parent || !isElementNode(parent))
102
+ return;
103
+ // Honor data-i18n on ANY ancestor, not just the direct parent — an
104
+ // ancestor `<div data-i18n="k"><p>Text</p></div>` must not re-extract.
105
+ if (hasDataI18nAncestor(parent))
106
+ return;
107
+ const parentRaw = parent.rawTagName;
108
+ if (!parentRaw)
109
+ return;
110
+ const parentTagName = parentRaw.toLowerCase();
111
+ if (string_utils_1.SKIP_TAGS.has(parentTagName) || VOID_ELEMENTS.has(parentTagName))
112
+ return;
113
+ const hasCurrency = (0, string_utils_1.matchesCurrencyPattern)(text);
114
+ const hasDate = (0, string_utils_1.matchesDatePattern)(text);
115
+ const isDateOrCurrencyValue = (hasCurrency || hasDate) && text.length >= 3;
116
+ const format = detectFormats ? (0, detect_format_1.detectFormat)(text) : null;
117
+ if (!(0, string_utils_1.isTranslatableText)(text) && !isDateOrCurrencyValue && !format)
118
+ return;
119
+ if ((0, string_utils_1.containsPII)(text))
120
+ return;
121
+ if (format) {
122
+ warnings.push({
123
+ type: 'ambiguous-format',
124
+ file: ctx.filePath,
125
+ line: findLineNumber(node, ctx.fileContent),
126
+ message: `Detected ${format.class} format: "${text}". Will be replaced with Intl.* API call.`,
127
+ severity: 'warning',
128
+ dismissible: true,
129
+ });
130
+ }
131
+ else {
132
+ if (hasCurrency) {
133
+ warnings.push({
134
+ type: 'hardcoded-currency',
135
+ file: ctx.filePath,
136
+ line: findLineNumber(node, ctx.fileContent),
137
+ message: 'Hardcoded currency/number format detected. Use Intl.NumberFormat.',
138
+ });
139
+ }
140
+ if (hasDate) {
141
+ warnings.push({
142
+ type: 'hardcoded-date',
143
+ file: ctx.filePath,
144
+ line: findLineNumber(node, ctx.fileContent),
145
+ message: 'Hardcoded date format detected. Use Intl.DateTimeFormat.',
146
+ });
147
+ }
148
+ }
149
+ const isDateOrCurrencyOnly = isDateOrCurrencyValue && !(0, string_utils_1.isTranslatableText)(text);
150
+ let context = 'jsx-text';
151
+ if (format) {
152
+ context = 'format-string';
153
+ }
154
+ else if (isDateOrCurrencyOnly) {
155
+ context = hasCurrency ? 'hardcoded-currency' : 'hardcoded-date';
156
+ }
157
+ const line = findLineNumber(node, ctx.fileContent);
158
+ const contentKey = `${ctx.filePath}:${(0, string_utils_1.hashString)(text)}`;
159
+ if (!ctx.seenTexts.has(contentKey)) {
160
+ const id = `${ctx.filePath}:${line}:${(0, string_utils_1.hashString)(text)}`;
161
+ ctx.seenTexts.add(contentKey);
162
+ ctx.strings.push({
163
+ text,
164
+ file: ctx.filePath,
165
+ line,
166
+ column: 0,
167
+ context,
168
+ id,
169
+ detectedFormat: format ?? undefined,
170
+ });
171
+ }
172
+ return;
173
+ }
174
+ const element = asElement(node);
175
+ if (element) {
176
+ const raw = element.rawTagName;
177
+ if (!raw) {
178
+ for (const child of element.childNodes) {
179
+ if (isTextNode(child) || asElement(child)) {
180
+ walkNode(child, ctx, depth + 1, warnings, detectFormats);
181
+ }
182
+ }
183
+ return;
184
+ }
185
+ const tagName = raw.toLowerCase();
186
+ for (const attr of TRANSLATABLE_ATTRS) {
187
+ const value = element.getAttribute(attr);
188
+ if (value && (0, string_utils_1.isTranslatableText)(value) && !(0, string_utils_1.containsPII)(value)) {
189
+ const line = findLineNumber(element, ctx.fileContent);
190
+ const contentKey = `${ctx.filePath}:${(0, string_utils_1.hashString)(value)}`;
191
+ if (!ctx.seenTexts.has(contentKey)) {
192
+ const id = `${ctx.filePath}:${line}:${(0, string_utils_1.hashString)(value)}`;
193
+ ctx.seenTexts.add(contentKey);
194
+ ctx.strings.push({
195
+ text: value,
196
+ file: ctx.filePath,
197
+ line,
198
+ column: 0,
199
+ context: 'jsx-attr',
200
+ id,
201
+ });
202
+ }
203
+ }
204
+ }
205
+ // Extract value attribute from <input type="button|submit|reset"> and <option>
206
+ if (tagName === 'input') {
207
+ const type = element.getAttribute('type')?.toLowerCase();
208
+ if (type === 'button' || type === 'submit' || type === 'reset') {
209
+ const value = element.getAttribute('value');
210
+ if (value && value.trim().length >= 2 && !(0, string_utils_1.containsPII)(value) && (0, string_utils_1.isTranslatableText)(value)) {
211
+ const line = findLineNumber(element, ctx.fileContent);
212
+ const contentKey = `${ctx.filePath}:${(0, string_utils_1.hashString)(value)}`;
213
+ if (!ctx.seenTexts.has(contentKey)) {
214
+ const id = `${ctx.filePath}:${line}:${(0, string_utils_1.hashString)(value)}`;
215
+ ctx.seenTexts.add(contentKey);
216
+ ctx.strings.push({
217
+ text: value,
218
+ file: ctx.filePath,
219
+ line,
220
+ column: 0,
221
+ context: 'jsx-attr',
222
+ id,
223
+ });
224
+ }
225
+ }
226
+ }
227
+ }
228
+ if (tagName === 'option') {
229
+ const value = element.getAttribute('value');
230
+ if (value && value.trim().length >= 2 && !(0, string_utils_1.containsPII)(value) && (0, string_utils_1.isTranslatableText)(value)) {
231
+ // Only extract if value differs from text content (avoid duplicates)
232
+ const textContent = element.textContent?.trim();
233
+ if (textContent && value !== textContent) {
234
+ const line = findLineNumber(element, ctx.fileContent);
235
+ const contentKey = `${ctx.filePath}:${(0, string_utils_1.hashString)(value)}`;
236
+ if (!ctx.seenTexts.has(contentKey)) {
237
+ const id = `${ctx.filePath}:${line}:${(0, string_utils_1.hashString)(value)}`;
238
+ ctx.seenTexts.add(contentKey);
239
+ ctx.strings.push({
240
+ text: value,
241
+ file: ctx.filePath,
242
+ line,
243
+ column: 0,
244
+ context: 'jsx-attr',
245
+ id,
246
+ });
247
+ }
248
+ }
249
+ }
250
+ }
251
+ if (string_utils_1.SKIP_TAGS.has(tagName) || VOID_ELEMENTS.has(tagName))
252
+ return;
253
+ for (const child of element.childNodes) {
254
+ if (isTextNode(child) || asElement(child)) {
255
+ walkNode(child, ctx, depth + 1, warnings, detectFormats);
256
+ }
257
+ }
258
+ }
259
+ }
260
+ function findLineNumber(node, fileContent) {
261
+ if (node.range) {
262
+ return (fileContent.slice(0, node.range[0]).match(/\n/g) || []).length + 1;
263
+ }
264
+ const raw = node.rawText || node.text || '';
265
+ if (!raw)
266
+ return 0;
267
+ const idx = fileContent.indexOf(raw);
268
+ if (idx === -1)
269
+ return 0;
270
+ return (fileContent.slice(0, idx).match(/\n/g) || []).length + 1;
271
+ }
272
+ function extractHeadStrings(root, ctx, _warnings) {
273
+ // <title>
274
+ const title = root.querySelector('title');
275
+ if (title) {
276
+ const text = title.textContent?.trim();
277
+ if (text && text.length >= 2 && !ctx.seenTexts.has(text)) {
278
+ const contentKey = `${ctx.filePath}:${(0, string_utils_1.hashString)(text)}`;
279
+ if (!ctx.seenTexts.has(contentKey)) {
280
+ const id = `${ctx.filePath}:title:${(0, string_utils_1.hashString)(text)}`;
281
+ ctx.seenTexts.add(contentKey);
282
+ ctx.strings.push({
283
+ text,
284
+ file: ctx.filePath,
285
+ line: 0,
286
+ column: 0,
287
+ context: 'jsx-attr',
288
+ id,
289
+ });
290
+ }
291
+ }
292
+ }
293
+ // Meta tags with content attribute
294
+ const metaTags = root.querySelectorAll('meta[name],meta[property]');
295
+ const seoAttrs = ['description', 'og:title', 'og:description', 'og:site_name', 'twitter:title', 'twitter:description'];
296
+ for (const meta of metaTags) {
297
+ const name = meta.getAttribute('name') || meta.getAttribute('property');
298
+ if (name && seoAttrs.includes(name)) {
299
+ const content = meta.getAttribute('content');
300
+ if (content && content.trim().length >= 2 && !ctx.seenTexts.has(content)) {
301
+ const contentKey = `${ctx.filePath}:${(0, string_utils_1.hashString)(content)}`;
302
+ if (!ctx.seenTexts.has(contentKey)) {
303
+ const id = `${ctx.filePath}:meta:${(0, string_utils_1.hashString)(content)}`;
304
+ ctx.seenTexts.add(contentKey);
305
+ ctx.strings.push({
306
+ text: content,
307
+ file: ctx.filePath,
308
+ line: 0,
309
+ column: 0,
310
+ context: 'jsx-attr',
311
+ id,
312
+ });
313
+ }
314
+ }
315
+ }
316
+ }
317
+ }
@@ -0,0 +1,38 @@
1
+ import type { WorkspaceInfo } from '../runtime/types';
2
+ /** Libraries that call i18next's init themselves (the config is inside the library). */
3
+ export declare const I18NEXT_WRAPPERS: string[];
4
+ /** First line of i18n configs Babelize writes; Babelize may update a file that has it. */
5
+ export declare const GENERATED_I18N_CONFIG_MARKER = "// @babelize-generated i18n config";
6
+ /**
7
+ * Whether an i18next config was written by Babelize: the marker, or the
8
+ * signature of the template from before the marker existed.
9
+ */
10
+ export declare function isGeneratedI18nConfig(content: string): boolean;
11
+ export interface I18nextIcuStatus {
12
+ /** ICU messages ("Hello {name}", plurals) will be formatted at runtime. */
13
+ available: boolean;
14
+ /** The project initializes i18next itself (its own config, or a wrapper like next-i18next): Babelize must not add a second config. */
15
+ userOwnsInit: boolean;
16
+ /** A config the user owns that doesn't load i18next-icu (relative path). */
17
+ userConfig?: string;
18
+ /** Wrapper library that owns i18next's init, when that's how the project is set up. */
19
+ wrapper?: string;
20
+ /** Babelize-generated configs that don't load i18next-icu yet (relative paths); finalization adds it. */
21
+ generatedWithoutIcu: string[];
22
+ }
23
+ /**
24
+ * Can a react-i18next project format ICU messages at runtime? Decided by the
25
+ * config that actually initializes i18next — the files calling
26
+ * `.use(initReactI18next)` / `i18n.init(…)`:
27
+ * - a config the user owns: only if it loads i18next-icu;
28
+ * - Babelize's own config: yes (finalization adds i18next-icu to older ones);
29
+ * - a wrapper that owns init (next-i18next, …): only if its config loads i18next-icu;
30
+ * - nothing yet: yes, Babelize will generate the config.
31
+ * Without ICU, placeholders would show as raw `{user}`, so sentences stay
32
+ * fragments until the user adds i18next-icu.
33
+ */
34
+ export declare function detectI18nextIcu(workspace: WorkspaceInfo): Promise<I18nextIcuStatus>;
35
+ /** Add i18next-icu to a Babelize-generated config (idempotent). */
36
+ export declare function addIcuToGeneratedConfig(content: string): string;
37
+ /** The note shown when sentences stay fragments because the runtime can't format ICU. */
38
+ export declare function missingIcuNote(status: I18nextIcuStatus): string | null;