@lastbrain/app 2.0.23 → 2.0.31

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.
@@ -1,7 +1,9 @@
1
1
  interface LanguageSwitcherProps {
2
2
  variant?: "default" | "minimal";
3
3
  className?: string;
4
+ availableLanguages?: string[];
5
+ localeMap?: Record<string, string>;
4
6
  }
5
- export declare function LanguageSwitcher({ variant, className, }: LanguageSwitcherProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function LanguageSwitcher({ variant, className, availableLanguages, localeMap, }: LanguageSwitcherProps): import("react/jsx-runtime").JSX.Element;
6
8
  export {};
7
9
  //# sourceMappingURL=LanguageSwitcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LanguageSwitcher.d.ts","sourceRoot":"","sources":["../../src/components/LanguageSwitcher.tsx"],"names":[],"mappings":"AAaA,UAAU,qBAAqB;IAC7B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAmB,EACnB,SAAc,GACf,EAAE,qBAAqB,2CAsIvB"}
1
+ {"version":3,"file":"LanguageSwitcher.d.ts","sourceRoot":"","sources":["../../src/components/LanguageSwitcher.tsx"],"names":[],"mappings":"AAcA,UAAU,qBAAqB;IAC7B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAmB,EACnB,SAAc,EACd,kBAAiC,EACjC,SAAwC,GACzC,EAAE,qBAAqB,2CAkKvB"}
@@ -1,35 +1,56 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
+ import { useRouter } from "next/navigation";
4
5
  import { useLanguage } from "../i18n/LanguageProvider";
5
6
  import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Spinner, } from "@lastbrain/ui";
6
- export function LanguageSwitcher({ variant = "default", className = "", }) {
7
- const { lang, setLang } = useLanguage();
8
- const [flagUrls, setFlagUrls] = useState({
9
- fr: "",
10
- en: "",
11
- });
7
+ export function LanguageSwitcher({ variant = "default", className = "", availableLanguages = ["fr", "en"], localeMap = { fr: "fr_FR", en: "en_US" }, }) {
8
+ const { lang, setLang: setLangFromContext } = useLanguage();
9
+ const router = useRouter();
10
+ const [flagUrls, setFlagUrls] = useState({});
12
11
  const [loading, setLoading] = useState(false);
13
12
  const [isHydrated, setIsHydrated] = useState(false);
14
13
  // Marquer l'hydration comme terminée
15
14
  useEffect(() => {
16
15
  setIsHydrated(true);
17
16
  }, []);
17
+ // Fonction pour changer la langue avec redirection correcte
18
+ const handleLanguageChange = (newLang) => {
19
+ const currentPath = window.location.pathname;
20
+ // Extraire les segments du chemin
21
+ const segments = currentPath.split("/").filter(Boolean);
22
+ // Nettoyer TOUS les codes langue (2 lettres) du début du path
23
+ // Ça corrige les paths corrompus comme /en/es/recipes
24
+ while (segments.length > 0 && /^[a-z]{2}$/.test(segments[0])) {
25
+ segments.shift(); // Enlever le premier segment s'il est une langue
26
+ }
27
+ // Reconstruire le path avec la nouvelle langue
28
+ const pathWithoutLang = segments.length > 0 ? "/" + segments.join("/") : "";
29
+ const newPath = `/${newLang}${pathWithoutLang}`;
30
+ // Naviguer directement - le LanguageProvider lira la nouvelle langue depuis l'URL
31
+ window.location.href = newPath;
32
+ };
18
33
  useEffect(() => {
19
34
  let canceled = false;
20
35
  const objectUrls = [];
21
36
  async function loadFlags() {
22
37
  setLoading(true);
23
38
  try {
24
- const entries = await Promise.all(["fr", "en"].map(async (code) => {
25
- const apiCode = code === "fr" ? "fr" : "gb";
26
- const response = await fetch(`https://flagcdn.com/${apiCode}.svg`);
27
- if (!response.ok)
28
- throw new Error(`Flag fetch failed: ${code}`);
29
- const blob = await response.blob();
30
- const url = URL.createObjectURL(blob);
31
- objectUrls.push(url);
32
- return [code, url];
39
+ const entries = await Promise.all(availableLanguages.map(async (code) => {
40
+ const apiCode = code === "fr" ? "fr" : code === "en" ? "gb" : code;
41
+ try {
42
+ const response = await fetch(`https://flagcdn.com/${apiCode}.svg`);
43
+ if (!response.ok)
44
+ throw new Error(`Flag fetch failed: ${code}`);
45
+ const blob = await response.blob();
46
+ const url = URL.createObjectURL(blob);
47
+ objectUrls.push(url);
48
+ return [code, url];
49
+ }
50
+ catch {
51
+ // Fallback to direct URL if fetch fails
52
+ return [code, `https://flagcdn.com/${apiCode}.svg`];
53
+ }
33
54
  }));
34
55
  if (!canceled) {
35
56
  setFlagUrls(Object.fromEntries(entries));
@@ -37,10 +58,13 @@ export function LanguageSwitcher({ variant = "default", className = "", }) {
37
58
  }
38
59
  catch (_err) {
39
60
  if (!canceled) {
40
- setFlagUrls({
41
- fr: "https://flagcdn.com/fr.svg",
42
- en: "https://flagcdn.com/gb.svg",
61
+ // Fallback: create a map with direct URLs
62
+ const fallbackUrls = {};
63
+ availableLanguages.forEach((code) => {
64
+ const apiCode = code === "fr" ? "fr" : code === "en" ? "gb" : code;
65
+ fallbackUrls[code] = `https://flagcdn.com/${apiCode}.svg`;
43
66
  });
67
+ setFlagUrls(fallbackUrls);
44
68
  }
45
69
  }
46
70
  finally {
@@ -53,10 +77,15 @@ export function LanguageSwitcher({ variant = "default", className = "", }) {
53
77
  canceled = true;
54
78
  objectUrls.forEach((url) => URL.revokeObjectURL(url));
55
79
  };
56
- }, []);
80
+ }, [availableLanguages]);
57
81
  const renderFlag = (code) => (_jsx("span", { className: "inline-flex items-center gap-2", children: flagUrls[code] ? (_jsx("img", { src: flagUrls[code], alt: code === "fr" ? "Drapeau français" : "Flag English", className: "h-4 w-6 rounded-sm border border-slate-200 object-cover" })) : isHydrated ? (_jsx("span", { className: "inline-flex h-4 w-6 items-center justify-center rounded-sm bg-slate-200 text-[10px] font-semibold text-slate-600", children: code.toUpperCase() })) : null }));
82
+ // SSR placeholder - ne pas rendre le Dropdown avant l'hydration
83
+ // pour éviter les mismatches d'ID React Aria
84
+ if (!isHydrated) {
85
+ return (_jsx("span", { className: "inline-flex h-4 w-6 items-center justify-center rounded-sm bg-slate-200 text-[10px] font-semibold text-slate-600", children: lang.toUpperCase() }));
86
+ }
58
87
  if (variant === "minimal") {
59
- return (_jsxs(Dropdown, { size: "sm", className: "px-0 m-0", children: [_jsx(DropdownTrigger, { className: "px-0 m-0", children: renderFlag(lang) }), _jsxs(DropdownMenu, { children: [_jsxs(DropdownItem, { onPress: () => setLang("en"), startContent: renderFlag("en"), className: lang === "en" ? "bg-primary/10" : "", children: ["English ", isHydrated && lang === "en" && "✓"] }, "en"), _jsxs(DropdownItem, { onPress: () => setLang("fr"), startContent: renderFlag("fr"), className: lang === "fr" ? "bg-primary/10" : "", children: ["Fran\u00E7ais ", isHydrated && lang === "fr" && "✓"] }, "fr")] })] }));
88
+ return (_jsxs(Dropdown, { size: "sm", className: "px-0 m-0", children: [_jsx(DropdownTrigger, { className: "px-0 m-0", children: renderFlag(lang) }), _jsx(DropdownMenu, { children: availableLanguages.map((code) => (_jsxs(DropdownItem, { onPress: () => handleLanguageChange(code), startContent: renderFlag(code), className: lang === code ? "bg-primary/10" : "", children: [code === "fr" ? "Français" : code === "en" ? "English" : code, isHydrated && lang === code && " ✓"] }, code))) })] }));
60
89
  }
61
- return (_jsxs(Dropdown, { size: "sm", className: "px-0", children: [_jsx(DropdownTrigger, { className: "px-0 m-0", children: _jsx(Button, { variant: "light", size: "sm", className: "px-0 m-0", startContent: renderFlag(lang), endContent: loading ? _jsx(Spinner, { size: "sm" }) : null }) }), _jsxs(DropdownMenu, { children: [_jsxs(DropdownItem, { onPress: () => setLang("en"), className: lang === "en" ? "bg-primary/10" : "", children: [renderFlag("en"), " English ", isHydrated && lang === "en" && ""] }, "en"), _jsxs(DropdownItem, { onPress: () => setLang("fr"), className: lang === "fr" ? "bg-primary/10" : "", children: [renderFlag("fr"), " Fran\u00E7ais ", isHydrated && lang === "fr" && "✓"] }, "fr")] })] }));
90
+ return (_jsxs(Dropdown, { size: "sm", className: "px-0", children: [_jsx(DropdownTrigger, { className: "px-0 m-0", children: _jsx(Button, { variant: "light", size: "sm", className: "px-0 m-0", "aria-label": `Switch language (current: ${lang})`, startContent: renderFlag(lang), endContent: loading ? _jsx(Spinner, { size: "sm" }) : null }) }), _jsx(DropdownMenu, { children: availableLanguages.map((code) => (_jsxs(DropdownItem, { onPress: () => handleLanguageChange(code), className: lang === code ? "bg-primary/10" : "", children: [renderFlag(code), " ", code === "fr" ? "Français" : code === "en" ? "English" : code, isHydrated && lang === code && " ✓"] }, code))) })] }));
62
91
  }
@@ -4,23 +4,23 @@
4
4
  * Mise à jour automatique lors de pnpm version:patch/minor/major
5
5
  */
6
6
  export const PACKAGE_VERSIONS = {
7
- "@lastbrain-labs/module-billing-pro": "^2.0.18",
8
- "@lastbrain-labs/module-cj-analyzer-pro": "^0.1.10",
9
- "@lastbrain-labs/module-core-cart-pro": "^2.0.18",
10
- "@lastbrain-labs/module-core-commerce-pro": "^2.0.18",
11
- "@lastbrain-labs/module-core-order-pro": "^2.0.18",
12
- "@lastbrain-labs/module-core-payment-pro": "^2.0.18",
13
- "@lastbrain-labs/module-core-product-pro": "^2.0.18",
14
- "@lastbrain-labs/module-recipes-pro": "^2.0.18",
15
- "@lastbrain-labs/module-shop-pro": "^0.1.10",
16
- "@lastbrain/app": "^2.0.21",
17
- "@lastbrain/core": "^2.0.19",
18
- "@lastbrain/module-ai": "^2.0.18",
19
- "@lastbrain/module-auth": "^2.0.19",
20
- "@lastbrain/module-legal": "^2.0.18",
21
- "@lastbrain/module-project-board": "^2.0.18",
22
- "@lastbrain/module-tasks": "^2.0.18",
23
- "@lastbrain/ui": "^2.0.19",
24
- "apps/recipe": "^2.0.11",
25
- "lastbrain": "^2.0.11",
7
+ "@lastbrain-labs/module-billing-pro": "^2.0.25",
8
+ "@lastbrain-labs/module-cj-analyzer-pro": "^0.1.17",
9
+ "@lastbrain-labs/module-core-cart-pro": "^2.0.25",
10
+ "@lastbrain-labs/module-core-commerce-pro": "^2.0.25",
11
+ "@lastbrain-labs/module-core-order-pro": "^2.0.25",
12
+ "@lastbrain-labs/module-core-payment-pro": "^2.0.25",
13
+ "@lastbrain-labs/module-core-product-pro": "^2.0.25",
14
+ "@lastbrain-labs/module-recipes-pro": "^2.0.27",
15
+ "@lastbrain-labs/module-shop-pro": "^0.1.17",
16
+ "@lastbrain/app": "^2.0.30",
17
+ "@lastbrain/core": "^2.0.26",
18
+ "@lastbrain/module-ai": "^2.0.25",
19
+ "@lastbrain/module-auth": "^2.0.26",
20
+ "@lastbrain/module-legal": "^2.0.25",
21
+ "@lastbrain/module-project-board": "^2.0.25",
22
+ "@lastbrain/module-tasks": "^2.0.25",
23
+ "@lastbrain/ui": "^2.0.26",
24
+ "apps/recipe": "^2.0.13",
25
+ "lastbrain": "^2.0.13",
26
26
  };
@@ -1,4 +1,4 @@
1
- export type Language = "fr" | "en";
1
+ export type Language = string;
2
2
  /**
3
3
  * Récupère la langue depuis les cookies côté serveur
4
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"server-lang.d.ts","sourceRoot":"","sources":["../../src/i18n/server-lang.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnC;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,QAAQ,CAAC,CAI3D;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA8BjC"}
1
+ {"version":3,"file":"server-lang.d.ts","sourceRoot":"","sources":["../../src/i18n/server-lang.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,QAAQ,CAAC,CAI3D;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA8BjC"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Types pour le système i18n
3
3
  */
4
- export type Language = "fr" | "en";
4
+ export type Language = string;
5
5
  export type TranslationKey = string;
6
6
  export interface Translations {
7
7
  [key: string]: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/i18n/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnC,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,QAAQ,CAAC;IACxB,OAAO,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,MAAM,IAAI,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/i18n/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,QAAQ,CAAC;IACxB,OAAO,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,MAAM,IAAI,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC"}
@@ -21,10 +21,12 @@ export declare function useAuth(): {
21
21
  loading: boolean;
22
22
  isSuperAdmin: boolean;
23
23
  };
24
- export declare function AppProviders({ children, realtimeConfig, lang, translations, }: {
24
+ export declare function AppProviders({ children, realtimeConfig, lang, translations, availableLanguages, }: {
25
25
  children: React.ReactNode;
26
26
  realtimeConfig?: ModuleRealtimeConfig[];
27
27
  lang?: Language;
28
28
  translations?: Record<string, string>;
29
+ /** Liste des langues disponibles depuis locales.generated.ts */
30
+ availableLanguages?: string[];
29
31
  }): import("react/jsx-runtime").JSX.Element;
30
32
  //# sourceMappingURL=AppProviders.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppProviders.d.ts","sourceRoot":"","sources":["../../src/layouts/AppProviders.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAuCnE,wBAAgB,UAAU,kDAEzB;AAED,wBAAgB,gBAAgB;UAvCxB,iBAAiB;aACd,OAAO;WACT,MAAM,GAAG,IAAI;aACX,MAAM,OAAO,CAAC,IAAI,CAAC;gBAChB,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;mBAC1B,MAAM,OAAO,CAAC,IAAI,CAAC;wBACd,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;aACxC,OAAO;eACL,OAAO;oBACF,OAAO;yBACF,OAAO;EA+B7B;AAED,wBAAgB,OAAO;UAjBf,IAAI,GAAG,IAAI;aACR,OAAO;kBACF,OAAO;EAiBtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,cAAmB,EACnB,IAAW,EACX,YAAiB,GAClB,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACxC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC,2CA0CA"}
1
+ {"version":3,"file":"AppProviders.d.ts","sourceRoot":"","sources":["../../src/layouts/AppProviders.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAuCnE,wBAAgB,UAAU,kDAEzB;AAED,wBAAgB,gBAAgB;UAvCxB,iBAAiB;aACd,OAAO;WACT,MAAM,GAAG,IAAI;aACX,MAAM,OAAO,CAAC,IAAI,CAAC;gBAChB,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;mBAC1B,MAAM,OAAO,CAAC,IAAI,CAAC;wBACd,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;aACxC,OAAO;eACL,OAAO;oBACF,OAAO;yBACF,OAAO;EA+B7B;AAED,wBAAgB,OAAO;UAjBf,IAAI,GAAG,IAAI;aACR,OAAO;kBACF,OAAO;EAiBtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,cAAmB,EACnB,IAAW,EACX,YAAiB,EACjB,kBAAiC,GAClC,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACxC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,gEAAgE;IAChE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,2CA8CA"}
@@ -35,7 +35,7 @@ export function useNotifications() {
35
35
  export function useAuth() {
36
36
  return useContext(AuthContext);
37
37
  }
38
- export function AppProviders({ children, realtimeConfig = [], lang = "fr", translations = {}, }) {
38
+ export function AppProviders({ children, realtimeConfig = [], lang = "fr", translations = {}, availableLanguages = ["fr", "en"], }) {
39
39
  const modules = useMemo(() => getModuleConfigs(), []);
40
40
  const { user, loading: authLoading, isSuperAdmin } = useAuthSession();
41
41
  // Hook de notifications seulement quand l'auth est prête
@@ -54,5 +54,5 @@ export function AppProviders({ children, realtimeConfig = [], lang = "fr", trans
54
54
  // Ne retourner un objet vide que si vraiment rien n'est disponible
55
55
  return {};
56
56
  }, [translations]);
57
- return (_jsx(LanguageProvider, { initialLang: lang, translations: memoizedTranslations, children: _jsx(AppLinkProvider, { lang: lang || "fr", children: _jsx(AuthContext.Provider, { value: authValue, children: _jsx(ModuleContext.Provider, { value: modules, children: _jsx(NotificationContext.Provider, { value: notificationsData, children: _jsxs(RealtimeProvider, { userId: user?.id, config: realtimeConfig, children: [children, _jsx(ToastProvider, { placement: "bottom-right", toastOffset: 5 })] }) }) }) }) }) }));
57
+ return (_jsx(LanguageProvider, { initialLang: lang, translations: memoizedTranslations, availableLanguages: availableLanguages, children: _jsx(AppLinkProvider, { lang: lang || "fr", children: _jsx(AuthContext.Provider, { value: authValue, children: _jsx(ModuleContext.Provider, { value: modules, children: _jsx(NotificationContext.Provider, { value: notificationsData, children: _jsxs(RealtimeProvider, { userId: user?.id, config: realtimeConfig, children: [children, _jsx(ToastProvider, { placement: "bottom-right", toastOffset: 5 })] }) }) }) }) }) }));
58
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init-app.d.ts","sourceRoot":"","sources":["../../src/scripts/init-app.ts"],"names":[],"mappings":"AAWA,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,iBAkSpD"}
1
+ {"version":3,"file":"init-app.d.ts","sourceRoot":"","sources":["../../src/scripts/init-app.ts"],"names":[],"mappings":"AAWA,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,iBAqSpD"}
@@ -88,6 +88,8 @@ export async function initApp(options) {
88
88
  await createEnvExample(targetDir, force);
89
89
  await createEnvLocal(targetDir, force, isMonorepoProject);
90
90
  await createEnvProd(targetDir, force);
91
+ // 6b. Créer les fichiers i18n/default pour les traductions personnalisées
92
+ await createI18nDefaults(targetDir, force, projectName);
91
93
  // 7. Créer la structure Supabase avec migrations (seulement pour projets indépendants)
92
94
  if (!isMonorepoProject) {
93
95
  console.log(chalk.blue("🗄️ Création de la structure Supabase locale...\n"));
@@ -891,6 +893,17 @@ export async function middleware(request: NextRequest) {
891
893
  const { pathname } = request.nextUrl;
892
894
  const isApi = pathname.startsWith("/api/");
893
895
 
896
+ // Détecter la langue depuis l'URL (ex: /fr/... ou /es/...)
897
+ const langMatch = pathname.match(/^\\/([a-z]{2})(\\/|$)/);
898
+ const detectedLang = langMatch ? langMatch[1] : "en";
899
+
900
+ // Créer la réponse de base avec les headers de langue
901
+ const createResponseWithLang = (response: NextResponse) => {
902
+ response.headers.set("x-locale", detectedLang);
903
+ response.headers.set("x-pathname", pathname);
904
+ return response;
905
+ };
906
+
894
907
  // Pages publiques d'authentification (ne pas protéger)
895
908
  const publicAuthPages = [
896
909
  "/signin",
@@ -911,7 +924,7 @@ export async function middleware(request: NextRequest) {
911
924
 
912
925
  // Ne pas protéger les pages publiques d'authentification
913
926
  if (isPublicAuthPage) {
914
- return NextResponse.next();
927
+ return createResponseWithLang(NextResponse.next());
915
928
  }
916
929
 
917
930
  // Protéger les routes /auth/* (espace membre)
@@ -1003,7 +1016,7 @@ export async function middleware(request: NextRequest) {
1003
1016
  }
1004
1017
  }
1005
1018
 
1006
- return NextResponse.next();
1019
+ return createResponseWithLang(NextResponse.next());
1007
1020
  }
1008
1021
 
1009
1022
  export const config = {
@@ -1470,8 +1483,6 @@ export const menuCustom: MenuCustom = {
1470
1483
  import type { FooterConfig } from "@lastbrain/ui";
1471
1484
 
1472
1485
  export const footerConfig: FooterConfig = {
1473
- companyName: "${projectName}",
1474
- companyDescription: "Application LastBrain",
1475
1486
  links: [],
1476
1487
  social: [],
1477
1488
  };
@@ -2173,3 +2184,34 @@ export async function GET(
2173
2184
  }
2174
2185
  console.log(chalk.green("✓ Système de proxy storage configuré"));
2175
2186
  }
2187
+ async function createI18nDefaults(targetDir, force, projectName) {
2188
+ console.log(chalk.blue("📝 Création des fichiers i18n/default...\n"));
2189
+ const i18nDefaultDir = path.join(targetDir, "i18n", "default");
2190
+ await fs.ensureDir(i18nDefaultDir);
2191
+ // Fichier FR
2192
+ const frPath = path.join(i18nDefaultDir, "fr.json");
2193
+ const frContent = {
2194
+ "app.name": projectName,
2195
+ "app.description": `Bienvenue dans ${projectName}`,
2196
+ "app.tagline": "Votre plateforme de gestion",
2197
+ "app.icon": "Utensils",
2198
+ };
2199
+ if (!fs.existsSync(frPath) || force) {
2200
+ await fs.writeJson(frPath, frContent, { spaces: 2 });
2201
+ console.log(chalk.green(`✓ i18n/default/fr.json créé`));
2202
+ }
2203
+ // Fichier EN
2204
+ const enPath = path.join(i18nDefaultDir, "en.json");
2205
+ const enContent = {
2206
+ "app.name": projectName,
2207
+ "app.description": `Welcome to ${projectName}`,
2208
+ "app.tagline": "Your management platform",
2209
+ "app.icon": "Utensils",
2210
+ };
2211
+ if (!fs.existsSync(enPath) || force) {
2212
+ await fs.writeJson(enPath, enContent, { spaces: 2 });
2213
+ console.log(chalk.green(`✓ i18n/default/en.json créé`));
2214
+ }
2215
+ console.log(chalk.yellow("\n💡 Vous pouvez personnaliser ces fichiers pour votre application"));
2216
+ console.log(chalk.gray(" Chemin: i18n/default/{fr,en}.json\n"));
2217
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AA8xDA,wBAAsB,cAAc,kBA8GnC"}
1
+ {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AA0uEA,wBAAsB,cAAc,kBAgInC"}