@babelize/engine 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/LICENSE +202 -0
  2. package/dist/BabelizeEngine.d.ts +12 -0
  3. package/dist/BabelizeEngine.js +47 -0
  4. package/dist/analysis/analyze.d.ts +106 -0
  5. package/dist/analysis/analyze.js +323 -0
  6. package/dist/config/env.d.ts +9 -0
  7. package/dist/config/env.js +16 -0
  8. package/dist/config.d.ts +74 -0
  9. package/dist/config.js +47 -0
  10. package/dist/detection/stack.d.ts +42 -0
  11. package/dist/detection/stack.js +277 -0
  12. package/dist/django/catalog.d.ts +92 -0
  13. package/dist/django/catalog.js +677 -0
  14. package/dist/django/extract.d.ts +20 -0
  15. package/dist/django/extract.js +79 -0
  16. package/dist/django/inject-file.d.ts +6 -0
  17. package/dist/django/inject-file.js +52 -0
  18. package/dist/django/inject.d.ts +11 -0
  19. package/dist/django/inject.js +205 -0
  20. package/dist/django/keys.d.ts +21 -0
  21. package/dist/django/keys.js +63 -0
  22. package/dist/django/locale-names.d.ts +13 -0
  23. package/dist/django/locale-names.js +60 -0
  24. package/dist/django/mo-file.d.ts +6 -0
  25. package/dist/django/mo-file.js +60 -0
  26. package/dist/django/plural-forms.d.ts +34 -0
  27. package/dist/django/plural-forms.js +216 -0
  28. package/dist/django/po-file.d.ts +38 -0
  29. package/dist/django/po-file.js +273 -0
  30. package/dist/django/python-extract.d.ts +34 -0
  31. package/dist/django/python-extract.js +619 -0
  32. package/dist/django/python-lexer.d.ts +65 -0
  33. package/dist/django/python-lexer.js +398 -0
  34. package/dist/django/template-extract.d.ts +32 -0
  35. package/dist/django/template-extract.js +680 -0
  36. package/dist/django/types.d.ts +49 -0
  37. package/dist/django/types.js +15 -0
  38. package/dist/index.d.ts +59 -0
  39. package/dist/index.js +140 -0
  40. package/dist/injection/InjectionStrategy.d.ts +62 -0
  41. package/dist/injection/InjectionStrategy.js +2 -0
  42. package/dist/injection/StrategyFactory.d.ts +4 -0
  43. package/dist/injection/StrategyFactory.js +29 -0
  44. package/dist/injection/html/html-injector.d.ts +11 -0
  45. package/dist/injection/html/html-injector.js +173 -0
  46. package/dist/injection/strategies/NextIntlStrategy.d.ts +25 -0
  47. package/dist/injection/strategies/NextIntlStrategy.js +141 -0
  48. package/dist/injection/strategies/ReactI18nextStrategy.d.ts +19 -0
  49. package/dist/injection/strategies/ReactI18nextStrategy.js +116 -0
  50. package/dist/injection/strategies/VanillaStrategy.d.ts +11 -0
  51. package/dist/injection/strategies/VanillaStrategy.js +73 -0
  52. package/dist/injection/strategies/VueI18nStrategy.d.ts +11 -0
  53. package/dist/injection/strategies/VueI18nStrategy.js +84 -0
  54. package/dist/local/formatter.d.ts +26 -0
  55. package/dist/local/formatter.js +105 -0
  56. package/dist/local/git.d.ts +23 -0
  57. package/dist/local/git.js +136 -0
  58. package/dist/local/hosted-provider.d.ts +68 -0
  59. package/dist/local/hosted-provider.js +215 -0
  60. package/dist/local/index.d.ts +23 -0
  61. package/dist/local/index.js +75 -0
  62. package/dist/local/provider.d.ts +56 -0
  63. package/dist/local/provider.js +119 -0
  64. package/dist/local/pseudo-provider.d.ts +18 -0
  65. package/dist/local/pseudo-provider.js +208 -0
  66. package/dist/local/report.d.ts +177 -0
  67. package/dist/local/report.js +186 -0
  68. package/dist/local/translate-core.d.ts +218 -0
  69. package/dist/local/translate-core.js +499 -0
  70. package/dist/local/translate-messages.d.ts +49 -0
  71. package/dist/local/translate-messages.js +89 -0
  72. package/dist/local/workspace-copy.d.ts +91 -0
  73. package/dist/local/workspace-copy.js +355 -0
  74. package/dist/modules/extractor/extract.d.ts +20 -0
  75. package/dist/modules/extractor/extract.js +709 -0
  76. package/dist/modules/extractor/index.d.ts +2 -0
  77. package/dist/modules/extractor/index.js +63 -0
  78. package/dist/modules/finalization/django-finalize.d.ts +28 -0
  79. package/dist/modules/finalization/django-finalize.js +412 -0
  80. package/dist/modules/finalization/finalize.d.ts +38 -0
  81. package/dist/modules/finalization/finalize.js +745 -0
  82. package/dist/modules/finalization/index.d.ts +1 -0
  83. package/dist/modules/finalization/index.js +5 -0
  84. package/dist/modules/finalization/nextjs-finalize.d.ts +40 -0
  85. package/dist/modules/finalization/nextjs-finalize.js +328 -0
  86. package/dist/modules/finalization/static-runtime.d.ts +9 -0
  87. package/dist/modules/finalization/static-runtime.js +261 -0
  88. package/dist/modules/finalization/writes.d.ts +29 -0
  89. package/dist/modules/finalization/writes.js +78 -0
  90. package/dist/modules/format-injection/index.d.ts +19 -0
  91. package/dist/modules/format-injection/index.js +273 -0
  92. package/dist/modules/format-injection/inject-formats.d.ts +31 -0
  93. package/dist/modules/format-injection/inject-formats.js +208 -0
  94. package/dist/modules/glossary/index.d.ts +2 -0
  95. package/dist/modules/glossary/index.js +16 -0
  96. package/dist/modules/glossary/rules.d.ts +81 -0
  97. package/dist/modules/glossary/rules.js +244 -0
  98. package/dist/modules/glossary/types.d.ts +22 -0
  99. package/dist/modules/glossary/types.js +2 -0
  100. package/dist/modules/injection/index.d.ts +1 -0
  101. package/dist/modules/injection/index.js +6 -0
  102. package/dist/modules/injection/inject.d.ts +29 -0
  103. package/dist/modules/injection/inject.js +1050 -0
  104. package/dist/modules/keygen/index.d.ts +1 -0
  105. package/dist/modules/keygen/index.js +11 -0
  106. package/dist/modules/keygen/keygen.d.ts +4 -0
  107. package/dist/modules/keygen/keygen.js +123 -0
  108. package/dist/modules/keygen/locale-data.d.ts +7 -0
  109. package/dist/modules/keygen/locale-data.js +73 -0
  110. package/dist/modules/keygen/public.d.ts +2 -0
  111. package/dist/modules/keygen/public.js +12 -0
  112. package/dist/modules/locale-builder/index.d.ts +1 -0
  113. package/dist/modules/locale-builder/index.js +7 -0
  114. package/dist/modules/locale-builder/locales.d.ts +13 -0
  115. package/dist/modules/locale-builder/locales.js +130 -0
  116. package/dist/modules/lockfile/hasher.d.ts +7 -0
  117. package/dist/modules/lockfile/hasher.js +13 -0
  118. package/dist/modules/lockfile/index.d.ts +3 -0
  119. package/dist/modules/lockfile/index.js +22 -0
  120. package/dist/modules/lockfile/lockfile-manager.d.ts +99 -0
  121. package/dist/modules/lockfile/lockfile-manager.js +497 -0
  122. package/dist/modules/lockfile/schema.d.ts +196 -0
  123. package/dist/modules/lockfile/schema.js +56 -0
  124. package/dist/modules/parser/index.d.ts +2 -0
  125. package/dist/modules/parser/index.js +9 -0
  126. package/dist/modules/summary/index.d.ts +1 -0
  127. package/dist/modules/summary/index.js +7 -0
  128. package/dist/modules/summary/summary.d.ts +9 -0
  129. package/dist/modules/summary/summary.js +195 -0
  130. package/dist/modules/sync-detector/index.d.ts +2 -0
  131. package/dist/modules/sync-detector/index.js +7 -0
  132. package/dist/modules/sync-detector/reporter.d.ts +3 -0
  133. package/dist/modules/sync-detector/reporter.js +16 -0
  134. package/dist/modules/sync-detector/sync-detector.d.ts +39 -0
  135. package/dist/modules/sync-detector/sync-detector.js +374 -0
  136. package/dist/modules/translator/batch-optimizer.d.ts +12 -0
  137. package/dist/modules/translator/batch-optimizer.js +30 -0
  138. package/dist/modules/translator/client/byok.provider.d.ts +64 -0
  139. package/dist/modules/translator/client/byok.provider.js +319 -0
  140. package/dist/modules/translator/client/direct-translator.client.d.ts +12 -0
  141. package/dist/modules/translator/client/direct-translator.client.js +71 -0
  142. package/dist/modules/translator/client/gemini.provider.d.ts +1 -0
  143. package/dist/modules/translator/client/gemini.provider.js +110 -0
  144. package/dist/modules/translator/client/groq.provider.d.ts +1 -0
  145. package/dist/modules/translator/client/groq.provider.js +111 -0
  146. package/dist/modules/translator/client/request.types.d.ts +1 -0
  147. package/dist/modules/translator/client/request.types.js +2 -0
  148. package/dist/modules/translator/client/retry.d.ts +29 -0
  149. package/dist/modules/translator/client/retry.js +94 -0
  150. package/dist/modules/translator/client/test-utils/mock.client.d.ts +31 -0
  151. package/dist/modules/translator/client/test-utils/mock.client.js +59 -0
  152. package/dist/modules/translator/client/translator.interface.d.ts +1 -0
  153. package/dist/modules/translator/client/translator.interface.js +2 -0
  154. package/dist/modules/translator/icu-branches.d.ts +138 -0
  155. package/dist/modules/translator/icu-branches.js +393 -0
  156. package/dist/modules/translator/language-codes/errors.d.ts +10 -0
  157. package/dist/modules/translator/language-codes/errors.js +18 -0
  158. package/dist/modules/translator/language-codes/flores-map.d.ts +1 -0
  159. package/dist/modules/translator/language-codes/flores-map.js +107 -0
  160. package/dist/modules/translator/language-codes/index.d.ts +11 -0
  161. package/dist/modules/translator/language-codes/index.js +24 -0
  162. package/dist/modules/translator/language-codes/language-mapper.d.ts +25 -0
  163. package/dist/modules/translator/language-codes/language-mapper.js +132 -0
  164. package/dist/modules/translator/language-codes/rtl-languages.d.ts +2 -0
  165. package/dist/modules/translator/language-codes/rtl-languages.js +9 -0
  166. package/dist/modules/translator/memory/memory-matcher.d.ts +8 -0
  167. package/dist/modules/translator/memory/memory-matcher.js +218 -0
  168. package/dist/modules/translator/memory/types.d.ts +22 -0
  169. package/dist/modules/translator/memory/types.js +2 -0
  170. package/dist/modules/translator/translate.d.ts +74 -0
  171. package/dist/modules/translator/translate.js +1047 -0
  172. package/dist/modules/vue/migrate.d.ts +13 -0
  173. package/dist/modules/vue/migrate.js +40 -0
  174. package/dist/modules/vue/script-inject.d.ts +16 -0
  175. package/dist/modules/vue/script-inject.js +115 -0
  176. package/dist/modules/vue/sfc.d.ts +11 -0
  177. package/dist/modules/vue/sfc.js +51 -0
  178. package/dist/modules/vue/template-rewrite.d.ts +25 -0
  179. package/dist/modules/vue/template-rewrite.js +76 -0
  180. package/dist/modules/vue/template-scan.d.ts +29 -0
  181. package/dist/modules/vue/template-scan.js +264 -0
  182. package/dist/modules/workspace/index.d.ts +1 -0
  183. package/dist/modules/workspace/index.js +5 -0
  184. package/dist/modules/workspace/validate.d.ts +13 -0
  185. package/dist/modules/workspace/validate.js +333 -0
  186. package/dist/orchestrator.d.ts +13 -0
  187. package/dist/orchestrator.js +727 -0
  188. package/dist/providers/default-translation-provider.d.ts +20 -0
  189. package/dist/providers/default-translation-provider.js +30 -0
  190. package/dist/providers/index.d.ts +2 -0
  191. package/dist/providers/index.js +21 -0
  192. package/dist/providers/types.d.ts +153 -0
  193. package/dist/providers/types.js +2 -0
  194. package/dist/qa/check-project.d.ts +50 -0
  195. package/dist/qa/check-project.js +193 -0
  196. package/dist/qa/gettext-files.d.ts +15 -0
  197. package/dist/qa/gettext-files.js +115 -0
  198. package/dist/qa/locale-files.d.ts +44 -0
  199. package/dist/qa/locale-files.js +159 -0
  200. package/dist/qa/locale-qa.d.ts +61 -0
  201. package/dist/qa/locale-qa.js +342 -0
  202. package/dist/qa/review.d.ts +43 -0
  203. package/dist/qa/review.js +175 -0
  204. package/dist/runtime/config.d.ts +8 -0
  205. package/dist/runtime/config.js +37 -0
  206. package/dist/runtime/index.d.ts +3 -0
  207. package/dist/runtime/index.js +8 -0
  208. package/dist/runtime/types.d.ts +427 -0
  209. package/dist/runtime/types.js +38 -0
  210. package/dist/types.d.ts +134 -0
  211. package/dist/types.js +12 -0
  212. package/dist/utils/ast-helpers.d.ts +57 -0
  213. package/dist/utils/ast-helpers.js +608 -0
  214. package/dist/utils/cache-keys.d.ts +8 -0
  215. package/dist/utils/cache-keys.js +18 -0
  216. package/dist/utils/circuit-breaker.d.ts +43 -0
  217. package/dist/utils/circuit-breaker.js +119 -0
  218. package/dist/utils/concurrent-map.d.ts +4 -0
  219. package/dist/utils/concurrent-map.js +21 -0
  220. package/dist/utils/detect-format.d.ts +5 -0
  221. package/dist/utils/detect-format.js +125 -0
  222. package/dist/utils/extraction-options.d.ts +29 -0
  223. package/dist/utils/extraction-options.js +33 -0
  224. package/dist/utils/file-utils.d.ts +33 -0
  225. package/dist/utils/file-utils.js +267 -0
  226. package/dist/utils/glossary-match.d.ts +9 -0
  227. package/dist/utils/glossary-match.js +25 -0
  228. package/dist/utils/html-extractor.d.ts +8 -0
  229. package/dist/utils/html-extractor.js +317 -0
  230. package/dist/utils/i18next-icu.d.ts +38 -0
  231. package/dist/utils/i18next-icu.js +159 -0
  232. package/dist/utils/icu-message.d.ts +65 -0
  233. package/dist/utils/icu-message.js +346 -0
  234. package/dist/utils/icu-spans.d.ts +10 -0
  235. package/dist/utils/icu-spans.js +126 -0
  236. package/dist/utils/jsx-message.d.ts +93 -0
  237. package/dist/utils/jsx-message.js +628 -0
  238. package/dist/utils/key-usage.d.ts +35 -0
  239. package/dist/utils/key-usage.js +247 -0
  240. package/dist/utils/logger.d.ts +24 -0
  241. package/dist/utils/logger.js +33 -0
  242. package/dist/utils/metadata.d.ts +82 -0
  243. package/dist/utils/metadata.js +261 -0
  244. package/dist/utils/module-constants.d.ts +30 -0
  245. package/dist/utils/module-constants.js +265 -0
  246. package/dist/utils/not-injected.d.ts +6 -0
  247. package/dist/utils/not-injected.js +38 -0
  248. package/dist/utils/placeholder-protection.d.ts +74 -0
  249. package/dist/utils/placeholder-protection.js +181 -0
  250. package/dist/utils/pseudo-markers.d.ts +9 -0
  251. package/dist/utils/pseudo-markers.js +16 -0
  252. package/dist/utils/string-utils.d.ts +52 -0
  253. package/dist/utils/string-utils.js +270 -0
  254. package/dist/utils/translation-quality.d.ts +34 -0
  255. package/dist/utils/translation-quality.js +156 -0
  256. package/dist/utils/translation-sanitizer.d.ts +11 -0
  257. package/dist/utils/translation-sanitizer.js +71 -0
  258. package/dist/utils/translator-caller.d.ts +32 -0
  259. package/dist/utils/translator-caller.js +217 -0
  260. package/dist/utils/translator-load-balancer.d.ts +73 -0
  261. package/dist/utils/translator-load-balancer.js +297 -0
  262. package/dist/utils/ui-copy.d.ts +32 -0
  263. package/dist/utils/ui-copy.js +126 -0
  264. package/dist/utils/untranslated.d.ts +15 -0
  265. package/dist/utils/untranslated.js +34 -0
  266. package/dist/utils/workspace.d.ts +14 -0
  267. package/dist/utils/workspace.js +89 -0
  268. package/package.json +64 -4
  269. package/schemas/translate-report.schema.json +297 -0
  270. package/README.md +0 -3
@@ -0,0 +1,499 @@
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.LocalTranslateError = exports.BETA_LOCAL_FRAMEWORKS = exports.SUPPORTED_LOCAL_FRAMEWORKS = exports.LOCAL_LOCKFILE = exports.LOCAL_PROJECT_ID = void 0;
37
+ exports.configRulesStore = configRulesStore;
38
+ exports.parseLocales = parseLocales;
39
+ exports.validateLocales = validateLocales;
40
+ exports.sentence = sentence;
41
+ exports.isEngineLogNoise = isEngineLogNoise;
42
+ exports.buildLocaleReports = buildLocaleReports;
43
+ exports.runLocalTranslate = runLocalTranslate;
44
+ const node_child_process_1 = require("node:child_process");
45
+ const promises_1 = require("node:fs/promises");
46
+ const path = __importStar(require("node:path"));
47
+ const node_util_1 = require("node:util");
48
+ const language_codes_1 = require("../modules/translator/language-codes");
49
+ const locale_names_1 = require("../django/locale-names");
50
+ const lockfile_1 = require("../modules/lockfile");
51
+ const orchestrator_1 = require("../orchestrator");
52
+ const logger_1 = require("../utils/logger");
53
+ const workspace_1 = require("../modules/workspace");
54
+ const check_project_1 = require("../qa/check-project");
55
+ /** A rules store serving a `.babelize/config.json` glossary (no brand voice locally). */
56
+ function configRulesStore(glossary) {
57
+ const rules = [
58
+ ...(glossary.neverTranslate ?? []).map((term) => ({ term, type: 'never_translate', translations: null })),
59
+ ...(glossary.alwaysTranslateAs ?? []).map((e) => ({ term: e.term, type: 'always_translate_as', translations: e.translations })),
60
+ ];
61
+ return {
62
+ getGlossary: async () => rules,
63
+ getBrandVoice: async () => null,
64
+ };
65
+ }
66
+ const types_1 = require("@babelize/types");
67
+ const git_1 = require("./git");
68
+ const formatter_1 = require("./formatter");
69
+ const report_1 = require("./report");
70
+ const workspace_copy_1 = require("./workspace-copy");
71
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
72
+ /** Lockfile scope for local runs: `.babelize/local/babelize-lock.json` in the project root. */
73
+ exports.LOCAL_PROJECT_ID = 'local';
74
+ exports.LOCAL_LOCKFILE = `.babelize/${exports.LOCAL_PROJECT_ID}/babelize-lock.json`;
75
+ exports.SUPPORTED_LOCAL_FRAMEWORKS = ['react', 'nextjs', 'django'];
76
+ exports.BETA_LOCAL_FRAMEWORKS = ['vue', 'static'];
77
+ class LocalTranslateError extends Error {
78
+ constructor(message, code,
79
+ /** Files left changed in the project (only for a failed, un-rolled-back apply). */
80
+ changedFiles = []) {
81
+ super(message);
82
+ this.code = code;
83
+ this.changedFiles = changedFiles;
84
+ this.name = 'LocalTranslateError';
85
+ }
86
+ }
87
+ exports.LocalTranslateError = LocalTranslateError;
88
+ /** Normalise `--to fr,de , ja` into a deduped list without the source locale. */
89
+ function parseLocales(raw, sourceLocale) {
90
+ const parts = (Array.isArray(raw) ? raw : (raw ?? '').split(','))
91
+ .flatMap((p) => p.split(','))
92
+ .map((p) => p.trim())
93
+ .filter(Boolean);
94
+ return [...new Set(parts)].filter((l) => l !== sourceLocale);
95
+ }
96
+ function validateLocales(sourceLocale, targetLocales) {
97
+ const bad = [sourceLocale, ...targetLocales].filter((l) => !(0, language_codes_1.isSupported)(l));
98
+ if (bad.length > 0) {
99
+ throw new LocalTranslateError(`Unsupported locale code${bad.length > 1 ? 's' : ''}: ${bad.join(', ')}. Use ISO codes like fr, de, ja, pt-BR, zh-CN.`, 'invalid_locale');
100
+ }
101
+ if (targetLocales.length === 0) {
102
+ throw new LocalTranslateError('No target locales given. Pass --to, e.g. --to fr,de.', 'invalid_locale');
103
+ }
104
+ }
105
+ /** Tracked + untracked-but-not-ignored files under `dir`, relative to it. */
106
+ async function listProjectFiles(dir) {
107
+ const { stdout } = await execFileAsync('git', ['ls-files', '-z', '--cached', '--others', '--exclude-standard', '--', '.'], { cwd: dir, maxBuffer: 256 * 1024 * 1024 });
108
+ return stdout.split('\0').filter(Boolean);
109
+ }
110
+ /** Locales this project was already translated into: locale files plus lockfile entries. */
111
+ async function existingTargetLocales(workspaceDir, publicDir, localeFormat, sourceLocale) {
112
+ const found = new Set();
113
+ if (localeFormat === 'po') {
114
+ // Django: <locale dir>/<pt_BR>/LC_MESSAGES/django.po
115
+ for (const entry of await (0, promises_1.readdir)(path.join(workspaceDir, publicDir)).catch(() => [])) {
116
+ const po = path.join(workspaceDir, publicDir, entry, 'LC_MESSAGES', 'django.po');
117
+ if (await (0, promises_1.readFile)(po, 'utf8').then(() => true, () => false))
118
+ found.add((0, locale_names_1.localeFromDjangoName)(entry));
119
+ }
120
+ }
121
+ else {
122
+ const ext = localeFormat === 'arb' ? '.arb' : '.json';
123
+ for (const entry of await (0, promises_1.readdir)(path.join(workspaceDir, publicDir, 'locales')).catch(() => [])) {
124
+ if (entry.endsWith(ext))
125
+ found.add(entry.slice(0, -ext.length));
126
+ }
127
+ }
128
+ try {
129
+ const lock = JSON.parse(await (0, promises_1.readFile)(path.join(workspaceDir, exports.LOCAL_LOCKFILE), 'utf8'));
130
+ for (const entry of Object.values(lock.keys ?? {})) {
131
+ for (const locale of Object.keys(entry.languages ?? {}))
132
+ found.add(locale);
133
+ }
134
+ }
135
+ catch {
136
+ // No lockfile yet.
137
+ }
138
+ found.delete(sourceLocale);
139
+ return [...found].filter((l) => (0, language_codes_1.isSupported)(l)).sort();
140
+ }
141
+ /**
142
+ * Engine log lines that are wrong or redundant for a local run: the
143
+ * TRANSLATOR_URL check doesn't know about BYOK/injected providers, and the
144
+ * "run your install" hint is already a finalization note in the summary.
145
+ */
146
+ /** `msg` as one sentence: trailing dots/spaces trimmed, exactly one period added. */
147
+ function sentence(msg) {
148
+ const trimmed = msg.trim().replace(/[.\s]+$/, '');
149
+ return /[!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
150
+ }
151
+ const ENGINE_LOG_NOISE = [
152
+ /TRANSLATOR_URL environment variable is not set/,
153
+ /TRANSLATOR_URL not configured/,
154
+ /^New dependencies added\. Please run your package manager install/,
155
+ // Reported in full in the summary (failures / not-wired strings).
156
+ /^Some strings could not be translated/,
157
+ /^Extracted strings not wired into code/,
158
+ ];
159
+ function isEngineLogNoise(message) {
160
+ return ENGINE_LOG_NOISE.some((re) => re.test(message));
161
+ }
162
+ async function readJsonIfExists(file) {
163
+ try {
164
+ return JSON.parse(await (0, promises_1.readFile)(file, 'utf8'));
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ }
170
+ function flattenData(data, prefix = '', out = {}) {
171
+ if (data && typeof data === 'object') {
172
+ for (const [k, v] of Object.entries(data)) {
173
+ const key = prefix ? `${prefix}.${k}` : k;
174
+ if (v && typeof v === 'object')
175
+ flattenData(v, key, out);
176
+ else if (typeof v === 'string')
177
+ out[key] = v;
178
+ }
179
+ }
180
+ return out;
181
+ }
182
+ function sortKeysDeep(value) {
183
+ if (!value || typeof value !== 'object' || Array.isArray(value))
184
+ return value;
185
+ return Object.fromEntries(Object.keys(value)
186
+ .sort()
187
+ .map((k) => [k, sortKeysDeep(value[k])]));
188
+ }
189
+ function frameworkStatusOf(framework) {
190
+ if (exports.SUPPORTED_LOCAL_FRAMEWORKS.includes(framework))
191
+ return 'supported';
192
+ if (exports.BETA_LOCAL_FRAMEWORKS.includes(framework))
193
+ return 'beta';
194
+ return null;
195
+ }
196
+ function stripMeta(flat) {
197
+ const out = {};
198
+ for (const [k, v] of Object.entries(flat ?? {})) {
199
+ if (k === '_meta' || k.startsWith('_meta.'))
200
+ continue;
201
+ out[k] = v;
202
+ }
203
+ return out;
204
+ }
205
+ /** Text that is only digits, symbols or whitespace is legitimately identical in every locale. */
206
+ function hasLetters(text) {
207
+ return /\p{L}/u.test(text);
208
+ }
209
+ function buildLocaleReports(targetLocales, source, targets, failures = [], metadata = {}) {
210
+ const src = stripMeta(source);
211
+ const keys = Object.keys(src);
212
+ return targetLocales.map((locale) => {
213
+ const target = stripMeta(targets[locale]);
214
+ const missing = [];
215
+ const sameAsSource = [];
216
+ let translated = 0;
217
+ for (const key of keys) {
218
+ const value = target[key];
219
+ if (value === undefined || value === '') {
220
+ missing.push(key);
221
+ continue;
222
+ }
223
+ translated++;
224
+ if (value === src[key] && hasLetters(src[key]))
225
+ sameAsSource.push(key);
226
+ }
227
+ const failed = failures
228
+ .filter((f) => f.locale === locale)
229
+ .map((f) => ({ key: f.key, reason: f.reason, message: f.message, retryable: f.retryable }));
230
+ const qa = [];
231
+ for (const [key, meta] of Object.entries(metadata[locale] ?? {})) {
232
+ const warnings = meta?.qa;
233
+ if (Array.isArray(warnings) && warnings.length > 0)
234
+ qa.push({ key, warnings: warnings.map(String) });
235
+ }
236
+ return { locale, total: keys.length, translated, missing, failed, qa, sameAsSource };
237
+ });
238
+ }
239
+ function describeEngineFailure(error, byok) {
240
+ if (byok && /translator service (is )?(unavailable|not responding)/i.test(error)) {
241
+ const name = types_1.PROVIDER_DEFINITIONS[byok.provider].name;
242
+ return new LocalTranslateError(`Could not reach ${name} with your API key (invalid key, no access, or network error). Nothing was changed.`, 'translator_unreachable');
243
+ }
244
+ return new LocalTranslateError(`Translation failed: ${sentence(error)} Nothing was changed.`, 'engine_failed');
245
+ }
246
+ function hostedFailure(err) {
247
+ return new LocalTranslateError(`${sentence(err.message)} Nothing was changed.`, err.code === 'hosted_credits' ? 'hosted_credits' : 'hosted_auth');
248
+ }
249
+ /**
250
+ * Run the Babelize engine on a temp copy of `projectDir` and, unless
251
+ * `dryRun`, apply the result back to the working tree. The project is never
252
+ * touched until the engine has finished successfully and every target locale
253
+ * has translations.
254
+ */
255
+ async function runLocalTranslate(opts, deps = {}) {
256
+ // Engine logs go to this run only (not the process-wide logger), so
257
+ // concurrent runs in one process (the MCP server) keep separate logs.
258
+ const engineLog = [];
259
+ const state = { collecting: false };
260
+ const log = (level) => (message) => {
261
+ if (state.collecting && !isEngineLogNoise(message))
262
+ engineLog.push({ level, message });
263
+ };
264
+ const logger = { debug: () => { }, info: () => { }, warn: log('warn'), error: log('error') };
265
+ return (0, logger_1.runWithEngineLogger)(logger, () => runScoped(opts, deps, engineLog, state));
266
+ }
267
+ async function runScoped(opts, deps, engineLog, state) {
268
+ const startedAt = Date.now();
269
+ const projectDir = path.resolve(opts.projectDir);
270
+ try {
271
+ await (0, git_1.assertGitRepo)(projectDir);
272
+ // A dry run writes nothing, so it is safe on a dirty tree.
273
+ if (!opts.dryRun)
274
+ await (0, git_1.assertCleanTree)(projectDir, opts.force);
275
+ }
276
+ catch (err) {
277
+ if (err instanceof git_1.GitError)
278
+ throw new LocalTranslateError(err.message, err.code);
279
+ throw err;
280
+ }
281
+ validateLocales(opts.sourceLocale, opts.targetLocales);
282
+ const translator = deps.hosted ?? deps.translator;
283
+ if (!opts.byok && !translator) {
284
+ throw new LocalTranslateError('No translation provider configured.', 'no_translator');
285
+ }
286
+ const files = await listProjectFiles(projectDir);
287
+ const temp = await (0, workspace_copy_1.createTempCopy)(projectDir, { files, tmpRoot: deps.tmpRoot });
288
+ try {
289
+ // Framework detection runs on the copy (it creates .babelize/ as a side effect).
290
+ let framework;
291
+ let publicDir = 'public';
292
+ let localeFormat = 'json';
293
+ try {
294
+ const { workspace } = await (0, workspace_1.validateWorkspace)({
295
+ workspacePath: temp.dir,
296
+ inputType: 'zip',
297
+ sourceLocale: opts.sourceLocale,
298
+ targetLocales: opts.targetLocales,
299
+ confidenceThreshold: 80,
300
+ dryRun: true,
301
+ });
302
+ framework = workspace.framework;
303
+ publicDir = workspace.publicDir;
304
+ localeFormat = workspace.localeFormat;
305
+ }
306
+ catch (err) {
307
+ throw new LocalTranslateError(err.message, 'unsupported_framework');
308
+ }
309
+ const frameworkStatus = frameworkStatusOf(framework);
310
+ if (!frameworkStatus) {
311
+ throw new LocalTranslateError(`Detected framework "${framework}", which \`babelize translate\` does not support. Supported: React, Next.js and Django (Vue and static HTML in beta).`, 'unsupported_framework');
312
+ }
313
+ deps.onFramework?.(framework, frameworkStatus);
314
+ // The engine deletes locale files that aren't in its target list, so
315
+ // `--to ja` after `--to fr,de` would silently drop fr and de. Unless the
316
+ // user asked to replace the set, earlier locales stay in it; their
317
+ // translations come from the lockfile, so only new strings cost tokens.
318
+ const earlier = await existingTargetLocales(temp.dir, publicDir, localeFormat, opts.sourceLocale);
319
+ const keptLocales = opts.replace ? [] : earlier.filter((l) => !opts.targetLocales.includes(l));
320
+ const droppedLocales = opts.replace ? earlier.filter((l) => !opts.targetLocales.includes(l)) : [];
321
+ const targetLocales = [...opts.targetLocales, ...keptLocales];
322
+ const before = await (0, workspace_copy_1.snapshot)(temp.dir);
323
+ state.collecting = true;
324
+ // The project's glossary from .babelize/config.json — the same file and
325
+ // schema `babelize check` reads — applied like a cloud project's.
326
+ const { glossary } = await (0, check_project_1.readProjectLocaleConfig)(projectDir);
327
+ const rules = glossary ? configRulesStore(glossary) : undefined;
328
+ const runEngine = deps.runEngine ?? orchestrator_1.runEngineV2;
329
+ const result = await runEngine({
330
+ workspacePath: temp.dir,
331
+ inputType: 'zip',
332
+ framework: framework,
333
+ sourceLocale: opts.sourceLocale,
334
+ targetLocales: [...targetLocales],
335
+ confidenceThreshold: 80,
336
+ dryRun: false,
337
+ projectId: exports.LOCAL_PROJECT_ID,
338
+ // The lockfile lives at <dataDir>/.babelize/local/. Pointing it at the
339
+ // copy means the project's committed lockfile is read (it was copied)
340
+ // and the updated one comes back with the other changes — only on success.
341
+ dataDir: temp.dir,
342
+ byok: translator ? undefined : opts.byok,
343
+ providers: translator || rules ? { ...(translator ? { translator } : {}), ...(rules ? { rules } : {}) } : undefined,
344
+ installDependencies: false,
345
+ onProgress: (message, step, status) => deps.onProgress?.(message, step, status),
346
+ });
347
+ const hostedFatal = deps.hosted?.fatalError;
348
+ if (!result.success) {
349
+ if (hostedFatal)
350
+ throw hostedFailure(hostedFatal);
351
+ throw describeEngineFailure(result.error ?? 'unknown engine error', translator ? undefined : opts.byok);
352
+ }
353
+ // --replace: forget dropped locales in the lockfile too, or a later run
354
+ // would rebuild their files from it.
355
+ for (const locale of droppedLocales)
356
+ await (0, lockfile_1.removeLocaleFromLockfile)(exports.LOCAL_PROJECT_ID, locale, temp.dir);
357
+ const source = stripMeta(result.results?.source);
358
+ const targets = result.results?.targets ?? {};
359
+ const translationFailures = result.translationFailures ?? [];
360
+ const metadata = (result.results?.metadata ?? {});
361
+ const locales = buildLocaleReports(targetLocales, source, targets, translationFailures, metadata);
362
+ // A locale with source keys but zero translations (nothing, or only copies
363
+ // of the source text) is a failed run, not a partial one: refuse to write
364
+ // it rather than ship an untranslated language as if it were done.
365
+ const empty = locales.filter((l) => l.total > 0 && l.translated - l.sameAsSource.length === 0);
366
+ if (empty.length > 0) {
367
+ if (hostedFatal)
368
+ throw hostedFailure(hostedFatal);
369
+ throw new LocalTranslateError(`No translations were produced for ${empty.map((l) => l.locale).join(', ')}. Nothing was changed.`, 'incomplete_translation');
370
+ }
371
+ // The engine's result carries each target locale's complete data (fresh
372
+ // translations merged with lockfile-cached ones); its on-disk files can
373
+ // hold only this run's new keys. Write the complete data, exactly as the
374
+ // cloud backend stages locale files for a PR.
375
+ const lost = [];
376
+ for (const locale of result.locales) {
377
+ if (locale.locale === opts.sourceLocale || locale.noFile)
378
+ continue;
379
+ const rel = locale.path.split(path.sep).join('/');
380
+ if (locale.content !== undefined) {
381
+ // A merged catalogue (Django .po): written exactly as the engine
382
+ // produced it. The merge never drops existing translations.
383
+ for (const f of [{ path: rel, content: locale.content, encoding: 'utf8' }, ...(locale.extraFiles ?? [])]) {
384
+ const abs = path.resolve(temp.dir, f.path.split(path.sep).join('/'));
385
+ if (path.relative(temp.dir, abs).startsWith('..'))
386
+ continue;
387
+ await (0, promises_1.mkdir)(path.dirname(abs), { recursive: true });
388
+ await (0, promises_1.writeFile)(abs, f.encoding === 'base64' ? Buffer.from(f.content, 'base64') : f.content);
389
+ }
390
+ continue;
391
+ }
392
+ const previous = await readJsonIfExists(path.join(projectDir, rel));
393
+ const next = flattenData(locale.data);
394
+ const failedHere = new Set(translationFailures.filter((f) => f.locale === locale.locale).map((f) => f.key));
395
+ for (const [key, value] of Object.entries(flattenData(previous ?? {}))) {
396
+ // A key whose re-translation failed is reported as a failure instead.
397
+ if (key.startsWith('_meta') || !value || failedHere.has(key))
398
+ continue;
399
+ if (!next[key])
400
+ lost.push(`${locale.locale}: ${key}`);
401
+ }
402
+ const abs = path.resolve(temp.dir, rel);
403
+ if (path.relative(temp.dir, abs).startsWith('..'))
404
+ continue;
405
+ await (0, promises_1.mkdir)(path.dirname(abs), { recursive: true });
406
+ await (0, promises_1.writeFile)(abs, `${JSON.stringify(sortKeysDeep(locale.data), null, 2)}\n`);
407
+ }
408
+ // The QA report (.babelize/local/report.json) stays out of git; the
409
+ // lockfile next to it is committed. Shipped with the run's other changes.
410
+ await (0, report_1.ensureReportGitignored)(temp.dir);
411
+ // Never ship a run that would delete translations the project already has.
412
+ if (lost.length > 0) {
413
+ throw new LocalTranslateError(`This run would remove ${lost.length} existing translation(s) (${lost.slice(0, 5).join(', ')}${lost.length > 5 ? ', …' : ''}). Nothing was changed.`, 'incomplete_translation');
414
+ }
415
+ let changes = (0, workspace_copy_1.diffSnapshots)(before, await (0, workspace_copy_1.snapshot)(temp.dir));
416
+ const formatter = (await (0, formatter_1.formatChangedFiles)(projectDir, temp.dir, [...changes.added, ...changes.modified])) ?? undefined;
417
+ if (formatter?.ok)
418
+ changes = (0, workspace_copy_1.diffSnapshots)(before, await (0, workspace_copy_1.snapshot)(temp.dir));
419
+ const diff = opts.dryRun ? await (0, workspace_copy_1.renderUnifiedDiff)(changes, projectDir, temp.dir) : undefined;
420
+ // Before apply: the project still holds the old content.
421
+ const diffStat = await (0, workspace_copy_1.diffStats)(changes, projectDir, temp.dir);
422
+ if (!opts.dryRun && (0, workspace_copy_1.changeCount)(changes) > 0) {
423
+ const conflicts = await (0, workspace_copy_1.findConflicts)(changes, before, projectDir);
424
+ if (conflicts.length > 0) {
425
+ throw new LocalTranslateError(`These files changed in your project while translate was running (or exist but weren't part of the copy), so nothing was applied:\n` +
426
+ conflicts.map((f) => ` ${f}`).join('\n') +
427
+ '\nCommit or discard those edits, then run translate again.', 'project_changed');
428
+ }
429
+ try {
430
+ await (0, workspace_copy_1.applyChanges)(changes, temp.dir, projectDir, deps.applyIo);
431
+ }
432
+ catch (err) {
433
+ if (!(err instanceof workspace_copy_1.ApplyError))
434
+ throw err;
435
+ if (err.rolledBack) {
436
+ throw new LocalTranslateError(`${sentence(err.message)} Nothing was changed.`, 'apply_failed');
437
+ }
438
+ throw new LocalTranslateError(`${err.message} These files are still changed:\n` +
439
+ err.changedFiles.map((f) => ` ${f}`).join('\n') +
440
+ `\nRestore them with: git checkout -- ${err.changedFiles.join(' ')} (and delete any of them that are new).`, 'apply_failed', err.changedFiles);
441
+ }
442
+ }
443
+ // Partial run cut short by the key or the balance: keep what was
444
+ // translated (it was paid for) and say why the rest is missing.
445
+ if (hostedFatal)
446
+ engineLog.unshift({ level: 'error', message: hostedFatal.message });
447
+ const s = result.stats;
448
+ const provider = deps.hosted
449
+ ? { provider: 'babelize', name: 'Babelize hosted', model: deps.hosted.model }
450
+ : !translator && opts.byok
451
+ ? {
452
+ provider: opts.byok.provider,
453
+ name: types_1.PROVIDER_DEFINITIONS[opts.byok.provider].name,
454
+ model: opts.byok.model ?? types_1.PROVIDER_DEFINITIONS[opts.byok.provider].defaultModel,
455
+ }
456
+ : deps.translatorInfo;
457
+ return {
458
+ framework,
459
+ frameworkStatus,
460
+ provider,
461
+ sourceLocale: opts.sourceLocale,
462
+ targetLocales,
463
+ keptLocales,
464
+ droppedLocales,
465
+ unusedKeys: result.keyUsage?.unused ?? [],
466
+ notInjected: result.notInjected ?? [],
467
+ dynamicKeyFiles: result.keyUsage?.dynamicFiles ?? [],
468
+ dryRun: opts.dryRun,
469
+ applied: !opts.dryRun && (0, workspace_copy_1.changeCount)(changes) > 0,
470
+ changes,
471
+ diff,
472
+ diffStat,
473
+ stats: {
474
+ filesScanned: s.filesScanned,
475
+ stringsExtracted: s.stringsExtracted,
476
+ keysExtracted: s.keysGenerated,
477
+ injectionsApplied: s.injectionsApplied,
478
+ keysFromLockfile: s.translationsFromLockfile ?? 0,
479
+ keysSentToTranslator: s.lockfileKeysTranslated ?? 0,
480
+ translatedThisRun: s.translatedThisRun,
481
+ },
482
+ locales,
483
+ translationFailures,
484
+ warnings: result.warnings.map((w) => ({ type: w.type, file: w.file, line: w.line, message: w.message })),
485
+ notes: result.notes ?? [],
486
+ formatter,
487
+ engineLog,
488
+ lockfile: exports.LOCAL_LOCKFILE,
489
+ lockfileIgnored: await (0, git_1.isGitIgnored)(projectDir, exports.LOCAL_LOCKFILE),
490
+ metadata,
491
+ results: { source, targets: Object.fromEntries(Object.entries(targets).map(([l, t]) => [l, stripMeta(t)])) },
492
+ startedAt,
493
+ durationMs: Date.now() - startedAt,
494
+ };
495
+ }
496
+ finally {
497
+ await temp.cleanup();
498
+ }
499
+ }
@@ -0,0 +1,49 @@
1
+ import type { ByokProvider } from '@babelize/types';
2
+ import type { TranslationFailure, TranslationProvider } from '../providers/types';
3
+ /**
4
+ * Translate a flat key → text map with the engine's translate phase, without
5
+ * a project: the same placeholder protection, ICU/plural handling, glossary,
6
+ * validation and repair pass as `babelize translate`, but no extraction or
7
+ * injection and nothing written anywhere. Used by the golden-set quality run.
8
+ */
9
+ export interface TranslateMessagesOptions {
10
+ sourceLocale: string;
11
+ targetLocales: string[];
12
+ /** Flat key → source text. Keys must not contain dots. */
13
+ messages: Record<string, string>;
14
+ /** The user's LLM key. Required unless `translator` is given. */
15
+ byok?: {
16
+ provider: ByokProvider;
17
+ apiKey: string;
18
+ model?: string;
19
+ };
20
+ translator?: TranslationProvider;
21
+ glossary?: {
22
+ neverTranslate?: string[];
23
+ alwaysTranslateAs?: Array<{
24
+ term: string;
25
+ translations: Record<string, string>;
26
+ }>;
27
+ };
28
+ /**
29
+ * The app's runtime formats ICU (next-intl, i18next-icu): count
30
+ * placeholders may become plurals. Default true.
31
+ */
32
+ targetPlurals?: boolean;
33
+ onProgress?: (message: string) => void;
34
+ /** Engine warnings/errors (silent by default). */
35
+ onLog?: (level: 'warn' | 'error', message: string) => void;
36
+ }
37
+ export interface TranslateMessagesResult {
38
+ /** locale → key → translation (keys that failed are absent). */
39
+ targets: Record<string, Record<string, string>>;
40
+ /** locale → key → engine metadata (`qa` warnings, confidence, …). */
41
+ metadata: Record<string, Record<string, unknown>>;
42
+ failures: TranslationFailure[];
43
+ /** Locales that failed as a whole. */
44
+ errors: Array<{
45
+ locale: string;
46
+ error: string;
47
+ }>;
48
+ }
49
+ export declare function translateMessages(opts: TranslateMessagesOptions): Promise<TranslateMessagesResult>;
@@ -0,0 +1,89 @@
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.translateMessages = translateMessages;
37
+ const promises_1 = require("node:fs/promises");
38
+ const node_os_1 = require("node:os");
39
+ const path = __importStar(require("node:path"));
40
+ const keygen_1 = require("../modules/keygen");
41
+ const translate_1 = require("../modules/translator/translate");
42
+ const glossary_1 = require("../modules/glossary");
43
+ const logger_1 = require("../utils/logger");
44
+ async function translateMessages(opts) {
45
+ const logger = {
46
+ debug: () => { },
47
+ info: () => { },
48
+ warn: (message) => opts.onLog?.('warn', message),
49
+ error: (message) => opts.onLog?.('error', message),
50
+ };
51
+ return (0, logger_1.runWithEngineLogger)(logger, () => run(opts));
52
+ }
53
+ async function run(opts) {
54
+ if (!opts.byok && !opts.translator)
55
+ throw new Error('translateMessages needs a byok key or a translator.');
56
+ const dir = await (0, promises_1.mkdtemp)(path.join((0, node_os_1.tmpdir)(), 'babelize-messages-'));
57
+ try {
58
+ // The glossary goes in as already-loaded rules, normalized like a project's.
59
+ const glossary = (0, glossary_1.normalizeGlossary)([
60
+ ...(opts.glossary?.neverTranslate ?? []).map((term) => ({ term, type: 'never_translate' })),
61
+ ...(opts.glossary?.alwaysTranslateAs ?? []).map((e) => ({ term: e.term, type: 'always_translate_as', translations: e.translations })),
62
+ ]);
63
+ const workspace = {
64
+ path: dir,
65
+ framework: 'react',
66
+ sourceDirs: [],
67
+ hasLocalesDir: false,
68
+ existingLocales: [],
69
+ publicDir: 'public',
70
+ localeFormat: 'json',
71
+ };
72
+ const result = await (0, translate_1.translateLocales)(workspace, { locale: opts.sourceLocale, path: '', data: { ...opts.messages }, isNew: false }, opts.targetLocales, false, true, // dry run: nothing written
73
+ undefined, opts.onProgress, undefined, undefined, opts.translator ? undefined : opts.byok, undefined, undefined, opts.translator ? { translator: opts.translator } : {}, { targetPlurals: opts.targetPlurals ?? true, rules: { glossary, brandVoice: null, complete: true } });
74
+ const targets = {};
75
+ for (const file of result.translation.locales) {
76
+ const flat = (0, keygen_1.flattenLocaleData)(file.data);
77
+ targets[file.locale] = Object.fromEntries(Object.entries(flat).filter(([k]) => k !== '_meta' && !k.startsWith('_meta.')));
78
+ }
79
+ return {
80
+ targets,
81
+ metadata: result.metadata,
82
+ failures: result.translation.failures,
83
+ errors: result.translation.errors,
84
+ };
85
+ }
86
+ finally {
87
+ await (0, promises_1.rm)(dir, { recursive: true, force: true });
88
+ }
89
+ }