@lastbrain/app 2.0.18 → 2.0.21

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 (254) hide show
  1. package/dist/app-shell/not-found.js +2 -2
  2. package/dist/components/LanguageSwitcher.d.ts +7 -0
  3. package/dist/components/LanguageSwitcher.d.ts.map +1 -0
  4. package/dist/components/LanguageSwitcher.js +62 -0
  5. package/dist/config/version.d.ts.map +1 -1
  6. package/dist/config/version.js +19 -21
  7. package/dist/i18n/LanguageProvider.d.ts +4 -0
  8. package/dist/i18n/LanguageProvider.d.ts.map +1 -0
  9. package/dist/i18n/LanguageProvider.js +6 -0
  10. package/dist/i18n/TranslationsScript.d.ts +8 -0
  11. package/dist/i18n/TranslationsScript.d.ts.map +1 -0
  12. package/dist/i18n/TranslationsScript.js +10 -0
  13. package/dist/i18n/cookies.d.ts +6 -0
  14. package/dist/i18n/cookies.d.ts.map +1 -0
  15. package/dist/i18n/cookies.js +24 -0
  16. package/dist/i18n/langHrefHelper.d.ts +36 -0
  17. package/dist/i18n/langHrefHelper.d.ts.map +1 -0
  18. package/dist/i18n/langHrefHelper.js +41 -0
  19. package/dist/i18n/server-helpers.d.ts +33 -0
  20. package/dist/i18n/server-helpers.d.ts.map +1 -0
  21. package/dist/i18n/server-helpers.js +39 -0
  22. package/dist/i18n/server-lang.d.ts +10 -0
  23. package/dist/i18n/server-lang.d.ts.map +1 -0
  24. package/dist/i18n/server-lang.js +42 -0
  25. package/dist/i18n/server.d.ts +10 -0
  26. package/dist/i18n/server.d.ts.map +1 -0
  27. package/dist/i18n/server.js +8 -0
  28. package/dist/i18n/types.d.ts +38 -0
  29. package/dist/i18n/types.d.ts.map +1 -0
  30. package/dist/i18n/types.js +4 -0
  31. package/dist/i18n/useLangRouter.d.ts +12 -0
  32. package/dist/i18n/useLangRouter.d.ts.map +1 -0
  33. package/dist/i18n/useLangRouter.js +18 -0
  34. package/dist/i18n/useLink.d.ts +34 -0
  35. package/dist/i18n/useLink.d.ts.map +1 -0
  36. package/dist/i18n/useLink.js +58 -0
  37. package/dist/i18n/useModuleTranslation.d.ts +11 -0
  38. package/dist/i18n/useModuleTranslation.d.ts.map +1 -0
  39. package/dist/i18n/useModuleTranslation.js +23 -0
  40. package/dist/index.d.ts +7 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +7 -0
  43. package/dist/layouts/AdminLayout.js +1 -1
  44. package/dist/layouts/AppProviders.d.ts +4 -1
  45. package/dist/layouts/AppProviders.d.ts.map +1 -1
  46. package/dist/layouts/AppProviders.js +18 -8
  47. package/dist/layouts/AuthLayout.js +1 -1
  48. package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -1
  49. package/dist/layouts/AuthLayoutWithSidebar.js +5 -3
  50. package/dist/layouts/RootLayout.d.ts +4 -1
  51. package/dist/layouts/RootLayout.d.ts.map +1 -1
  52. package/dist/layouts/RootLayout.js +2 -2
  53. package/dist/scripts/i18n-build.d.ts +7 -0
  54. package/dist/scripts/i18n-build.d.ts.map +1 -0
  55. package/dist/scripts/i18n-build.js +100 -0
  56. package/dist/scripts/init-app.js +12 -12
  57. package/dist/scripts/module-build.d.ts.map +1 -1
  58. package/dist/scripts/module-build.js +384 -81
  59. package/dist/styles.css +1 -1
  60. package/dist/templates/DefaultDoc.d.ts.map +1 -1
  61. package/dist/templates/DefaultDoc.js +90 -2
  62. package/dist/templates/middleware-i18n.example.d.ts +16 -0
  63. package/dist/templates/middleware-i18n.example.d.ts.map +1 -0
  64. package/dist/templates/middleware-i18n.example.js +72 -0
  65. package/package.json +14 -3
  66. package/src/app-shell/not-found.tsx +2 -2
  67. package/src/components/LanguageSwitcher.tsx +156 -0
  68. package/src/config/version.ts +19 -21
  69. package/src/i18n/LanguageProvider.tsx +7 -0
  70. package/src/i18n/README_LANG_HELPERS.md +187 -0
  71. package/src/i18n/TranslationsScript.tsx +17 -0
  72. package/src/i18n/cookies.ts +24 -0
  73. package/src/i18n/langHrefHelper.ts +51 -0
  74. package/src/i18n/server-helpers.ts +48 -0
  75. package/src/i18n/server-lang.ts +51 -0
  76. package/src/i18n/server.ts +13 -0
  77. package/src/i18n/types.ts +39 -0
  78. package/src/i18n/useLangRouter.ts +21 -0
  79. package/src/i18n/useLink.ts +60 -0
  80. package/src/i18n/useModuleTranslation.ts +27 -0
  81. package/src/index.ts +20 -0
  82. package/src/layouts/AdminLayout.tsx +1 -1
  83. package/src/layouts/AppProviders.tsx +35 -16
  84. package/src/layouts/AuthLayout.tsx +1 -1
  85. package/src/layouts/AuthLayoutWithSidebar.tsx +5 -3
  86. package/src/layouts/RootLayout.tsx +12 -5
  87. package/src/scripts/i18n-build.ts +122 -0
  88. package/src/scripts/init-app.ts +12 -12
  89. package/src/scripts/module-build.ts +476 -94
  90. package/src/templates/DefaultDoc.tsx +511 -1
  91. package/src/templates/middleware-i18n.example.ts +92 -0
  92. package/dist/app-shell/(admin)/dashboard/page.d.ts +0 -2
  93. package/dist/app-shell/(admin)/dashboard/page.d.ts.map +0 -1
  94. package/dist/app-shell/(admin)/dashboard/page.js +0 -5
  95. package/dist/app-shell/(admin)/page.d.ts +0 -2
  96. package/dist/app-shell/(admin)/page.d.ts.map +0 -1
  97. package/dist/app-shell/(admin)/page.js +0 -5
  98. package/dist/app-shell/(auth)/dashboard/page.d.ts +0 -2
  99. package/dist/app-shell/(auth)/dashboard/page.d.ts.map +0 -1
  100. package/dist/app-shell/(auth)/dashboard/page.js +0 -5
  101. package/dist/app-shell/(auth)/page.d.ts +0 -2
  102. package/dist/app-shell/(auth)/page.d.ts.map +0 -1
  103. package/dist/app-shell/(auth)/page.js +0 -5
  104. package/dist/components/TableStructure.d.ts +0 -8
  105. package/dist/components/TableStructure.d.ts.map +0 -1
  106. package/dist/components/TableStructure.js +0 -37
  107. package/dist/hooks/useNotificationsSimple.d.ts +0 -20
  108. package/dist/hooks/useNotificationsSimple.d.ts.map +0 -1
  109. package/dist/hooks/useNotificationsSimple.js +0 -37
  110. package/dist/module-build.d.ts +0 -2
  111. package/dist/module-build.d.ts.map +0 -1
  112. package/dist/module-build.js +0 -50
  113. package/dist/modules/index.d.ts +0 -3
  114. package/dist/modules/index.d.ts.map +0 -1
  115. package/dist/modules/index.js +0 -2
  116. package/dist/src/__tests__/module-registry.test.d.ts +0 -2
  117. package/dist/src/__tests__/module-registry.test.d.ts.map +0 -1
  118. package/dist/src/__tests__/module-registry.test.js +0 -53
  119. package/dist/src/app-shell/(admin)/layout.d.ts +0 -4
  120. package/dist/src/app-shell/(admin)/layout.d.ts.map +0 -1
  121. package/dist/src/app-shell/(admin)/layout.js +0 -5
  122. package/dist/src/app-shell/(auth)/layout.d.ts +0 -4
  123. package/dist/src/app-shell/(auth)/layout.d.ts.map +0 -1
  124. package/dist/src/app-shell/(auth)/layout.js +0 -5
  125. package/dist/src/app-shell/(public)/page.d.ts +0 -2
  126. package/dist/src/app-shell/(public)/page.d.ts.map +0 -1
  127. package/dist/src/app-shell/(public)/page.js +0 -5
  128. package/dist/src/app-shell/layout.d.ts +0 -3
  129. package/dist/src/app-shell/layout.d.ts.map +0 -1
  130. package/dist/src/app-shell/layout.js +0 -3
  131. package/dist/src/app-shell/not-found.d.ts +0 -2
  132. package/dist/src/app-shell/not-found.d.ts.map +0 -1
  133. package/dist/src/app-shell/not-found.js +0 -10
  134. package/dist/src/auth/authHelpers.d.ts +0 -7
  135. package/dist/src/auth/authHelpers.d.ts.map +0 -1
  136. package/dist/src/auth/authHelpers.js +0 -19
  137. package/dist/src/auth/useAuthSession.d.ts +0 -7
  138. package/dist/src/auth/useAuthSession.d.ts.map +0 -1
  139. package/dist/src/auth/useAuthSession.js +0 -49
  140. package/dist/src/cli.d.ts +0 -3
  141. package/dist/src/cli.d.ts.map +0 -1
  142. package/dist/src/cli.js +0 -143
  143. package/dist/src/components/NotificationContainer.d.ts +0 -2
  144. package/dist/src/components/NotificationContainer.d.ts.map +0 -1
  145. package/dist/src/components/NotificationContainer.js +0 -8
  146. package/dist/src/hooks/useNotifications.d.ts +0 -30
  147. package/dist/src/hooks/useNotifications.d.ts.map +0 -1
  148. package/dist/src/hooks/useNotifications.js +0 -165
  149. package/dist/src/index.d.ts +0 -22
  150. package/dist/src/index.d.ts.map +0 -1
  151. package/dist/src/index.js +0 -22
  152. package/dist/src/layouts/AdminLayout.d.ts +0 -4
  153. package/dist/src/layouts/AdminLayout.d.ts.map +0 -1
  154. package/dist/src/layouts/AdminLayout.js +0 -4
  155. package/dist/src/layouts/AdminLayoutWithSidebar.d.ts +0 -10
  156. package/dist/src/layouts/AdminLayoutWithSidebar.d.ts.map +0 -1
  157. package/dist/src/layouts/AdminLayoutWithSidebar.js +0 -62
  158. package/dist/src/layouts/AppProviders.d.ts +0 -27
  159. package/dist/src/layouts/AppProviders.d.ts.map +0 -1
  160. package/dist/src/layouts/AppProviders.js +0 -48
  161. package/dist/src/layouts/AuthLayout.d.ts +0 -4
  162. package/dist/src/layouts/AuthLayout.d.ts.map +0 -1
  163. package/dist/src/layouts/AuthLayout.js +0 -4
  164. package/dist/src/layouts/AuthLayoutWithSidebar.d.ts +0 -12
  165. package/dist/src/layouts/AuthLayoutWithSidebar.d.ts.map +0 -1
  166. package/dist/src/layouts/AuthLayoutWithSidebar.js +0 -60
  167. package/dist/src/layouts/PublicLayout.d.ts +0 -8
  168. package/dist/src/layouts/PublicLayout.d.ts.map +0 -1
  169. package/dist/src/layouts/PublicLayout.js +0 -6
  170. package/dist/src/layouts/PublicLayoutWithSidebar.d.ts +0 -9
  171. package/dist/src/layouts/PublicLayoutWithSidebar.d.ts.map +0 -1
  172. package/dist/src/layouts/PublicLayoutWithSidebar.js +0 -60
  173. package/dist/src/layouts/RootLayout.d.ts +0 -6
  174. package/dist/src/layouts/RootLayout.d.ts.map +0 -1
  175. package/dist/src/layouts/RootLayout.js +0 -9
  176. package/dist/src/modules/module-loader.d.ts +0 -5
  177. package/dist/src/modules/module-loader.d.ts.map +0 -1
  178. package/dist/src/modules/module-loader.js +0 -10
  179. package/dist/src/scripts/db-init.d.ts +0 -2
  180. package/dist/src/scripts/db-init.d.ts.map +0 -1
  181. package/dist/src/scripts/db-init.js +0 -300
  182. package/dist/src/scripts/db-migrations-sync.d.ts +0 -2
  183. package/dist/src/scripts/db-migrations-sync.d.ts.map +0 -1
  184. package/dist/src/scripts/db-migrations-sync.js +0 -84
  185. package/dist/src/scripts/dev-sync.d.ts +0 -2
  186. package/dist/src/scripts/dev-sync.d.ts.map +0 -1
  187. package/dist/src/scripts/dev-sync.js +0 -194
  188. package/dist/src/scripts/init-app.d.ts +0 -12
  189. package/dist/src/scripts/init-app.d.ts.map +0 -1
  190. package/dist/src/scripts/init-app.js +0 -2175
  191. package/dist/src/scripts/module-add.d.ts +0 -2
  192. package/dist/src/scripts/module-add.d.ts.map +0 -1
  193. package/dist/src/scripts/module-add.js +0 -232
  194. package/dist/src/scripts/module-build.d.ts +0 -2
  195. package/dist/src/scripts/module-build.d.ts.map +0 -1
  196. package/dist/src/scripts/module-build.js +0 -1280
  197. package/dist/src/scripts/module-create.d.ts +0 -28
  198. package/dist/src/scripts/module-create.d.ts.map +0 -1
  199. package/dist/src/scripts/module-create.js +0 -1429
  200. package/dist/src/scripts/module-delete.d.ts +0 -6
  201. package/dist/src/scripts/module-delete.d.ts.map +0 -1
  202. package/dist/src/scripts/module-delete.js +0 -147
  203. package/dist/src/scripts/module-list.d.ts +0 -2
  204. package/dist/src/scripts/module-list.d.ts.map +0 -1
  205. package/dist/src/scripts/module-list.js +0 -61
  206. package/dist/src/scripts/module-remove.d.ts +0 -2
  207. package/dist/src/scripts/module-remove.d.ts.map +0 -1
  208. package/dist/src/scripts/module-remove.js +0 -311
  209. package/dist/src/scripts/readme-build.d.ts +0 -2
  210. package/dist/src/scripts/readme-build.d.ts.map +0 -1
  211. package/dist/src/scripts/readme-build.js +0 -39
  212. package/dist/src/scripts/script-runner.d.ts +0 -5
  213. package/dist/src/scripts/script-runner.d.ts.map +0 -1
  214. package/dist/src/scripts/script-runner.js +0 -25
  215. package/dist/src/templates/AuthGuidePage.d.ts +0 -2
  216. package/dist/src/templates/AuthGuidePage.d.ts.map +0 -1
  217. package/dist/src/templates/AuthGuidePage.js +0 -9
  218. package/dist/src/templates/DefaultDoc.d.ts +0 -2
  219. package/dist/src/templates/DefaultDoc.d.ts.map +0 -1
  220. package/dist/src/templates/DefaultDoc.js +0 -240
  221. package/dist/src/templates/DocPage.d.ts +0 -17
  222. package/dist/src/templates/DocPage.d.ts.map +0 -1
  223. package/dist/src/templates/DocPage.js +0 -193
  224. package/dist/src/templates/DocsPageWithModules.d.ts +0 -2
  225. package/dist/src/templates/DocsPageWithModules.d.ts.map +0 -1
  226. package/dist/src/templates/DocsPageWithModules.js +0 -8
  227. package/dist/src/templates/MigrationsGuidePage.d.ts +0 -2
  228. package/dist/src/templates/MigrationsGuidePage.d.ts.map +0 -1
  229. package/dist/src/templates/MigrationsGuidePage.js +0 -11
  230. package/dist/src/templates/ModuleGuidePage.d.ts +0 -2
  231. package/dist/src/templates/ModuleGuidePage.d.ts.map +0 -1
  232. package/dist/src/templates/ModuleGuidePage.js +0 -14
  233. package/dist/src/templates/SimpleDocPage.d.ts +0 -2
  234. package/dist/src/templates/SimpleDocPage.d.ts.map +0 -1
  235. package/dist/src/templates/SimpleDocPage.js +0 -28
  236. package/dist/src/templates/SimpleHomePage.d.ts +0 -6
  237. package/dist/src/templates/SimpleHomePage.d.ts.map +0 -1
  238. package/dist/src/templates/SimpleHomePage.js +0 -7
  239. package/dist/src/types/menu.d.ts +0 -23
  240. package/dist/src/types/menu.d.ts.map +0 -1
  241. package/dist/src/types/menu.js +0 -1
  242. package/dist/templates/HomePage.d.ts +0 -6
  243. package/dist/templates/HomePage.d.ts.map +0 -1
  244. package/dist/templates/HomePage.js +0 -6
  245. package/dist/templates/components/AppAside.d.ts +0 -6
  246. package/dist/templates/components/AppAside.d.ts.map +0 -1
  247. package/dist/templates/components/AppAside.js +0 -9
  248. package/dist/templates/layouts/admin-layout.d.ts +0 -4
  249. package/dist/templates/layouts/admin-layout.d.ts.map +0 -1
  250. package/dist/templates/layouts/admin-layout.js +0 -6
  251. package/dist/templates/layouts/auth-layout.d.ts +0 -4
  252. package/dist/templates/layouts/auth-layout.d.ts.map +0 -1
  253. package/dist/templates/layouts/auth-layout.js +0 -6
  254. package/dist/templates/migrations/20201010100000_init.sql +0 -123
@@ -22,6 +22,22 @@ const appDirectory = path.join(projectRoot, "app");
22
22
  // Mode debug activé via --debug
23
23
  const isDebugMode = process.argv.includes("--debug");
24
24
 
25
+ // Helper pour écrire (générer ou mettre à jour) un fichier de scaffold
26
+ function ensureDirectory(dir: string) {
27
+ fs.mkdirSync(dir, { recursive: true });
28
+ }
29
+
30
+ function writeScaffoldFile(targetPath: string, content: string, label: string) {
31
+ const existed = fs.existsSync(targetPath);
32
+ ensureDirectory(path.dirname(targetPath));
33
+ fs.writeFileSync(targetPath, content, "utf-8");
34
+
35
+ if (isDebugMode) {
36
+ const status = existed ? "♻️ Updated" : "✅ Generated";
37
+ console.log(`${status} ${label}: ${targetPath}`);
38
+ }
39
+ }
40
+
25
41
  // Créer un require dans le contexte de l'application pour résoudre les modules installés dans l'app
26
42
  const projectRequire = createRequire(path.join(projectRoot, "package.json"));
27
43
 
@@ -93,10 +109,10 @@ async function loadModuleConfigs(): Promise<ModuleBuildConfig[]> {
93
109
  }
94
110
 
95
111
  const sectionDirectoryMap: Record<ModuleSection, string[]> = {
96
- public: ["(public)"],
97
- auth: ["auth"],
98
- admin: ["admin"],
99
- user: ["auth", "user"],
112
+ public: ["[lang]", "(public)"],
113
+ auth: ["[lang]", "auth"],
114
+ admin: ["[lang]", "admin"],
115
+ user: ["[lang]", "auth", "user"],
100
116
  };
101
117
 
102
118
  const sectionLayoutMap: Record<string, string> = {
@@ -121,10 +137,6 @@ interface MenuEntry {
121
137
  section: ModuleSection;
122
138
  }
123
139
 
124
- function ensureDirectory(dir: string) {
125
- fs.mkdirSync(dir, { recursive: true });
126
- }
127
-
128
140
  function ensureSectionLayout(sectionPath: string[]) {
129
141
  const sectionDir = path.join(appDirectory, ...sectionPath);
130
142
  const sectionKey = sectionPath[0];
@@ -139,16 +151,14 @@ function ensureSectionLayout(sectionPath: string[]) {
139
151
  const layoutName = sectionLayoutMap[sectionKey];
140
152
  if (layoutName) {
141
153
  const layoutPath = path.join(sectionDir, "layout.tsx");
142
- if (!fs.existsSync(layoutPath)) {
143
- const layoutContent = `import { ${layoutName} } from "@lastbrain/app";
154
+ const layoutContent = `import { ${layoutName} } from "@lastbrain/app";
155
+ import type React from "react";
144
156
 
145
157
  export default function SectionLayout({ children }: { children: React.ReactNode }) {
146
158
  return <${layoutName}>{children}</${layoutName}>;
147
- }
148
- `;
149
- fs.writeFileSync(layoutPath, layoutContent);
150
- console.log(`📐 Generated section layout: ${layoutPath}`);
151
- }
159
+ }`;
160
+
161
+ writeScaffoldFile(layoutPath, layoutContent, `${sectionKey} layout`);
152
162
  }
153
163
 
154
164
  generatedSections.add(sectionKey);
@@ -217,7 +227,9 @@ function buildPage(moduleConfig: ModuleBuildConfig, page: ModulePageConfig) {
217
227
  const wrapperName = `${page.componentExport}${wrapperSuffix}Route`;
218
228
 
219
229
  // Vérifier si la route a des paramètres dynamiques (segments avec [])
220
- const hasDynamicParams = segments.some(
230
+ // Inclure les segments du sectionPath (ex: [lang]) ET les segments du module
231
+ const allSegments = [...sectionPath, ...segments];
232
+ const hasDynamicParams = allSegments.some(
221
233
  (seg) => seg.startsWith("[") && seg.endsWith("]")
222
234
  );
223
235
 
@@ -255,7 +267,7 @@ interface UserPageProps { params: Promise<{ id: string }> }
255
267
  async function getModuleUserTabs() {
256
268
  try {
257
269
  // Depuis /app/admin/auth/users/[id]/ vers /apps/test-01/config/user-tabs
258
- const { moduleUserTabs } = await import("../../../../../config/user-tabs");
270
+ const { moduleUserTabs } = await import("../../../../../../config/user-tabs");
259
271
  return moduleUserTabs || [];
260
272
  } catch (e) {
261
273
  console.warn("[user-detail-wrapper] erreur chargement user-tabs", e);
@@ -300,11 +312,35 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
300
312
  ? `${moduleConfig.moduleName}/${page.entryPoint}`
301
313
  : moduleConfig.moduleName;
302
314
 
315
+ // Extraire les noms des paramètres dynamiques pour le typage
316
+ // Inclure les segments du sectionPath (ex: [lang]) ET les segments du module
317
+ const allSegments = [...sectionPath, ...segments];
318
+ const dynamicParamNames = allSegments
319
+ .filter((seg) => seg.startsWith("[") && seg.endsWith("]"))
320
+ .map((seg) => seg.slice(1, -1)); // Enlève les []
321
+
322
+ // Générer le type des params si nécessaire
323
+ const paramsType =
324
+ dynamicParamNames.length > 0
325
+ ? `{ ${dynamicParamNames.map((name) => `${name}: string`).join("; ")} }`
326
+ : "";
327
+
328
+ const propsSignature =
329
+ hasDynamicParams && paramsType
330
+ ? `({\n params,\n}: {\n params: Promise<${paramsType}>;\n})`
331
+ : "()";
332
+
333
+ // Les composants utilisent useParams() en interne, on ne passe jamais params en props
334
+ // On fait juste l'unwrap pour Next.js 15 si des params existent
335
+ const awaitParams = hasDynamicParams
336
+ ? "await params; // Unwrap params for Next.js 15\n "
337
+ : "";
338
+
303
339
  content = `// GENERATED BY LASTBRAIN MODULE BUILD
304
340
  import { ${page.componentExport} } from "${importPath}";
305
341
  ${page.metadataExport ? `\nexport { ${page.metadataExport} as generateMetadata } from "${importPath}";\n` : ""}
306
- export default function ${wrapperName}${hasDynamicParams ? "(props: Record<string, unknown>)" : "()"} {
307
- return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
342
+ export default ${hasDynamicParams ? "async " : ""}function ${wrapperName}${propsSignature} {
343
+ ${awaitParams}return <${page.componentExport} />;
308
344
  }
309
345
  `;
310
346
  }
@@ -349,12 +385,16 @@ function generateMenuConfig(moduleConfigs: ModuleBuildConfig[]) {
349
385
  ensureDirectory(configDir);
350
386
 
351
387
  const menuPath = path.join(configDir, "menu.ts");
388
+ type AugmentedMenuItem = ModuleMenuItemConfig & {
389
+ moduleName?: string;
390
+ __componentRef?: string;
391
+ };
352
392
 
353
393
  // Collecter les menus de tous les modules avec leurs modules sources
354
- const publicMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
355
- const authMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
356
- const adminMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
357
- const accountMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
394
+ const publicMenus: AugmentedMenuItem[] = [];
395
+ const authMenus: AugmentedMenuItem[] = [];
396
+ const adminMenus: AugmentedMenuItem[] = [];
397
+ const accountMenus: AugmentedMenuItem[] = [];
358
398
 
359
399
  moduleConfigs.forEach((moduleConfig: ModuleBuildConfig) => {
360
400
  if (moduleConfig.menu) {
@@ -419,14 +459,14 @@ function generateMenuConfig(moduleConfigs: ModuleBuildConfig[]) {
419
459
  `const ${componentName} = dynamic(() => import("${menu.moduleName}").then(mod => ({ default: mod.${menu.componentExport} })), { ssr: false });`
420
460
  );
421
461
  // Ajouter une référence au composant
422
- (menu as any).__componentRef = componentName;
462
+ menu.__componentRef = componentName;
423
463
  }
424
464
  });
425
465
 
426
466
  // Fonction pour préparer les menus avec les composants
427
- const prepareMenusForExport = (menus: any[]) => {
467
+ const prepareMenusForExport = (menus: AugmentedMenuItem[]) => {
428
468
  return menus.map((menu) => {
429
- const { moduleName, __componentRef, ...cleanMenu } = menu;
469
+ const { moduleName: _moduleName, __componentRef, ...cleanMenu } = menu;
430
470
  if (__componentRef) {
431
471
  // Retourner une référence au composant au lieu de la sérialiser
432
472
  return `{ ...${JSON.stringify(cleanMenu)}, component: ${__componentRef} }`;
@@ -698,10 +738,13 @@ function buildGroupedApi(
698
738
 
699
739
  apis.forEach(({ moduleConfig, api }) => {
700
740
  const handler = `${moduleConfig.moduleName}/${api.entryPoint}`;
701
- if (!exportsBySource.has(handler)) {
702
- exportsBySource.set(handler, []);
741
+ const currentExports = exportsBySource.get(handler);
742
+ if (currentExports) {
743
+ currentExports.push(api.handlerExport);
744
+ return;
703
745
  }
704
- exportsBySource.get(handler)!.push(api.handlerExport);
746
+
747
+ exportsBySource.set(handler, [api.handlerExport]);
705
748
  });
706
749
 
707
750
  // Générer les exports - un export statement par source
@@ -822,7 +865,7 @@ function cleanGeneratedFiles() {
822
865
  };
823
866
 
824
867
  // Nettoyer les dossiers de sections
825
- const sectionsToClean = ["(public)", "auth", "admin", "api"];
868
+ const sectionsToClean = ["(public)", "auth", "admin", "api", "[lang]"];
826
869
  sectionsToClean.forEach((section) => {
827
870
  const sectionPath = path.join(appDirectory, section);
828
871
  if (fs.existsSync(sectionPath)) {
@@ -847,17 +890,59 @@ function cleanGeneratedFiles() {
847
890
  }
848
891
  }
849
892
 
850
- function generateAppAside() {
851
- const targetPath = path.join(appDirectory, "components", "AppAside.tsx");
893
+ /**
894
+ * Supprime les anciennes routes sans [lang] et les layouts générés
895
+ */
896
+ function cleanOldRoutesWithoutLang() {
897
+ const generatedComment = "// GENERATED BY LASTBRAIN MODULE BUILD";
852
898
 
853
- // Ne pas écraser si le fichier existe déjà
854
- if (fs.existsSync(targetPath)) {
855
- if (isDebugMode) {
856
- console.log(`⏭️ AppAside already exists, skipping: ${targetPath}`);
899
+ // Supprimer les anciens dossiers de sections sans [lang]
900
+ const oldSections = ["(public)", "auth", "admin"];
901
+
902
+ oldSections.forEach((section) => {
903
+ const sectionPath = path.join(appDirectory, section);
904
+ if (fs.existsSync(sectionPath)) {
905
+ try {
906
+ // Supprimer récursivement tout le dossier
907
+ fs.rmSync(sectionPath, { recursive: true, force: true });
908
+ if (isDebugMode) {
909
+ console.log(`🗑️ Removed old section without [lang]: ${section}`);
910
+ }
911
+ } catch (error) {
912
+ console.warn(`⚠️ Could not remove ${section}:`, error);
913
+ }
857
914
  }
858
- return;
859
- }
915
+ });
916
+
917
+ // Supprimer les layouts générés dans [lang]
918
+ const langLayoutsToRemove = [
919
+ path.join(appDirectory, "[lang]", "layout.tsx"),
920
+ path.join(appDirectory, "[lang]", "auth", "layout.tsx"),
921
+ path.join(appDirectory, "[lang]", "admin", "layout.tsx"),
922
+ ];
860
923
 
924
+ langLayoutsToRemove.forEach((layoutPath) => {
925
+ if (fs.existsSync(layoutPath)) {
926
+ try {
927
+ const content = fs.readFileSync(layoutPath, "utf-8");
928
+ // Supprimer si c'est un fichier généré
929
+ if (
930
+ content.includes(generatedComment) ||
931
+ content.includes("AppProviders")
932
+ ) {
933
+ fs.unlinkSync(layoutPath);
934
+ if (isDebugMode) {
935
+ console.log(`🗑️ Removed generated layout: ${layoutPath}`);
936
+ }
937
+ }
938
+ } catch (error) {
939
+ console.warn(`⚠️ Could not remove layout ${layoutPath}:`, error);
940
+ }
941
+ }
942
+ });
943
+ }
944
+ function generateAppAside() {
945
+ const targetPath = path.join(appDirectory, "components", "AppAside.tsx");
861
946
  const templateContent = `"use client";
862
947
 
863
948
  import { AppAside as UIAppAside } from "@lastbrain/app";
@@ -883,59 +968,253 @@ export function AppAside({ className = "", isVisible = true }: AppAsideProps) {
883
968
  }`;
884
969
 
885
970
  try {
886
- ensureDirectory(path.dirname(targetPath));
887
- fs.writeFileSync(targetPath, templateContent, "utf-8");
888
- if (isDebugMode) {
889
- console.log(`✅ Generated AppAside component: ${targetPath}`);
890
- }
971
+ writeScaffoldFile(targetPath, templateContent, "AppAside component");
891
972
  } catch (error) {
892
973
  console.error(`❌ Error generating AppAside component: ${error}`);
893
974
  }
894
975
  }
895
976
 
896
977
  function generateLayouts() {
897
- // Générer layout auth avec sidebar
898
- const authLayoutPath = path.join(appDirectory, "auth", "layout.tsx");
899
- if (!fs.existsSync(authLayoutPath)) {
900
- const authLayoutContent = `import { AuthLayoutWithSidebar } from "@lastbrain/app";
978
+ // Générer ClientLayout wrapper client
979
+ const clientLayoutPath = path.join(
980
+ appDirectory,
981
+ "components",
982
+ "ClientLayout.tsx"
983
+ );
984
+ const clientLayoutContent = `"use client";
985
+
986
+ import { HeroUIProvider } from "@heroui/system";
987
+ import { ThemeProvider } from "next-themes";
988
+ import { AppProviders } from "./AppProviders";
989
+ import { AppHeader } from "./AppHeader";
990
+ import type { ReactNode } from "react";
991
+ import { useLocalizedRouter, type Language } from "@lastbrain/core";
992
+
993
+
994
+ export function ClientLayout({
995
+ children,
996
+ lang = "fr",
997
+ translations = {},
998
+ }: {
999
+ children: ReactNode;
1000
+ lang?: Language;
1001
+ translations?: Record<string, string>;
1002
+ }) {
1003
+ const router = useLocalizedRouter();
1004
+
1005
+ return (
1006
+ <HeroUIProvider navigate={router.push}>
1007
+ <ThemeProvider
1008
+ attribute="class"
1009
+ defaultTheme="dark"
1010
+ enableSystem={false}
1011
+ storageKey="lastbrain-theme"
1012
+ >
1013
+ <AppProviders lang={lang} translations={translations}>
1014
+ <AppHeader />
1015
+ <div className="min-h-screen text-foreground bg-background">
1016
+ {children}
1017
+ </div>
1018
+ </AppProviders>
1019
+ </ThemeProvider>
1020
+ </HeroUIProvider>
1021
+ );
1022
+ }`;
1023
+
1024
+ try {
1025
+ writeScaffoldFile(
1026
+ clientLayoutPath,
1027
+ clientLayoutContent,
1028
+ "ClientLayout component"
1029
+ );
1030
+ } catch (error) {
1031
+ console.error(`❌ Error generating ClientLayout component: ${error}`);
1032
+ }
1033
+
1034
+ // Générer AppProviders wrapper client
1035
+ const appProvidersPath = path.join(
1036
+ appDirectory,
1037
+ "components",
1038
+ "AppProviders.tsx"
1039
+ );
1040
+ const appProvidersContent = `"use client";
1041
+
1042
+ import { AppProviders as BaseAppProviders } from "@lastbrain/app";
1043
+ import { realtimeConfig } from "../../config/realtime";
1044
+ import type { ReactNode } from "react";
1045
+ import type { Language } from "@lastbrain/core";
1046
+
1047
+ export function AppProviders({
1048
+ children,
1049
+ lang = "fr",
1050
+ translations = {},
1051
+ }: {
1052
+ children: ReactNode;
1053
+ lang?: Language;
1054
+ translations?: Record<string, string>;
1055
+ }) {
1056
+ return (
1057
+ <BaseAppProviders
1058
+ realtimeConfig={realtimeConfig}
1059
+ lang={lang}
1060
+ translations={translations}
1061
+ >
1062
+ {children}
1063
+ </BaseAppProviders>
1064
+ );
1065
+ }`;
1066
+
1067
+ try {
1068
+ writeScaffoldFile(
1069
+ appProvidersPath,
1070
+ appProvidersContent,
1071
+ "AppProviders component"
1072
+ );
1073
+ } catch (error) {
1074
+ console.error(`❌ Error generating AppProviders component: ${error}`);
1075
+ }
1076
+
1077
+ // Générer AppHeader component
1078
+ const appHeaderPath = path.join(appDirectory, "components", "AppHeader.tsx");
1079
+ const appHeaderContent = `"use client";
1080
+
1081
+ import { Header } from "@lastbrain/ui";
1082
+ import { LanguageSwitcher } from "@lastbrain/app";
901
1083
  import { menuConfig } from "../../config/menu";
902
1084
 
903
- export default function SectionLayout({
1085
+ export function AppHeader() {
1086
+ return (
1087
+ <Header
1088
+ menuConfig={menuConfig}
1089
+ languageSwitcher={<LanguageSwitcher variant="minimal" />}
1090
+ />
1091
+ );
1092
+ }`;
1093
+
1094
+ try {
1095
+ writeScaffoldFile(appHeaderPath, appHeaderContent, "AppHeader component");
1096
+ } catch (error) {
1097
+ console.error(`❌ Error generating AppHeader component: ${error}`);
1098
+ }
1099
+
1100
+ // Générer layout [lang] pour la gestion i18n
1101
+ const langLayoutPath = path.join(appDirectory, "[lang]", "layout.tsx");
1102
+ if (!fs.existsSync(langLayoutPath)) {
1103
+ const langLayoutContent = `import { loadTranslations } from "@lastbrain/app/i18n/server-lang";
1104
+ import { ClientLayout } from "../../components/ClientLayout";
1105
+
1106
+ export default async function LangLayout({
904
1107
  children,
1108
+ params,
905
1109
  }: {
906
1110
  children: React.ReactNode;
1111
+ params: Promise<{ lang: string }>;
907
1112
  }) {
1113
+ const { lang } = await params;
1114
+ const validLang = lang === "en" || lang === "fr" ? lang : "fr";
1115
+
1116
+ // Charger les traductions pour cette langue
1117
+ const translations = await loadTranslations(validLang);
1118
+
908
1119
  return (
909
- <AuthLayoutWithSidebar menuConfig={menuConfig}>
910
- {children}
911
- </AuthLayoutWithSidebar>
1120
+ <ClientLayout lang={validLang} translations={translations}>
1121
+ <div className="min-h-screen pt-16">
1122
+ {children}
1123
+ </div>
1124
+ </ClientLayout>
912
1125
  );
913
1126
  }`;
914
1127
 
915
1128
  try {
916
- ensureDirectory(path.dirname(authLayoutPath));
917
- fs.writeFileSync(authLayoutPath, authLayoutContent, "utf-8");
1129
+ ensureDirectory(path.dirname(langLayoutPath));
1130
+ fs.writeFileSync(langLayoutPath, langLayoutContent, "utf-8");
918
1131
  if (isDebugMode) {
919
- console.log(`✅ Generated auth layout with sidebar: ${authLayoutPath}`);
1132
+ console.log(`✅ Generated [lang] layout: ${langLayoutPath}`);
920
1133
  }
921
1134
  } catch (error) {
922
- console.error(`❌ Error generating auth layout: ${error}`);
1135
+ console.error(`❌ Error generating [lang] layout: ${error}`);
923
1136
  }
924
1137
  } else if (isDebugMode) {
925
- console.log(`⏭️ Auth layout already exists, skipping: ${authLayoutPath}`);
1138
+ console.log(`⏭️ [lang] layout already exists, skipping: ${langLayoutPath}`);
1139
+ }
1140
+
1141
+ // Générer layout auth avec sidebar
1142
+ const authLayoutPath = path.join(
1143
+ appDirectory,
1144
+ "[lang]",
1145
+ "auth",
1146
+ "layout.tsx"
1147
+ );
1148
+ const authLayoutContent = `"use client";
1149
+
1150
+ import { AuthLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
1151
+ import { menuConfig as fullMenuConfig } from "../../../config/menu";
1152
+
1153
+ export default function SectionLayout({
1154
+ children,
1155
+ }: {
1156
+ children: React.ReactNode;
1157
+ }) {
1158
+ const { lang } = useLanguage();
1159
+
1160
+ // Transformer les paths du menu avec la langue
1161
+ const menuConfig = {
1162
+ ...fullMenuConfig,
1163
+ auth: (fullMenuConfig.auth || []).map(item => ({
1164
+ ...item,
1165
+ path: item.path?.startsWith("/") && !item.path?.startsWith("/api/")
1166
+ ? langHref(item.path, lang)
1167
+ : item.path
1168
+ })),
1169
+ };
1170
+
1171
+ return (
1172
+ <AuthLayoutWithSidebar menuConfig={menuConfig}>
1173
+ {children}
1174
+ </AuthLayoutWithSidebar>
1175
+ );
1176
+ }`;
1177
+
1178
+ try {
1179
+ writeScaffoldFile(
1180
+ authLayoutPath,
1181
+ authLayoutContent,
1182
+ "auth layout with sidebar"
1183
+ );
1184
+ } catch (error) {
1185
+ console.error(`❌ Error generating auth layout: ${error}`);
926
1186
  }
927
1187
 
928
1188
  // Générer layout admin avec sidebar
929
- const adminLayoutPath = path.join(appDirectory, "admin", "layout.tsx");
930
- if (!fs.existsSync(adminLayoutPath)) {
931
- const adminLayoutContent = `import { AdminLayoutWithSidebar } from "@lastbrain/app";
932
- import { menuConfig } from "../../config/menu";
1189
+ const adminLayoutPath = path.join(
1190
+ appDirectory,
1191
+ "[lang]",
1192
+ "admin",
1193
+ "layout.tsx"
1194
+ );
1195
+ const adminLayoutContent = `"use client";
1196
+
1197
+ import { AdminLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
1198
+ import { menuConfig as fullMenuConfig } from "../../../config/menu";
933
1199
 
934
1200
  export default function AdminLayout({
935
1201
  children,
936
1202
  }: {
937
1203
  children: React.ReactNode;
938
1204
  }) {
1205
+ const { lang } = useLanguage();
1206
+
1207
+ // Transformer les paths du menu avec la langue
1208
+ const menuConfig = {
1209
+ ...fullMenuConfig,
1210
+ admin: (fullMenuConfig.admin || []).map(item => ({
1211
+ ...item,
1212
+ path: item.path?.startsWith("/") && !item.path?.startsWith("/api/")
1213
+ ? langHref(item.path, lang)
1214
+ : item.path
1215
+ })),
1216
+ };
1217
+
939
1218
  return (
940
1219
  <AdminLayoutWithSidebar menuConfig={menuConfig}>
941
1220
  {children}
@@ -943,28 +1222,23 @@ export default function AdminLayout({
943
1222
  );
944
1223
  }`;
945
1224
 
946
- try {
947
- ensureDirectory(path.dirname(adminLayoutPath));
948
- fs.writeFileSync(adminLayoutPath, adminLayoutContent, "utf-8");
949
- if (isDebugMode) {
950
- console.log(
951
- `✅ Generated admin layout with sidebar: ${adminLayoutPath}`
952
- );
953
- }
954
- } catch (error) {
955
- console.error(`❌ Error generating admin layout: ${error}`);
956
- }
957
- } else if (isDebugMode) {
958
- console.log(`⏭️ Admin layout already exists, skipping: ${adminLayoutPath}`);
1225
+ try {
1226
+ writeScaffoldFile(
1227
+ adminLayoutPath,
1228
+ adminLayoutContent,
1229
+ "admin layout with sidebar"
1230
+ );
1231
+ } catch (error) {
1232
+ console.error(`❌ Error generating admin layout: ${error}`);
959
1233
  }
960
1234
  }
961
1235
 
962
1236
  async function generateRealtimeConfig(moduleConfigs: ModuleBuildConfig[]) {
963
1237
  try {
964
1238
  // Extraire les configurations realtime des modules
965
- const realtimeConfigs: ModuleRealtimeConfig[] = moduleConfigs
966
- .filter((config) => config.realtime)
967
- .map((config) => config.realtime!);
1239
+ const realtimeConfigs: ModuleRealtimeConfig[] = moduleConfigs.flatMap(
1240
+ (config) => (config.realtime ? [config.realtime] : [])
1241
+ );
968
1242
 
969
1243
  if (realtimeConfigs.length === 0) {
970
1244
  console.log("⏭️ No realtime configuration found in modules");
@@ -1025,9 +1299,8 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
1025
1299
  try {
1026
1300
  // Extraire les configurations user tabs des modules
1027
1301
  const userTabsConfigs = moduleConfigs
1028
- .filter((config) => config.userTabs && config.userTabs.length > 0)
1029
1302
  .flatMap((config) =>
1030
- config.userTabs!.map((tab) => ({
1303
+ (config.userTabs ?? []).map((tab) => ({
1031
1304
  ...tab,
1032
1305
  moduleName: config.moduleName,
1033
1306
  }))
@@ -1100,11 +1373,9 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
1100
1373
  async function generateBucketsConfig(moduleConfigs: ModuleBuildConfig[]) {
1101
1374
  try {
1102
1375
  // Extraire les configurations storage des modules
1103
- const allBuckets = moduleConfigs
1104
- .filter(
1105
- (config) => config.storage?.buckets && config.storage.buckets.length > 0
1106
- )
1107
- .flatMap((config) => config.storage!.buckets);
1376
+ const allBuckets = moduleConfigs.flatMap(
1377
+ (config) => config.storage?.buckets ?? []
1378
+ );
1108
1379
 
1109
1380
  if (allBuckets.length === 0) {
1110
1381
  console.log("⏭️ No storage buckets configuration found in modules");
@@ -1236,11 +1507,9 @@ export function isValidFileSize(bucketName: string, fileSize: number): boolean {
1236
1507
  async function generateStorageProxyApi(moduleConfigs: ModuleBuildConfig[]) {
1237
1508
  try {
1238
1509
  // Extraire les configurations storage des modules
1239
- const allBuckets = moduleConfigs
1240
- .filter(
1241
- (config) => config.storage?.buckets && config.storage.buckets.length > 0
1242
- )
1243
- .flatMap((config) => config.storage!.buckets);
1510
+ const allBuckets = moduleConfigs.flatMap(
1511
+ (config) => config.storage?.buckets ?? []
1512
+ );
1244
1513
 
1245
1514
  if (allBuckets.length === 0) {
1246
1515
  console.log("⏭️ No storage buckets found, skipping proxy API generation");
@@ -1393,9 +1662,9 @@ export async function GET(
1393
1662
  async function generateFooterConfig(moduleConfigs: ModuleBuildConfig[]) {
1394
1663
  try {
1395
1664
  // Extraire tous les liens footer des modules
1396
- const allFooterLinks = moduleConfigs
1397
- .filter((config) => config.footer && config.footer.length > 0)
1398
- .flatMap((config) => config.footer!);
1665
+ const allFooterLinks = moduleConfigs.flatMap(
1666
+ (config) => config.footer ?? []
1667
+ );
1399
1668
 
1400
1669
  if (allFooterLinks.length === 0) {
1401
1670
  console.log(
@@ -1444,6 +1713,104 @@ export const footerConfig: FooterConfig = {
1444
1713
  }
1445
1714
  }
1446
1715
 
1716
+ /**
1717
+ * Génère les fichiers i18n en concaténant les traductions des modules
1718
+ */
1719
+ async function generateI18nFiles() {
1720
+ const appI18nDir = path.join(projectRoot, "i18n");
1721
+ const packagesDir = path.join(_monorepoRoot, "packages");
1722
+
1723
+ interface TranslationMap {
1724
+ [key: string]: Record<string, string>;
1725
+ }
1726
+
1727
+ const translations: TranslationMap = {
1728
+ fr: {},
1729
+ en: {},
1730
+ };
1731
+
1732
+ try {
1733
+ // Importer glob dynamiquement
1734
+ const { glob } = await import("glob");
1735
+
1736
+ // Rechercher tous les fichiers i18n dans packages/**/src/i18n/*.json
1737
+ const pattern = path.join(packagesDir, "**/src/i18n/*.json");
1738
+ const files = await glob(pattern, {
1739
+ ignore: ["**/node_modules/**", "**/dist/**"],
1740
+ });
1741
+
1742
+ if (isDebugMode) {
1743
+ console.log(`\n🔍 Found ${files.length} i18n files\n`);
1744
+ }
1745
+
1746
+ for (const file of files) {
1747
+ const fileName = path.basename(file, ".json");
1748
+ const lang = fileName; // fr.json -> fr, en.json -> en
1749
+
1750
+ if (!translations[lang]) {
1751
+ translations[lang] = {};
1752
+ }
1753
+
1754
+ try {
1755
+ const content = JSON.parse(fs.readFileSync(file, "utf-8"));
1756
+ const moduleName = extractModuleNameFromPath(file);
1757
+
1758
+ if (isDebugMode) {
1759
+ console.log(` ✓ ${moduleName} (${lang})`);
1760
+ }
1761
+
1762
+ // Préfixer uniquement si la clé n'est pas déjà namespacée (avec ou sans suffixe -pro)
1763
+ const moduleNamespace = moduleName.replace(/-pro$/, "");
1764
+ for (const [key, value] of Object.entries(content)) {
1765
+ const keyRoot = key.split(".")[0];
1766
+ const isGenericNamespaced = keyRoot.startsWith("module-");
1767
+ const isAlreadyNamespaced =
1768
+ isGenericNamespaced ||
1769
+ key.startsWith(`${moduleName}.`) ||
1770
+ key.startsWith(`${moduleNamespace}.`);
1771
+
1772
+ const normalizedKey = isAlreadyNamespaced
1773
+ ? key
1774
+ : `${moduleNamespace}.${key}`;
1775
+
1776
+ translations[lang][normalizedKey] = value as string;
1777
+ }
1778
+ } catch (error) {
1779
+ console.warn(` ⚠️ Error reading ${file}:`, error);
1780
+ }
1781
+ }
1782
+
1783
+ // Créer le dossier i18n s'il n'existe pas
1784
+ ensureDirectory(appI18nDir);
1785
+
1786
+ // Écrire les fichiers de traduction
1787
+ for (const [lang, content] of Object.entries(translations)) {
1788
+ const filePath = path.join(appI18nDir, `${lang}.json`);
1789
+ fs.writeFileSync(filePath, JSON.stringify(content, null, 2), "utf-8");
1790
+ const keyCount = Object.keys(content).length;
1791
+
1792
+ if (isDebugMode) {
1793
+ console.log(`✅ Generated ${filePath} (${keyCount} keys)`);
1794
+ }
1795
+ }
1796
+ } catch (error) {
1797
+ console.error("❌ Error generating i18n files:", error);
1798
+ }
1799
+ }
1800
+
1801
+ /**
1802
+ * Extrait le nom du module depuis le chemin du fichier
1803
+ */
1804
+ function extractModuleNameFromPath(filePath: string): string {
1805
+ // packages/module-auth/src/i18n/fr.json -> module-auth
1806
+ const parts = filePath.split(path.sep);
1807
+ const packagesIndex = parts.indexOf("packages");
1808
+ if (packagesIndex !== -1 && packagesIndex < parts.length - 1) {
1809
+ return parts[packagesIndex + 1];
1810
+ }
1811
+ return "unknown";
1812
+ }
1813
+
1447
1814
  export async function runModuleBuild() {
1448
1815
  ensureDirectory(appDirectory);
1449
1816
 
@@ -1453,6 +1820,12 @@ export async function runModuleBuild() {
1453
1820
  }
1454
1821
  cleanGeneratedFiles();
1455
1822
 
1823
+ // Supprimer les anciennes routes sans [lang]
1824
+ if (isDebugMode) {
1825
+ console.log("🗑️ Removing old routes without [lang]...");
1826
+ }
1827
+ cleanOldRoutesWithoutLang();
1828
+
1456
1829
  const moduleConfigs = await loadModuleConfigs();
1457
1830
  if (isDebugMode) {
1458
1831
  console.log(`🔍 Loaded ${moduleConfigs.length} module configurations`);
@@ -1482,10 +1855,13 @@ export async function runModuleBuild() {
1482
1855
 
1483
1856
  moduleConfigs.forEach((moduleConfig) => {
1484
1857
  moduleConfig.apis.forEach((api) => {
1485
- if (!apisByPath.has(api.path)) {
1486
- apisByPath.set(api.path, []);
1858
+ const apisForPath = apisByPath.get(api.path);
1859
+ if (apisForPath) {
1860
+ apisForPath.push({ moduleConfig, api });
1861
+ return;
1487
1862
  }
1488
- apisByPath.get(api.path)!.push({ moduleConfig, api });
1863
+
1864
+ apisByPath.set(api.path, [{ moduleConfig, api }]);
1489
1865
  });
1490
1866
  });
1491
1867
 
@@ -1531,6 +1907,12 @@ export async function runModuleBuild() {
1531
1907
  }
1532
1908
  await generateFooterConfig(moduleConfigs);
1533
1909
 
1910
+ // Générer les fichiers i18n
1911
+ if (isDebugMode) {
1912
+ console.log("🌍 Building i18n files...");
1913
+ }
1914
+ await generateI18nFiles();
1915
+
1534
1916
  // Message de succès final
1535
1917
  if (!isDebugMode) {
1536
1918
  console.log("\n✅ Module build completed successfully!");