@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,272 @@
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.injectFormats = injectFormats;
37
+ exports.partitionFormatStrings = partitionFormatStrings;
38
+ const path = __importStar(require("path"));
39
+ const t = __importStar(require("@babel/types"));
40
+ const inject_formats_1 = require("./inject-formats");
41
+ const ast_helpers_1 = require("../../utils/ast-helpers");
42
+ const file_utils_1 = require("../../utils/file-utils");
43
+ const types_1 = require("../../runtime/types");
44
+ const generator_1 = require("@babel/generator");
45
+ /**
46
+ * Run format injection for all format strings.
47
+ *
48
+ * Replaces hardcoded currency, date, and number values in source files
49
+ * with Intl.* API calls (e.g., Intl.NumberFormat, Intl.DateTimeFormat).
50
+ */
51
+ async function injectFormats(workspace, formatStrings, formatConfig, dryRun = false) {
52
+ const modifiedFiles = [];
53
+ const skippedFiles = [];
54
+ let totalInjections = 0;
55
+ if (!formatConfig.enabled || formatStrings.length === 0) {
56
+ return { modifiedFiles, skippedFiles, totalInjections: 0 };
57
+ }
58
+ // Group format strings by file
59
+ const stringsByFile = new Map();
60
+ for (const s of formatStrings) {
61
+ if (!s.detectedFormat)
62
+ continue;
63
+ const fileKey = s.file;
64
+ if (!stringsByFile.has(fileKey)) {
65
+ stringsByFile.set(fileKey, []);
66
+ }
67
+ stringsByFile.get(fileKey).push(s);
68
+ }
69
+ // Build locale expression based on framework
70
+ const localeExpr = (0, inject_formats_1.buildLocaleExpression)(workspace.framework || 'unknown');
71
+ // Find all source files
72
+ const sourceFiles = [];
73
+ for (const sourceDir of workspace.sourceDirs) {
74
+ const dirPath = path.join(workspace.path, sourceDir);
75
+ const files = await (0, file_utils_1.findFiles)(dirPath, types_1.SUPPORTED_EXTENSIONS, types_1.EXCLUDED_PATTERNS);
76
+ sourceFiles.push(...files);
77
+ }
78
+ // Build a lookup of format strings by (relative file path, line)
79
+ const formatStringLookup = new Map();
80
+ for (const [fileKey, strings] of stringsByFile) {
81
+ const lineMap = new Map();
82
+ for (const s of strings) {
83
+ lineMap.set(s.line, s);
84
+ }
85
+ formatStringLookup.set(fileKey, lineMap);
86
+ }
87
+ // Process each file
88
+ for (const filePath of sourceFiles) {
89
+ const relativePath = (0, file_utils_1.getRelativePath)(workspace.path, filePath);
90
+ const fileStrings = stringsByFile.get(relativePath);
91
+ if (!fileStrings || fileStrings.length === 0)
92
+ continue;
93
+ try {
94
+ const originalContent = await (0, file_utils_1.readFile)(filePath);
95
+ const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
96
+ const ast = (0, ast_helpers_1.parseCode)(originalContent, { isTypeScript });
97
+ const injections = [];
98
+ const lineLookup = formatStringLookup.get(relativePath);
99
+ if (!lineLookup)
100
+ continue;
101
+ // Walk AST and replace matching string literals with Intl.* expressions
102
+ (0, ast_helpers_1.traverse)(ast, {
103
+ StringLiteral(strPath) {
104
+ const node = strPath.node;
105
+ if (!node.loc)
106
+ return;
107
+ const line = node.loc.start.line;
108
+ const formatStr = lineLookup.get(line);
109
+ if (!formatStr || !formatStr.detectedFormat)
110
+ return;
111
+ // Verify the text matches exactly (avoid false positives from partial matches)
112
+ if (node.value !== formatStr.text)
113
+ return;
114
+ const astExpr = (0, inject_formats_1.buildFormatAstExpression)(formatStr.detectedFormat, formatConfig, localeExpr);
115
+ if (!astExpr) {
116
+ skippedFiles.push({
117
+ file: relativePath,
118
+ reason: 'format-ast-build-failed',
119
+ details: `Could not build AST for: "${formatStr.text}"`,
120
+ });
121
+ return;
122
+ }
123
+ strPath.replaceWith(astExpr);
124
+ const replacementCode = (0, generator_1.generate)(astExpr).code;
125
+ injections.push({
126
+ file: relativePath,
127
+ line,
128
+ column: node.loc.start.column,
129
+ original: formatStr.text,
130
+ replacement: replacementCode,
131
+ key: '',
132
+ confidence: 100,
133
+ formatClass: formatStr.detectedFormat.class,
134
+ });
135
+ },
136
+ JSXText(jsxPath) {
137
+ const node = jsxPath.node;
138
+ if (!node.loc)
139
+ return;
140
+ const line = node.loc.start.line;
141
+ const formatStr = lineLookup.get(line);
142
+ if (!formatStr || !formatStr.detectedFormat)
143
+ return;
144
+ const text = node.value.trim();
145
+ if (!text)
146
+ return;
147
+ const astExpr = (0, inject_formats_1.buildFormatAstExpression)(formatStr.detectedFormat, formatConfig, localeExpr);
148
+ if (!astExpr) {
149
+ skippedFiles.push({
150
+ file: relativePath,
151
+ reason: 'format-ast-build-failed',
152
+ details: `Could not build AST for: "${formatStr.text}"`,
153
+ });
154
+ return;
155
+ }
156
+ const exprContainer = t.jsxExpressionContainer(astExpr);
157
+ jsxPath.replaceWith(exprContainer);
158
+ const replacementCode = (0, generator_1.generate)(astExpr).code;
159
+ injections.push({
160
+ file: relativePath,
161
+ line,
162
+ column: node.loc.start.column,
163
+ original: formatStr.text,
164
+ replacement: replacementCode,
165
+ key: '',
166
+ confidence: 100,
167
+ formatClass: formatStr.detectedFormat.class,
168
+ });
169
+ },
170
+ });
171
+ if (injections.length === 0)
172
+ continue;
173
+ const modifiedContent = (0, ast_helpers_1.generateCode)(ast, originalContent);
174
+ if (!dryRun) {
175
+ await (0, file_utils_1.writeFile)(filePath, modifiedContent);
176
+ }
177
+ modifiedFiles.push({
178
+ file: relativePath,
179
+ originalContent,
180
+ modifiedContent,
181
+ injections,
182
+ importAdded: false,
183
+ hookAdded: false,
184
+ });
185
+ totalInjections += injections.length;
186
+ }
187
+ catch (error) {
188
+ skippedFiles.push({
189
+ file: relativePath,
190
+ reason: 'parse-error',
191
+ details: error instanceof Error ? error.message : String(error),
192
+ });
193
+ }
194
+ }
195
+ // Process HTML files with regex-based format injection
196
+ const htmlExtensions = ['.html', '.htm'];
197
+ for (const sourceDir of workspace.sourceDirs) {
198
+ const dirPath = path.join(workspace.path, sourceDir);
199
+ const files = await (0, file_utils_1.findFiles)(dirPath, htmlExtensions, types_1.EXCLUDED_PATTERNS);
200
+ for (const filePath of files) {
201
+ const relativePath = (0, file_utils_1.getRelativePath)(workspace.path, filePath);
202
+ const fileStrings = stringsByFile.get(relativePath);
203
+ if (!fileStrings || fileStrings.length === 0)
204
+ continue;
205
+ try {
206
+ let content = await (0, file_utils_1.readFile)(filePath);
207
+ const injections = [];
208
+ for (const formatStr of fileStrings) {
209
+ if (!formatStr.detectedFormat)
210
+ continue;
211
+ const escaped = formatStr.text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
212
+ const regex = new RegExp(escaped, 'g');
213
+ if (!regex.test(content))
214
+ continue;
215
+ const astExpr = (0, inject_formats_1.buildFormatAstExpression)(formatStr.detectedFormat, formatConfig, localeExpr);
216
+ if (!astExpr)
217
+ continue;
218
+ const replacementCode = (0, generator_1.generate)(astExpr).code;
219
+ content = content.replace(regex, replacementCode);
220
+ injections.push({
221
+ file: relativePath,
222
+ line: formatStr.line,
223
+ column: 0,
224
+ original: formatStr.text,
225
+ replacement: replacementCode,
226
+ key: '',
227
+ confidence: 100,
228
+ formatClass: formatStr.detectedFormat.class,
229
+ });
230
+ }
231
+ if (injections.length === 0)
232
+ continue;
233
+ if (!dryRun)
234
+ await (0, file_utils_1.writeFile)(filePath, content);
235
+ modifiedFiles.push({
236
+ file: relativePath,
237
+ originalContent: await (0, file_utils_1.readFile)(filePath).catch(() => ''),
238
+ modifiedContent: content,
239
+ injections,
240
+ importAdded: false,
241
+ hookAdded: false,
242
+ });
243
+ totalInjections += injections.length;
244
+ }
245
+ catch (error) {
246
+ skippedFiles.push({
247
+ file: relativePath,
248
+ reason: 'parse-error',
249
+ details: error instanceof Error ? error.message : String(error),
250
+ });
251
+ }
252
+ }
253
+ }
254
+ return { modifiedFiles, skippedFiles, totalInjections };
255
+ }
256
+ /**
257
+ * Filter format strings out of an array of extracted strings.
258
+ * Returns [translatableStrings, formatStrings].
259
+ */
260
+ function partitionFormatStrings(strings) {
261
+ const translatable = [];
262
+ const format = [];
263
+ for (const s of strings) {
264
+ if (s.context === 'format-string' && s.detectedFormat) {
265
+ format.push(s);
266
+ }
267
+ else {
268
+ translatable.push(s);
269
+ }
270
+ }
271
+ return [translatable, format];
272
+ }
@@ -0,0 +1,31 @@
1
+ import * as t from '@babel/types';
2
+ import type { FormatLocaleConfig } from '@babelize/types';
3
+ import type { DetectedFormat, ExtractedString } from '../../runtime/types';
4
+ /**
5
+ * Generate the Intl.* API call string that replaces a hardcoded format value.
6
+ *
7
+ * Examples:
8
+ * "$1,234.56" → `new Intl.NumberFormat(locale, { style: 'currency', currency: 'USD' }).format(1234.56)`
9
+ * "01/15/2024" → `new Intl.DateTimeFormat(locale, { month: '2-digit', day: '2-digit', year: 'numeric' }).format(new Date(2024, 0, 15))`
10
+ * "1,234.56" → `new Intl.NumberFormat(locale).format(1234.56)`
11
+ */
12
+ export declare function generateFormatExpression(format: DetectedFormat, formatConfig: FormatLocaleConfig): string | null;
13
+ /**
14
+ * Given a locale and DetectedFormat, return both the Intl.* expression
15
+ * and a getTranslationKey-compatible fallback string.
16
+ */
17
+ export interface FormatInjection {
18
+ expression: string;
19
+ fallback: string;
20
+ }
21
+ export declare function buildFormatInjection(extracted: ExtractedString, formatConfig: FormatLocaleConfig): FormatInjection | null;
22
+ /**
23
+ * Build a Babel AST Expression node for the Intl.* call that replaces
24
+ * a hardcoded format value. The localeExpr parameter provides the runtime
25
+ * locale expression (e.g., i18n.language, navigator.language).
26
+ */
27
+ export declare function buildFormatAstExpression(format: DetectedFormat, formatConfig: FormatLocaleConfig, localeExpr: t.Expression): t.Expression | null;
28
+ /**
29
+ * Build a locale expression based on the detected framework.
30
+ */
31
+ export declare function buildLocaleExpression(framework: string): t.Expression;
@@ -0,0 +1,208 @@
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.generateFormatExpression = generateFormatExpression;
37
+ exports.buildFormatInjection = buildFormatInjection;
38
+ exports.buildFormatAstExpression = buildFormatAstExpression;
39
+ exports.buildLocaleExpression = buildLocaleExpression;
40
+ const t = __importStar(require("@babel/types"));
41
+ /**
42
+ * Generate the Intl.* API call string that replaces a hardcoded format value.
43
+ *
44
+ * Examples:
45
+ * "$1,234.56" → `new Intl.NumberFormat(locale, { style: 'currency', currency: 'USD' }).format(1234.56)`
46
+ * "01/15/2024" → `new Intl.DateTimeFormat(locale, { month: '2-digit', day: '2-digit', year: 'numeric' }).format(new Date(2024, 0, 15))`
47
+ * "1,234.56" → `new Intl.NumberFormat(locale).format(1234.56)`
48
+ */
49
+ function generateFormatExpression(format, formatConfig) {
50
+ switch (format.class) {
51
+ case 'currency':
52
+ return generateCurrencyExpression(format, formatConfig);
53
+ case 'date':
54
+ return generateDateExpression(format, formatConfig);
55
+ case 'number':
56
+ return generateNumberExpression(format, formatConfig);
57
+ default:
58
+ return null;
59
+ }
60
+ }
61
+ function generateCurrencyExpression(format, formatConfig) {
62
+ const currencyCode = format.detectedCurrency || formatConfig.currency?.code || 'USD';
63
+ const numericValue = parseNumericValue(format.rawValue);
64
+ if (isNaN(numericValue)) {
65
+ return JSON.stringify(format.originalString);
66
+ }
67
+ return `new Intl.NumberFormat(locale, { style: 'currency', currency: '${currencyCode}' }).format(${numericValue})`;
68
+ }
69
+ function generateDateExpression(format, _formatConfig) {
70
+ const date = parseDateValue(format.rawValue, format.dateSource);
71
+ if (!date) {
72
+ return JSON.stringify(format.originalString);
73
+ }
74
+ const dateOptions = format.dateOptions || { year: 'numeric', month: '2-digit', day: '2-digit' };
75
+ const dateOptionsStr = JSON.stringify(dateOptions);
76
+ return `new Intl.DateTimeFormat(locale, ${dateOptionsStr}).format(new Date(${date.getTime()}))`;
77
+ }
78
+ function generateNumberExpression(format, _formatConfig) {
79
+ const numericValue = parseNumericValue(format.rawValue);
80
+ if (isNaN(numericValue)) {
81
+ return JSON.stringify(format.originalString);
82
+ }
83
+ const options = { ...format.numberOptions };
84
+ return `new Intl.NumberFormat(locale, ${JSON.stringify(options)}).format(${numericValue})`;
85
+ }
86
+ function parseNumericValue(rawValue) {
87
+ const cleaned = rawValue.replace(/[^0-9.,-]/g, '');
88
+ const normalized = cleaned.replace(/,/g, '');
89
+ return parseFloat(normalized);
90
+ }
91
+ function parseDateValue(rawValue, source) {
92
+ // Remove ordinal suffixes like "th", "nd", "st", "rd"
93
+ const cleaned = rawValue.replace(/(\d+)(st|nd|rd|th)/, '$1');
94
+ switch (source) {
95
+ case 'MM/DD/YYYY': {
96
+ const parts = cleaned.split(/[/]/);
97
+ if (parts.length !== 3)
98
+ return null;
99
+ return new Date(parseInt(parts[2]), parseInt(parts[0]) - 1, parseInt(parts[1]));
100
+ }
101
+ case 'DD/MM/YYYY': {
102
+ const parts = cleaned.split(/[/]/);
103
+ if (parts.length !== 3)
104
+ return null;
105
+ return new Date(parseInt(parts[2]), parseInt(parts[1]) - 1, parseInt(parts[0]));
106
+ }
107
+ case 'YYYY-MM-DD': {
108
+ const parts = cleaned.split(/[-/]/);
109
+ if (parts.length !== 3)
110
+ return null;
111
+ return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]));
112
+ }
113
+ case 'DD.MM.YYYY': {
114
+ const parts = cleaned.split('.');
115
+ if (parts.length !== 3)
116
+ return null;
117
+ return new Date(parseInt(parts[2]), parseInt(parts[1]) - 1, parseInt(parts[0]));
118
+ }
119
+ case 'MMM DD, YYYY': {
120
+ return new Date(cleaned);
121
+ }
122
+ case 'DD MMM YYYY': {
123
+ return new Date(cleaned);
124
+ }
125
+ default: {
126
+ return new Date(cleaned);
127
+ }
128
+ }
129
+ }
130
+ function buildFormatInjection(extracted, formatConfig) {
131
+ if (!extracted.detectedFormat)
132
+ return null;
133
+ const expression = generateFormatExpression(extracted.detectedFormat, formatConfig);
134
+ if (!expression)
135
+ return null;
136
+ return {
137
+ expression,
138
+ fallback: extracted.text,
139
+ };
140
+ }
141
+ /**
142
+ * Build a Babel AST Expression node for the Intl.* call that replaces
143
+ * a hardcoded format value. The localeExpr parameter provides the runtime
144
+ * locale expression (e.g., i18n.language, navigator.language).
145
+ */
146
+ function buildFormatAstExpression(format, formatConfig, localeExpr) {
147
+ switch (format.class) {
148
+ case 'currency':
149
+ return buildCurrencyAstExpression(format, formatConfig, localeExpr);
150
+ case 'date':
151
+ return buildDateAstExpression(format, localeExpr);
152
+ case 'number':
153
+ return buildNumberAstExpression(format, localeExpr);
154
+ default:
155
+ return null;
156
+ }
157
+ }
158
+ function buildCurrencyAstExpression(format, formatConfig, localeExpr) {
159
+ const currencyCode = format.detectedCurrency || formatConfig.currency?.code || 'USD';
160
+ const numericValue = parseNumericValue(format.rawValue);
161
+ if (isNaN(numericValue))
162
+ return null;
163
+ const intlCallee = t.memberExpression(t.identifier('Intl'), t.identifier('NumberFormat'));
164
+ const options = t.objectExpression([
165
+ t.objectProperty(t.identifier('style'), t.stringLiteral('currency')),
166
+ t.objectProperty(t.identifier('currency'), t.stringLiteral(currencyCode)),
167
+ ]);
168
+ const nfNew = t.newExpression(intlCallee, [localeExpr, options]);
169
+ return t.callExpression(t.memberExpression(nfNew, t.identifier('format')), [t.numericLiteral(numericValue)]);
170
+ }
171
+ function buildDateAstExpression(format, localeExpr) {
172
+ const date = parseDateValue(format.rawValue, format.dateSource);
173
+ if (!date)
174
+ return null;
175
+ const dateOptions = format.dateOptions || { year: 'numeric', month: '2-digit', day: '2-digit' };
176
+ const props = Object.entries(dateOptions).map(([key, val]) => t.objectProperty(t.identifier(key), t.stringLiteral(val)));
177
+ const intlCallee = t.memberExpression(t.identifier('Intl'), t.identifier('DateTimeFormat'));
178
+ const dtfNew = t.newExpression(intlCallee, [localeExpr, t.objectExpression(props)]);
179
+ const dateArg = t.newExpression(t.identifier('Date'), [t.numericLiteral(date.getTime())]);
180
+ return t.callExpression(t.memberExpression(dtfNew, t.identifier('format')), [dateArg]);
181
+ }
182
+ function buildNumberAstExpression(format, localeExpr) {
183
+ const numericValue = parseNumericValue(format.rawValue);
184
+ if (isNaN(numericValue))
185
+ return null;
186
+ const intlCallee = t.memberExpression(t.identifier('Intl'), t.identifier('NumberFormat'));
187
+ const args = [localeExpr];
188
+ const options = { ...format.numberOptions };
189
+ if (Object.keys(options).length > 0) {
190
+ const props = Object.entries(options).map(([key, val]) => t.objectProperty(t.identifier(key), t.numericLiteral(val)));
191
+ args.push(t.objectExpression(props));
192
+ }
193
+ const nfNew = t.newExpression(intlCallee, args);
194
+ return t.callExpression(t.memberExpression(nfNew, t.identifier('format')), [t.numericLiteral(numericValue)]);
195
+ }
196
+ /**
197
+ * Build a locale expression based on the detected framework.
198
+ */
199
+ function buildLocaleExpression(framework) {
200
+ switch (framework) {
201
+ case 'react-i18next':
202
+ return t.memberExpression(t.identifier('i18n'), t.identifier('language'));
203
+ case 'next-intl':
204
+ return t.callExpression(t.identifier('useLocale'), []);
205
+ default:
206
+ return t.logicalExpression('||', t.memberExpression(t.identifier('navigator'), t.identifier('language')), t.stringLiteral('en-US'));
207
+ }
208
+ }
@@ -0,0 +1,9 @@
1
+ import type { AlwaysTranslateAsEntry } from './types';
2
+ /**
3
+ * Post-translation processor for "always translate as" glossary entries.
4
+ *
5
+ * After the HF model returns a translation, this function scans the result
6
+ * for each always-translate-as term and replaces it with the fixed
7
+ * translation for the target locale.
8
+ */
9
+ export declare function applyAlwaysTranslateAs(translatedText: string, entries: AlwaysTranslateAsEntry[], targetLocale: string): string;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyAlwaysTranslateAs = applyAlwaysTranslateAs;
4
+ const string_utils_1 = require("../../utils/string-utils");
5
+ /**
6
+ * Post-translation processor for "always translate as" glossary entries.
7
+ *
8
+ * After the HF model returns a translation, this function scans the result
9
+ * for each always-translate-as term and replaces it with the fixed
10
+ * translation for the target locale.
11
+ */
12
+ function applyAlwaysTranslateAs(translatedText, entries, targetLocale) {
13
+ if (!entries.length || !translatedText)
14
+ return translatedText;
15
+ let result = translatedText;
16
+ // Sort longest-first to avoid partial replacements
17
+ const sorted = [...entries].sort((a, b) => b.term.length - a.term.length);
18
+ for (const entry of sorted) {
19
+ const fixedTranslation = entry.translations[targetLocale];
20
+ if (!fixedTranslation)
21
+ continue;
22
+ // Replace all occurrences of the term (case-insensitive, word-boundary)
23
+ const regex = new RegExp(`\\b${(0, string_utils_1.escapeRegex)(entry.term)}\\b`, 'gi');
24
+ result = result.replace(regex, fixedTranslation);
25
+ }
26
+ return result;
27
+ }
@@ -0,0 +1,3 @@
1
+ export { applyGlossaryMask, restoreGlossaryMask } from './masker';
2
+ export { applyAlwaysTranslateAs } from './glossary';
3
+ export type { GlossaryEntry, GlossaryEntryType, BrandVoice, AlwaysTranslateAsEntry } from './types';
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyAlwaysTranslateAs = exports.restoreGlossaryMask = exports.applyGlossaryMask = void 0;
4
+ var masker_1 = require("./masker");
5
+ Object.defineProperty(exports, "applyGlossaryMask", { enumerable: true, get: function () { return masker_1.applyGlossaryMask; } });
6
+ Object.defineProperty(exports, "restoreGlossaryMask", { enumerable: true, get: function () { return masker_1.restoreGlossaryMask; } });
7
+ var glossary_1 = require("./glossary");
8
+ Object.defineProperty(exports, "applyAlwaysTranslateAs", { enumerable: true, get: function () { return glossary_1.applyAlwaysTranslateAs; } });
@@ -0,0 +1,23 @@
1
+ export interface MaskedString {
2
+ masked: string;
3
+ terms: MaskedTerm[];
4
+ }
5
+ export interface MaskedTerm {
6
+ term: string;
7
+ matches: string[];
8
+ }
9
+ /**
10
+ * Replace never-translate terms with placeholders (__TERM_0__, __TERM_1__, ...)
11
+ * so NLLB does not attempt to translate them.
12
+ *
13
+ * Rules:
14
+ * - Whole-word matching only (word-boundary anchors)
15
+ * - Case-insensitive matching
16
+ * - Longest terms matched first to prevent partial overlaps
17
+ * - All occurrences of each term are masked
18
+ */
19
+ export declare function applyGlossaryMask(text: string, terms: string[]): MaskedString;
20
+ /**
21
+ * Restore __TERM_N__ placeholders back to their original terms.
22
+ */
23
+ export declare function restoreGlossaryMask(text: string, terms: MaskedTerm[]): string;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyGlossaryMask = applyGlossaryMask;
4
+ exports.restoreGlossaryMask = restoreGlossaryMask;
5
+ const string_utils_1 = require("../../utils/string-utils");
6
+ /**
7
+ * Replace never-translate terms with placeholders (__TERM_0__, __TERM_1__, ...)
8
+ * so NLLB does not attempt to translate them.
9
+ *
10
+ * Rules:
11
+ * - Whole-word matching only (word-boundary anchors)
12
+ * - Case-insensitive matching
13
+ * - Longest terms matched first to prevent partial overlaps
14
+ * - All occurrences of each term are masked
15
+ */
16
+ function applyGlossaryMask(text, terms) {
17
+ if (!terms.length || !text) {
18
+ return { masked: text, terms: [] };
19
+ }
20
+ // Sort terms longest-first to avoid smaller terms eating larger ones.
21
+ const sorted = [...terms].sort((a, b) => b.length - a.length);
22
+ let masked = text;
23
+ const usedTerms = [];
24
+ sorted.forEach((term) => {
25
+ let placeholderIndex = usedTerms.findIndex((entry) => entry.term === term);
26
+ if (placeholderIndex === -1) {
27
+ placeholderIndex = usedTerms.push({ term, matches: [] }) - 1;
28
+ }
29
+ const placeholder = `__TERM_${placeholderIndex}__`;
30
+ // Word-boundary + case-insensitive matching
31
+ const regex = new RegExp(`(?:^|(?<=\\W))${(0, string_utils_1.escapeRegex)(term)}(?:$|(?=\\W))`, 'gi');
32
+ masked = masked.replace(regex, (matched) => {
33
+ usedTerms[placeholderIndex].matches.push(matched);
34
+ return placeholder;
35
+ });
36
+ });
37
+ return { masked, terms: usedTerms };
38
+ }
39
+ /**
40
+ * Restore __TERM_N__ placeholders back to their original terms.
41
+ */
42
+ function restoreGlossaryMask(text, terms) {
43
+ if (!terms.length || !text)
44
+ return text;
45
+ let restored = text;
46
+ terms.forEach((maskedTerm, index) => {
47
+ const placeholderRegex = new RegExp(`__TERM_${index}__`, 'g');
48
+ let replacementIndex = 0;
49
+ restored = restored.replace(placeholderRegex, () => {
50
+ const replacement = maskedTerm.matches[replacementIndex] ?? maskedTerm.term;
51
+ replacementIndex++;
52
+ return replacement;
53
+ });
54
+ });
55
+ return restored;
56
+ }
@@ -0,0 +1,22 @@
1
+ export type GlossaryEntryType = 'never_translate' | 'always_translate_as';
2
+ export interface GlossaryEntry {
3
+ id?: string;
4
+ project_id: string;
5
+ term: string;
6
+ type: GlossaryEntryType;
7
+ /** For always_translate_as: locale code → fixed translation */
8
+ translations?: Record<string, string>;
9
+ created_at?: string;
10
+ updated_at?: string;
11
+ }
12
+ export interface BrandVoice {
13
+ name: string;
14
+ description?: string;
15
+ guidelines?: string[];
16
+ }
17
+ /** Subset used by the masker and translation pipeline */
18
+ export interface AlwaysTranslateAsEntry {
19
+ term: string;
20
+ type: 'always_translate_as';
21
+ translations: Record<string, string>;
22
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export { injectTranslations, checkIdempotency } from './inject';