import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import HttpBackend from 'i18next-http-backend'; import { createClient } from '@supabase/supabase-js'; // Initialize Supabase client const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY; if (!supabaseUrl || !supabaseAnonKey) { console.error('❌ Variables d\'environnement Supabase manquantes'); throw new Error('VITE_SUPABASE_URL et VITE_SUPABASE_ANON_KEY sont requis'); } const supabase = createClient(supabaseUrl, supabaseAnonKey); // Custom backend request function to fetch translations from Supabase const customBackendRequest = async (options: any, url: string, payload: any, callback: any) => { try { // Parse the URL to extract language and namespace // URL format: /locales/{{lng}}/{{ns}}.json const urlParts = url.split('/'); const filename = urlParts[urlParts.length - 1]; // e.g., "translation.json" const namespace = filename.replace('.json', ''); // e.g., "translation" const language = urlParts[urlParts.length - 2]; // e.g., "fr" console.log(`🔄 Chargement des traductions: ${language}/${namespace}`); // Query Supabase for translations const { data, error } = await supabase .from('translations') .select('key, value') .eq('lang', language) .eq('namespace', namespace); if (error) { console.error(`❌ Erreur lors du chargement de ${namespace}:`, error); callback(error, null); return; } if (!data || data.length === 0) { console.warn(`⚠ Aucune traduction trouvĂ©e pour ${language}/${namespace}`); callback(null, {}); return; } // Convert flat key-value pairs back to nested object structure const translations: any = {}; data.forEach(({ key, value }) => { const keys = key.split('.'); let current = translations; // Navigate/create the nested structure for (let i = 0; i < keys.length - 1; i++) { if (!current[keys[i]]) { current[keys[i]] = {}; } current = current[keys[i]]; } // Set the final value current[keys[keys.length - 1]] = value; }); console.log(`✅ ${data.length} traductions chargĂ©es pour ${namespace}`); callback(null, translations); } catch (error) { console.error('❌ Erreur lors de la requĂȘte de traductions:', error); callback(error, null); } }; i18n .use(HttpBackend) .use(initReactI18next) .init({ lng: 'fr', // Langue par dĂ©faut fallbackLng: 'fr', // Langue de secours si une traduction est manquante // Espaces de noms par dĂ©faut defaultNS: 'translation', ns: [ 'translation', 'common', 'navigation', 'fileUpload', 'forms', 'tables', 'article', 'client', 'company', 'employee', 'supplier', 'quote', 'order', 'production', 'installation', 'atelier' ], interpolation: { escapeValue: false // React Ă©chappe dĂ©jĂ  les valeurs }, // Configuration du backend HTTP personnalisĂ© backend: { // URL de base pour les traductions (sera interceptĂ©e par notre fonction personnalisĂ©e) loadPath: '/locales/{{lng}}/{{ns}}.json', // Fonction personnalisĂ©e pour charger les traductions depuis Supabase request: customBackendRequest, // Options de cache requestOptions: { cache: 'default' } }, // Options de dĂ©bogage (Ă  dĂ©sactiver en production) debug: import.meta.env.DEV, // Gestion des clĂ©s manquantes saveMissing: import.meta.env.DEV, missingKeyHandler: (lng, ns, key) => { if (import.meta.env.DEV) { console.warn(`ClĂ© manquante: ${key} (${ns}:${lng})`); } }, // Configuration pour le chargement asynchrone react: { useSuspense: false // DĂ©sactiver Suspense pour Ă©viter les problĂšmes de chargement } }); export default i18n;