@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,745 @@
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.finalizeProject = finalizeProject;
37
+ exports.i18nRuntimeFor = i18nRuntimeFor;
38
+ const path = __importStar(require("path"));
39
+ const child_process_1 = require("child_process");
40
+ const fs_1 = require("fs");
41
+ const types_1 = require("@babelize/types");
42
+ const file_utils_1 = require("../../utils/file-utils");
43
+ const ast_helpers_1 = require("../../utils/ast-helpers");
44
+ const static_runtime_1 = require("./static-runtime");
45
+ const django_finalize_1 = require("./django-finalize");
46
+ const nextjs_finalize_1 = require("./nextjs-finalize");
47
+ const writes_1 = require("./writes");
48
+ const i18next_icu_1 = require("../../utils/i18next-icu");
49
+ const DEPENDENCIES_BY_FRAMEWORK = {
50
+ react: {
51
+ 'i18next': '^24.0.0',
52
+ 'react-i18next': '^14.0.0',
53
+ 'i18next-http-backend': '^2.0.2',
54
+ 'i18next-browser-languagedetector': '^8.0.0',
55
+ // Messages are ICU ("Welcome back, {user}", plurals): i18next-icu formats them.
56
+ 'i18next-icu': '^2.5.0',
57
+ 'intl-messageformat': '^10.7.0',
58
+ },
59
+ // Next.js projects that already use react-i18next keep it (injection picks
60
+ // ReactI18nextStrategy for them). Everything else gets next-intl — see
61
+ // NEXT_INTL_DEPENDENCIES and finalizeNextIntl().
62
+ nextjs: {
63
+ 'i18next': '^24.0.0',
64
+ 'react-i18next': '^14.0.0',
65
+ 'i18next-http-backend': '^2.0.2',
66
+ 'i18next-browser-languagedetector': '^8.0.0',
67
+ // Messages are ICU ("Welcome back, {user}", plurals): i18next-icu formats them.
68
+ 'i18next-icu': '^2.5.0',
69
+ 'intl-messageformat': '^10.7.0',
70
+ },
71
+ vue: {
72
+ 'vue-i18n': '^10.0.0',
73
+ },
74
+ static: {},
75
+ django: {},
76
+ unknown: {
77
+ 'i18next': '^24.0.0',
78
+ 'react-i18next': '^14.0.0',
79
+ 'i18next-http-backend': '^2.0.2',
80
+ 'i18next-browser-languagedetector': '^8.0.0',
81
+ // Messages are ICU ("Welcome back, {user}", plurals): i18next-icu formats them.
82
+ 'i18next-icu': '^2.5.0',
83
+ 'intl-messageformat': '^10.7.0',
84
+ },
85
+ };
86
+ const I18N_CONFIG_TEMPLATES = {
87
+ react: `${i18next_icu_1.GENERATED_I18N_CONFIG_MARKER}
88
+ "use client";
89
+ import i18n from 'i18next';
90
+ import { initReactI18next } from 'react-i18next';
91
+
92
+ import Backend from 'i18next-http-backend';
93
+ import LanguageDetector from 'i18next-browser-languagedetector';
94
+ import ICU from 'i18next-icu';
95
+
96
+ i18n
97
+ .use(Backend)
98
+ .use(LanguageDetector)
99
+ .use(ICU)
100
+ .use(initReactI18next)
101
+ .init({
102
+ fallbackLng: 'en',
103
+ defaultNS: 'translation',
104
+ debug: process.env.NODE_ENV !== 'production',
105
+ interpolation: {
106
+ escapeValue: false,
107
+ },
108
+ react: {
109
+ useSuspense: false,
110
+ bindI18n: 'languageChanged loaded',
111
+ bindI18nStore: 'added removed',
112
+ },
113
+ backend: {
114
+ loadPath: '/locales/{{lng}}.json',
115
+ },
116
+ });
117
+
118
+ export const t = i18n.t.bind(i18n);
119
+ export default i18n;
120
+ `,
121
+ nextjs: `${i18next_icu_1.GENERATED_I18N_CONFIG_MARKER}
122
+ "use client";
123
+ import i18n from 'i18next';
124
+ import { initReactI18next } from 'react-i18next';
125
+
126
+ import Backend from 'i18next-http-backend';
127
+ import LanguageDetector from 'i18next-browser-languagedetector';
128
+ import ICU from 'i18next-icu';
129
+
130
+ i18n
131
+ .use(Backend)
132
+ .use(LanguageDetector)
133
+ .use(ICU)
134
+ .use(initReactI18next)
135
+ .init({
136
+ fallbackLng: 'en',
137
+ defaultNS: 'translation',
138
+ debug: process.env.NODE_ENV !== 'production',
139
+ interpolation: {
140
+ escapeValue: false,
141
+ },
142
+ react: {
143
+ useSuspense: false,
144
+ bindI18n: 'languageChanged loaded',
145
+ bindI18nStore: 'added removed',
146
+ },
147
+ backend: {
148
+ loadPath: '/locales/{{lng}}.json',
149
+ },
150
+ });
151
+
152
+ export const t = i18n.t.bind(i18n);
153
+ export default i18n;
154
+ `,
155
+ vue: `/* babelize:i18n-config v2 */
156
+ import { createI18n } from 'vue-i18n';
157
+
158
+ const i18n = createI18n({
159
+ legacy: false,
160
+ globalInjection: true,
161
+ locale: 'en',
162
+ fallbackLocale: 'en',
163
+ messages: {},
164
+ });
165
+
166
+ export async function loadLocale(locale) {
167
+ try {
168
+ const response = await fetch(\`/locales/\${locale}.json\`);
169
+ if (!response.ok) return;
170
+ const data = await response.json();
171
+ // Strip _meta key if present
172
+ const { _meta, ...messages } = data;
173
+ i18n.global.setLocaleMessage(locale, messages);
174
+ } catch (e) {
175
+ console.warn(\`[babelize] Failed to load locale \${locale}\`, e);
176
+ }
177
+ }
178
+
179
+ // Pre-load default locale
180
+ await loadLocale('en');
181
+
182
+ export default i18n;
183
+ `,
184
+ unknown: `${i18next_icu_1.GENERATED_I18N_CONFIG_MARKER}
185
+ "use client";
186
+ import i18n from 'i18next';
187
+ import { initReactI18next } from 'react-i18next';
188
+
189
+ import Backend from 'i18next-http-backend';
190
+ import LanguageDetector from 'i18next-browser-languagedetector';
191
+ import ICU from 'i18next-icu';
192
+
193
+ i18n
194
+ .use(Backend)
195
+ .use(LanguageDetector)
196
+ .use(ICU)
197
+ .use(initReactI18next)
198
+ .init({
199
+ fallbackLng: 'en',
200
+ defaultNS: 'translation',
201
+ debug: process.env.NODE_ENV !== 'production',
202
+ interpolation: {
203
+ escapeValue: false,
204
+ },
205
+ react: {
206
+ useSuspense: false,
207
+ bindI18n: 'languageChanged loaded',
208
+ bindI18nStore: 'added removed',
209
+ },
210
+ backend: {
211
+ loadPath: '/locales/{{lng}}.json',
212
+ },
213
+ });
214
+
215
+ export const t = i18n.t.bind(i18n);
216
+ export default i18n;
217
+ `,
218
+ };
219
+ const I18N_PROVIDER_TEMPLATE = `"use client";
220
+
221
+ import { I18nextProvider } from 'react-i18next';
222
+ import i18n from './i18n';
223
+
224
+ export default function I18nProvider({
225
+ children,
226
+ }: {
227
+ children: React.ReactNode;
228
+ }) {
229
+ return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
230
+ }
231
+ `;
232
+ async function finalizeProject(workspace, dryRun, options = {}) {
233
+ const warnings = [];
234
+ let dependenciesInvalid = false;
235
+ let configFileCreated = false;
236
+ let entryPointModified = false;
237
+ let packagesInstalled = false;
238
+ let userOwnsInit = false;
239
+ /**
240
+ * Every file finalization writes goes through this recorder, so hosts can
241
+ * ship all of them with the run (the PR, the ZIP, the CLI diff) — including
242
+ * files it creates.
243
+ */
244
+ const writer = new writes_1.FinalizationWriter(workspace.path, dryRun);
245
+ // Coerced against FRAMEWORKS, not SUPPORTED_FRAMEWORKS. SUPPORTED_FRAMEWORKS
246
+ // omits 'django', so a Django project used to collapse to 'unknown' here —
247
+ // which made the two `framework === 'django'` branches below unreachable and
248
+ // put this function on the path to writing i18next dependencies into a Django
249
+ // project that happened to carry a package.json.
250
+ const framework = types_1.FRAMEWORKS.includes(workspace.framework)
251
+ ? workspace.framework
252
+ : 'unknown';
253
+ let requiredDeps = DEPENDENCIES_BY_FRAMEWORK[framework];
254
+ // Mirrors StrategyFactory: a Next.js project that already has react-i18next
255
+ // is injected with react-i18next; every other Next.js project gets next-intl.
256
+ let useNextIntl = framework === 'nextjs';
257
+ if (!['static', 'django'].includes(framework)) {
258
+ const packageJsonPath = path.join(workspace.path, 'package.json');
259
+ if (await (0, file_utils_1.pathExists)(packageJsonPath)) {
260
+ const pkg = await (0, file_utils_1.readJsonFile)(packageJsonPath);
261
+ if (pkg) {
262
+ const deps = pkg.dependencies || {};
263
+ if (useNextIntl && { ...deps, ...(pkg.devDependencies || {}) }['react-i18next'])
264
+ useNextIntl = false;
265
+ if (useNextIntl)
266
+ requiredDeps = nextjs_finalize_1.NEXT_INTL_DEPENDENCIES;
267
+ const missingDeps = Object.keys(requiredDeps).filter((d) => !deps[d]);
268
+ if (missingDeps.length > 0) {
269
+ pkg.dependencies = pkg.dependencies || {};
270
+ for (const dep of missingDeps) {
271
+ pkg.dependencies[dep] = requiredDeps[dep];
272
+ }
273
+ // Keep the user's key order (a sorted package.json is an unreviewable
274
+ // diff); only the dependency map is sorted, as npm does.
275
+ pkg.dependencies = Object.fromEntries(Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b)));
276
+ await writer.write(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`);
277
+ if (!dryRun) {
278
+ // A committed lockfile must move with package.json, or `npm ci` /
279
+ // `--frozen-lockfile` fails on the merged PR. Snapshot it so the
280
+ // install's update is recorded (and shipped) like any other write.
281
+ const lockfilesBefore = await readLockfiles(workspace.path);
282
+ if (options.installDependencies !== false) {
283
+ try {
284
+ const pkgManager = detectPackageManager(workspace.path);
285
+ await new Promise((resolve, reject) => {
286
+ const child = (0, child_process_1.spawn)(pkgManager, ['install'], {
287
+ cwd: workspace.path,
288
+ stdio: 'pipe',
289
+ timeout: 120000,
290
+ });
291
+ let stderr = '';
292
+ let stdout = '';
293
+ child.stdout?.on('data', (data) => { stdout += data.toString(); });
294
+ child.stderr?.on('data', (data) => { stderr += data.toString(); });
295
+ child.on('close', (code) => {
296
+ if (code === 0) {
297
+ packagesInstalled = true;
298
+ resolve();
299
+ }
300
+ else {
301
+ reject(new Error(`Exit code ${code}: ${stderr || stdout}`));
302
+ }
303
+ });
304
+ child.on('error', reject);
305
+ });
306
+ }
307
+ catch {
308
+ warnings.push(`Added dependencies to package.json but auto-install failed. Please run your package manager to install: ${missingDeps.join(', ')}`);
309
+ }
310
+ }
311
+ const lockfilesAfter = await readLockfiles(workspace.path);
312
+ for (const [name, before] of lockfilesBefore) {
313
+ const after = lockfilesAfter.get(name);
314
+ if (after !== undefined && after !== before) {
315
+ writer.record(path.join(workspace.path, name), before, after);
316
+ }
317
+ else {
318
+ warnings.push(`${name} was not updated for the new dependencies (${missingDeps.join(', ')}). ` +
319
+ `Run your package manager's install and commit ${name} before merging, or frozen-lockfile installs (npm ci) will fail.`);
320
+ }
321
+ }
322
+ }
323
+ dependenciesInvalid = true;
324
+ if (packagesInstalled) {
325
+ warnings.push(`Installed missing dependencies: ${missingDeps.join(', ')}.`);
326
+ }
327
+ else if (!dryRun) {
328
+ warnings.push(`Added missing dependencies: ${missingDeps.join(', ')}. Please run your package manager install.`);
329
+ }
330
+ else {
331
+ warnings.push(`Added missing dependencies: ${missingDeps.join(', ')}. Run again without --dry-run to install.`);
332
+ }
333
+ }
334
+ }
335
+ }
336
+ }
337
+ const srcDir = workspace.sourceDirs.find((d) => d.includes('src')) || workspace.sourceDirs[0] || '.';
338
+ if (framework === 'static') {
339
+ const i18nJsPath = path.join(workspace.path, 'i18n.js');
340
+ const existingContent = await (0, file_utils_1.pathExists)(i18nJsPath) ? await (0, file_utils_1.readFile)(i18nJsPath) : null;
341
+ const regenerationStatus = existingContent ? (0, static_runtime_1.checkRuntimeNeedsRegeneration)(existingContent) : 'missing';
342
+ if (regenerationStatus === 'user') {
343
+ warnings.push('WARNING: i18n.js exists but is not generated by Babelize. Leaving it untouched. The static runtime will not be updated.');
344
+ }
345
+ else {
346
+ // 'missing' or 'ours' — write/overwrite
347
+ const runtimeContent = (0, static_runtime_1.buildStaticRuntime)({
348
+ sourceLocale: workspace.sourceDirs[0] || 'en',
349
+ targetLocales: workspace.existingLocales || [],
350
+ });
351
+ await writer.write(i18nJsPath, runtimeContent);
352
+ configFileCreated = true;
353
+ if (regenerationStatus === 'ours') {
354
+ warnings.push('Updated i18n.js (new version available).');
355
+ }
356
+ else {
357
+ warnings.push('Created i18n.js for static HTML site. No npm packages required.');
358
+ }
359
+ }
360
+ }
361
+ else if (framework === 'django') {
362
+ // No JS runtime — the .po files are the locale output. What Django does need
363
+ // is settings.py opting in, otherwise the .po files are simply never loaded
364
+ // and the site stays in the source language.
365
+ const django = await (0, django_finalize_1.finalizeDjangoSettings)(workspace.path, {
366
+ targetLocales: options.targetLocales,
367
+ sourceLocale: options.sourceLocale,
368
+ dryRun,
369
+ djangoRoot: workspace.djangoRoot,
370
+ localeDir: workspace.publicDir,
371
+ });
372
+ warnings.push(...django.warnings);
373
+ if (django.changes.length > 0) {
374
+ configFileCreated = true;
375
+ warnings.push(`Configured ${path.basename(django.settingsPath ?? 'settings.py')}: ${django.changes.join('; ')}.`);
376
+ if (django.settingsPath && django.original !== undefined && django.updated !== undefined) {
377
+ // finalizeDjangoSettings writes settings.py itself (it needs the parse); record it here.
378
+ writer.record(django.settingsPath, django.original, django.updated);
379
+ }
380
+ }
381
+ }
382
+ else if (useNextIntl) {
383
+ const nextIntl = await (0, nextjs_finalize_1.finalizeNextIntl)(workspace, dryRun, {
384
+ sourceLocale: options.sourceLocale,
385
+ targetLocales: options.targetLocales,
386
+ }, writer);
387
+ warnings.push(...nextIntl.warnings);
388
+ configFileCreated = configFileCreated || nextIntl.configFileCreated;
389
+ entryPointModified = entryPointModified || nextIntl.entryPointModified;
390
+ }
391
+ else {
392
+ const i18nPath = path.join(workspace.path, srcDir, 'i18n.ts');
393
+ const i18nJsPath = path.join(workspace.path, srcDir, 'i18n.js');
394
+ // JSX sentences are ICU messages ("Hello {name}", plurals): make sure
395
+ // i18next-icu formats them. Babelize's own configs are updated; a config
396
+ // the user owns is left alone (extraction kept fragments for it).
397
+ const icu = await (0, i18next_icu_1.detectI18nextIcu)(workspace);
398
+ for (const rel of icu.generatedWithoutIcu) {
399
+ const abs = path.join(workspace.path, rel);
400
+ const content = await (0, file_utils_1.readFile)(abs);
401
+ const updated = (0, i18next_icu_1.addIcuToGeneratedConfig)(content);
402
+ if (updated !== content) {
403
+ await writer.write(abs, updated);
404
+ warnings.push(`Added i18next-icu to ${rel} (needed for messages with placeholders and plurals).`);
405
+ }
406
+ }
407
+ const note = (0, i18next_icu_1.missingIcuNote)(icu);
408
+ if (note)
409
+ warnings.push(note);
410
+ // The project initializes i18next itself (its own config elsewhere, or
411
+ // next-i18next): a second, Babelize config would be a second i18next.
412
+ userOwnsInit = icu.userOwnsInit;
413
+ if (!userOwnsInit && !(await (0, file_utils_1.pathExists)(i18nPath)) && !(await (0, file_utils_1.pathExists)(i18nJsPath))) {
414
+ const isTS = workspace.sourceDirs.some((d) => d.endsWith('ts') || d.endsWith('tsx')) ||
415
+ (await (0, file_utils_1.pathExists)(path.join(workspace.path, 'tsconfig.json')));
416
+ const targetPath = isTS ? i18nPath : i18nJsPath;
417
+ const template = I18N_CONFIG_TEMPLATES[framework] || I18N_CONFIG_TEMPLATES['unknown'];
418
+ await writer.write(targetPath, template);
419
+ configFileCreated = true;
420
+ }
421
+ if (framework === 'nextjs' && !userOwnsInit) {
422
+ const providerPath = path.join(workspace.path, srcDir, 'I18nProvider.tsx');
423
+ if (!(await (0, file_utils_1.pathExists)(providerPath))) {
424
+ await writer.write(providerPath, I18N_PROVIDER_TEMPLATE);
425
+ configFileCreated = true;
426
+ }
427
+ }
428
+ }
429
+ const entryPoints = [
430
+ 'src/main.tsx',
431
+ 'src/main.jsx',
432
+ 'src/main.ts',
433
+ 'src/main.js',
434
+ 'src/index.tsx',
435
+ 'src/index.jsx',
436
+ 'src/index.ts',
437
+ 'src/index.js',
438
+ 'src/App.tsx',
439
+ 'src/App.jsx',
440
+ 'src/App.ts',
441
+ 'src/App.js',
442
+ 'src/pages/_app.tsx',
443
+ 'src/pages/_app.jsx',
444
+ 'pages/_app.tsx',
445
+ 'pages/_app.jsx',
446
+ 'app/layout.tsx',
447
+ 'app/layout.jsx',
448
+ 'src/app/layout.tsx',
449
+ 'src/app/layout.jsx',
450
+ ];
451
+ if (framework === 'vue') {
452
+ for (const entry of entryPoints) {
453
+ const entryPath = path.join(workspace.path, entry);
454
+ if (await (0, file_utils_1.pathExists)(entryPath)) {
455
+ const content = await (0, file_utils_1.readFile)(entryPath);
456
+ const entryDir = path.dirname(entryPath);
457
+ const i18nTargetAbs = path.join(workspace.path, srcDir, 'i18n');
458
+ let relativeI18nPath = path.relative(entryDir, i18nTargetAbs).split(path.sep).join('/');
459
+ if (!relativeI18nPath.startsWith('.')) {
460
+ relativeI18nPath = './' + relativeI18nPath;
461
+ }
462
+ const modified = modifyVueEntryPoint(content, relativeI18nPath);
463
+ if (modified !== null) {
464
+ await writer.write(entryPath, modified);
465
+ entryPointModified = true;
466
+ }
467
+ break;
468
+ }
469
+ }
470
+ }
471
+ else if (!['static', 'django'].includes(framework) && !useNextIntl && !userOwnsInit) {
472
+ for (const entry of entryPoints) {
473
+ const entryPath = path.join(workspace.path, entry);
474
+ if (await (0, file_utils_1.pathExists)(entryPath)) {
475
+ const content = await (0, file_utils_1.readFile)(entryPath);
476
+ const entryDir = path.dirname(entryPath);
477
+ const i18nTargetAbs = path.join(workspace.path, srcDir, 'i18n');
478
+ let relativeI18nPath = path.relative(entryDir, i18nTargetAbs).split(path.sep).join('/');
479
+ if (!relativeI18nPath.startsWith('.')) {
480
+ relativeI18nPath = './' + relativeI18nPath;
481
+ }
482
+ const isNextLayout = framework === 'nextjs' &&
483
+ (entry.endsWith('app/layout.tsx') || entry.endsWith('app/layout.jsx'));
484
+ if (isNextLayout) {
485
+ const modified = modifyNextjsLayout(content, relativeI18nPath);
486
+ if (modified !== null) {
487
+ await writer.write(entryPath, modified);
488
+ entryPointModified = true;
489
+ }
490
+ break;
491
+ }
492
+ const hasImport = content.includes(`import '${relativeI18nPath}'`) ||
493
+ content.includes(`import "${relativeI18nPath}"`) ||
494
+ content.includes(`import '${relativeI18nPath}.js'`) ||
495
+ content.includes(`import "${relativeI18nPath}.js"`) ||
496
+ content.includes(`import '${relativeI18nPath}.ts'`) ||
497
+ content.includes(`import "${relativeI18nPath}.ts"`) ||
498
+ content.includes("import '@/i18n'");
499
+ if (!hasImport) {
500
+ await writer.write(entryPath, `import '${relativeI18nPath}';\n${content}`);
501
+ entryPointModified = true;
502
+ }
503
+ break;
504
+ }
505
+ }
506
+ }
507
+ const changedFiles = writer.changes;
508
+ return { finalization: { dependenciesInvalid, configFileCreated, entryPointModified, packagesInstalled, warnings, changedFiles } };
509
+ }
510
+ /**
511
+ * Wire Next.js App Router layouts through a client I18nProvider so i18n
512
+ * initializes in the browser and translation loads trigger re-renders.
513
+ */
514
+ function modifyNextjsLayout(content, relativeI18nPath) {
515
+ const providerImportPath = relativeI18nPath.replace(/i18n(\.(ts|js))?$/, 'I18nProvider');
516
+ const isTS = content.includes(': Readonly') || content.includes(': React.ReactNode');
517
+ try {
518
+ const ast = (0, ast_helpers_1.parseCode)(content, { isTypeScript: isTS });
519
+ let modified = false;
520
+ let wrapped = false;
521
+ const sideEffectImports = [
522
+ relativeI18nPath,
523
+ `${relativeI18nPath}.js`,
524
+ `${relativeI18nPath}.ts`,
525
+ './i18n',
526
+ './i18n.js',
527
+ './i18n.ts',
528
+ ];
529
+ for (let i = ast.program.body.length - 1; i >= 0; i--) {
530
+ const node = ast.program.body[i];
531
+ if (ast_helpers_1.t.isImportDeclaration(node) &&
532
+ sideEffectImports.includes(node.source.value) &&
533
+ node.specifiers.length === 0) {
534
+ ast.program.body.splice(i, 1);
535
+ modified = true;
536
+ }
537
+ }
538
+ const hasProviderImport = ast.program.body.some((node) => ast_helpers_1.t.isImportDeclaration(node) &&
539
+ (node.source.value === providerImportPath ||
540
+ node.source.value === './I18nProvider' ||
541
+ node.source.value === './I18nProvider.tsx'));
542
+ if (!hasProviderImport) {
543
+ ast.program.body.unshift(ast_helpers_1.t.importDeclaration([ast_helpers_1.t.importDefaultSpecifier(ast_helpers_1.t.identifier('I18nProvider'))], ast_helpers_1.t.stringLiteral(providerImportPath)));
544
+ modified = true;
545
+ }
546
+ (0, ast_helpers_1.traverse)(ast, {
547
+ JSXElement(path) {
548
+ const opening = path.node.openingElement;
549
+ if (ast_helpers_1.t.isJSXIdentifier(opening.name) &&
550
+ opening.name.name === 'body' &&
551
+ path.node.children.length === 1 &&
552
+ ast_helpers_1.t.isJSXExpressionContainer(path.node.children[0]) &&
553
+ ast_helpers_1.t.isIdentifier(path.node.children[0].expression) &&
554
+ path.node.children[0].expression.name === 'children') {
555
+ path.node.children = [
556
+ ast_helpers_1.t.jsxText('\n '),
557
+ ast_helpers_1.t.jsxElement(ast_helpers_1.t.jsxOpeningElement(ast_helpers_1.t.jsxIdentifier('I18nProvider'), [], false), ast_helpers_1.t.jsxClosingElement(ast_helpers_1.t.jsxIdentifier('I18nProvider')), [ast_helpers_1.t.jsxExpressionContainer(ast_helpers_1.t.identifier('children'))], false),
558
+ ast_helpers_1.t.jsxText('\n '),
559
+ ];
560
+ wrapped = true;
561
+ modified = true;
562
+ path.stop();
563
+ }
564
+ },
565
+ });
566
+ if (!modified)
567
+ return null;
568
+ if (wrapped)
569
+ return (0, ast_helpers_1.generateCode)(ast);
570
+ }
571
+ catch {
572
+ /* fall through to fallback */
573
+ }
574
+ return modifyNextjsLayoutFallback(content, providerImportPath);
575
+ }
576
+ function modifyNextjsLayoutFallback(content, providerImportPath) {
577
+ let updated = content
578
+ .replace(/import\s+['"](?:.*\/)?i18n(?:\.(?:ts|js))?['"];\s*\n?/g, '')
579
+ .replace(/<body([^>]*)>\s*\{children\}\s*<\/body>/, '<body$1>\n <I18nProvider>{children}</I18nProvider>\n </body>');
580
+ if (!updated.includes('I18nProvider')) {
581
+ updated = `import I18nProvider from '${providerImportPath}';\n${updated}`;
582
+ }
583
+ else if (!updated.includes(`from '${providerImportPath}'`) && !updated.includes('from "./I18nProvider"')) {
584
+ updated = `import I18nProvider from '${providerImportPath}';\n${updated}`;
585
+ }
586
+ if (updated === content)
587
+ return null;
588
+ return updated;
589
+ }
590
+ /**
591
+ * Modify a Vue entry point (main.ts) to import and use vue-i18n.
592
+ * Adds `import i18n from './i18n'` and inserts `.use(i18n)` in the
593
+ * `createApp(...)` chain. Returns the modified content, or null if
594
+ * no changes were needed.
595
+ */
596
+ function modifyVueEntryPoint(content, relativeI18nPath) {
597
+ const isTS = /\.tsx?$/.test(relativeI18nPath) || /\bfrom\s+['"]vue['"]/.test(content) || /:\s*(?:string|number|boolean|Ref|ComputedRef)/.test(content);
598
+ try {
599
+ const ast = (0, ast_helpers_1.parseCode)(content, { isTypeScript: isTS });
600
+ let hasExistingImport = false;
601
+ let modified = false;
602
+ for (const node of ast.program.body) {
603
+ if (ast_helpers_1.t.isImportDeclaration(node) &&
604
+ (node.source.value === relativeI18nPath ||
605
+ node.source.value === relativeI18nPath + '.js' ||
606
+ node.source.value === relativeI18nPath + '.ts' ||
607
+ node.source.value === '@/i18n' ||
608
+ node.source.value === './i18n')) {
609
+ hasExistingImport = true;
610
+ break;
611
+ }
612
+ }
613
+ if (!hasExistingImport) {
614
+ const importDecl = ast_helpers_1.t.importDeclaration([ast_helpers_1.t.importDefaultSpecifier(ast_helpers_1.t.identifier('i18n'))], ast_helpers_1.t.stringLiteral(relativeI18nPath));
615
+ ast.program.body.unshift(importDecl);
616
+ modified = true;
617
+ }
618
+ (0, ast_helpers_1.traverse)(ast, {
619
+ CallExpression(path) {
620
+ if (ast_helpers_1.t.isIdentifier(path.node.callee) &&
621
+ path.node.callee.name === 'createApp') {
622
+ let topPath = path;
623
+ while (topPath.parentPath &&
624
+ topPath.parentPath.isMemberExpression() &&
625
+ topPath.parentPath.parentPath &&
626
+ topPath.parentPath.parentPath.isCallExpression()) {
627
+ topPath = topPath.parentPath.parentPath;
628
+ }
629
+ const topCall = topPath.node;
630
+ let current = topCall.callee;
631
+ while (current) {
632
+ if (ast_helpers_1.t.isMemberExpression(current)) {
633
+ if (ast_helpers_1.t.isIdentifier(current.property) &&
634
+ current.property.name === 'use') {
635
+ return;
636
+ }
637
+ current = current.object;
638
+ }
639
+ else if (ast_helpers_1.t.isCallExpression(current)) {
640
+ current = current.callee;
641
+ }
642
+ else {
643
+ break;
644
+ }
645
+ }
646
+ if (ast_helpers_1.t.isMemberExpression(topCall.callee)) {
647
+ const callee = topCall.callee;
648
+ const useCall = ast_helpers_1.t.callExpression(ast_helpers_1.t.memberExpression(callee.object, ast_helpers_1.t.identifier('use')), [ast_helpers_1.t.identifier('i18n')]);
649
+ topCall.callee = ast_helpers_1.t.memberExpression(useCall, callee.property);
650
+ modified = true;
651
+ }
652
+ }
653
+ },
654
+ });
655
+ if (!modified) {
656
+ (0, ast_helpers_1.traverse)(ast, {
657
+ VariableDeclarator(path) {
658
+ if (ast_helpers_1.t.isIdentifier(path.node.id) &&
659
+ path.node.init &&
660
+ ast_helpers_1.t.isCallExpression(path.node.init) &&
661
+ ast_helpers_1.t.isIdentifier(path.node.init.callee) &&
662
+ path.node.init.callee.name === 'createApp') {
663
+ const varName = path.node.id.name;
664
+ let hasUse = false;
665
+ const parentContainer = path.parentPath?.parentPath?.node;
666
+ const parentBody = parentContainer?.body || ast.program.body;
667
+ const declStmt = path.parentPath?.node;
668
+ const stmtIndex = parentBody.indexOf(declStmt);
669
+ if (stmtIndex !== -1) {
670
+ for (let i = stmtIndex + 1; i < parentBody.length; i++) {
671
+ const node = parentBody[i];
672
+ if (ast_helpers_1.t.isExpressionStatement(node) &&
673
+ ast_helpers_1.t.isCallExpression(node.expression) &&
674
+ ast_helpers_1.t.isMemberExpression(node.expression.callee) &&
675
+ ast_helpers_1.t.isIdentifier(node.expression.callee.object) &&
676
+ node.expression.callee.object.name === varName &&
677
+ ast_helpers_1.t.isIdentifier(node.expression.callee.property) &&
678
+ node.expression.callee.property.name === 'use') {
679
+ hasUse = true;
680
+ break;
681
+ }
682
+ }
683
+ }
684
+ if (!hasUse) {
685
+ const useStmt = ast_helpers_1.t.expressionStatement(ast_helpers_1.t.callExpression(ast_helpers_1.t.memberExpression(ast_helpers_1.t.identifier(varName), ast_helpers_1.t.identifier('use')), [ast_helpers_1.t.identifier('i18n')]));
686
+ path.parentPath?.insertAfter(useStmt);
687
+ modified = true;
688
+ path.stop();
689
+ }
690
+ }
691
+ },
692
+ });
693
+ }
694
+ if (!modified)
695
+ return null;
696
+ return (0, ast_helpers_1.generateCode)(ast, content);
697
+ }
698
+ catch {
699
+ const hasImport = content.includes(`import i18n from '${relativeI18nPath}'`) ||
700
+ content.includes(`import i18n from "${relativeI18nPath}"`) ||
701
+ content.includes("import i18n from '@/i18n'");
702
+ if (hasImport)
703
+ return null;
704
+ return `import i18n from '${relativeI18nPath}';\n${content}`;
705
+ }
706
+ }
707
+ /** Text lockfiles a package manager rewrites on install (bun.lockb is binary and left to the user). */
708
+ const TEXT_LOCKFILES = ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lock'];
709
+ async function readLockfiles(workspacePath) {
710
+ const found = new Map();
711
+ for (const name of TEXT_LOCKFILES) {
712
+ const abs = path.join(workspacePath, name);
713
+ if (await (0, file_utils_1.pathExists)(abs))
714
+ found.set(name, await (0, file_utils_1.readFile)(abs));
715
+ }
716
+ return found;
717
+ }
718
+ function detectPackageManager(workspacePath) {
719
+ if ((0, fs_1.existsSync)(path.join(workspacePath, 'bun.lock')))
720
+ return 'bun';
721
+ if ((0, fs_1.existsSync)(path.join(workspacePath, 'bun.lockb')))
722
+ return 'bun';
723
+ if ((0, fs_1.existsSync)(path.join(workspacePath, 'pnpm-lock.yaml')))
724
+ return 'pnpm';
725
+ if ((0, fs_1.existsSync)(path.join(workspacePath, 'yarn.lock')))
726
+ return 'yarn';
727
+ return 'npm';
728
+ }
729
+ /**
730
+ * The i18n runtime the engine wires a project to, and which of its packages
731
+ * package.json doesn't list yet. Same choice as finalizeProject: Next.js gets
732
+ * next-intl unless it already uses react-i18next.
733
+ */
734
+ function i18nRuntimeFor(framework, pkg) {
735
+ const fw = types_1.FRAMEWORKS.includes(framework) ? framework : 'unknown';
736
+ if (fw === 'static')
737
+ return { library: 'Babelize static runtime (i18n.js)', required: [], missing: [] };
738
+ if (fw === 'django')
739
+ return { library: 'Django gettext', required: [], missing: [] };
740
+ const deps = { ...(pkg?.dependencies ?? {}), ...(pkg?.devDependencies ?? {}) };
741
+ const nextIntl = fw === 'nextjs' && !deps['react-i18next'];
742
+ const required = Object.keys(nextIntl ? nextjs_finalize_1.NEXT_INTL_DEPENDENCIES : DEPENDENCIES_BY_FRAMEWORK[fw]);
743
+ const library = nextIntl ? 'next-intl' : fw === 'vue' ? 'vue-i18n' : 'react-i18next';
744
+ return { library, required, missing: required.filter((d) => !(pkg?.dependencies ?? {})[d]) };
745
+ }