import { useState, useEffect } from "react"; import { apiClient } from "@/lib/api/client"; // 전역 언어 상태 (다른 컴포넌트에서 접근 가능) let globalUserLang = "KR"; let globalChangeLangCallback: ((lang: string) => void) | null = null; // 🎯 효율적인 언어 변경 알림 시스템 const languageChangeCallbacks = new Set<(newLang: string) => void>(); // 전역 언어 변경 함수 (외부에서 호출 가능하도록 export) export const notifyLanguageChange = (newLang: string) => { globalUserLang = newLang; (window as any).__GLOBAL_USER_LANG = newLang; localStorage.setItem("userLocale", newLang); localStorage.setItem("userLocaleLoaded", "true"); (window as any).__GLOBAL_USER_LOCALE_LOADED = true; // 🗑️ 번역 캐시 초기화 (window as any).__TRANSLATION_CACHE = {}; // 모든 컴포넌트에 즉시 알림 (폴링 없이!) languageChangeCallbacks.forEach((callback) => callback(newLang)); console.log("🔄 모든 컴포넌트에 언어 변경 알림:", newLang); }; export const useMultiLang = (options: { companyCode?: string } = {}) => { const [userLang, setUserLang] = useState(null); // null로 시작 const companyCode = options.companyCode || "*"; // 🎯 효율적인 언어 변경 감지 (폴링 대신 콜백 등록) useEffect(() => { // 언어 변경 콜백 등록 const handleLanguageChange = (newLang: string) => { console.log("🔄 언어 변경 감지 (즉시):", { from: userLang, to: newLang }); setUserLang(newLang); }; languageChangeCallbacks.add(handleLanguageChange); // 초기 언어 설정 const currentLang = (window as any).__GLOBAL_USER_LANG || localStorage.getItem("userLocale") || globalUserLang; if (currentLang && currentLang !== userLang) { setUserLang(currentLang); } // 클린업: 콜백 제거 return () => { languageChangeCallbacks.delete(handleLanguageChange); }; }, []); // 한 번만 등록 // 언어 변경 시 전역 콜백 호출 (무한 루프 방지) useEffect(() => { // 언어가 설정된 경우에만 콜백 호출 if (globalChangeLangCallback && userLang) { globalChangeLangCallback(userLang); } }, [userLang]); // 사용자 로케일 조회 (한 번만 실행) useEffect(() => { // localStorage에서 로케일 확인 (새 창에서도 공유) const storedLocale = localStorage.getItem("userLocale"); const storedLocaleLoaded = localStorage.getItem("userLocaleLoaded"); if (storedLocaleLoaded === "true" && storedLocale) { console.log("🌐 localStorage에서 사용자 로케일 사용:", storedLocale); setUserLang(storedLocale); globalUserLang = storedLocale; // 전역 상태도 동기화 (window as any).__GLOBAL_USER_LANG = storedLocale; (window as any).__GLOBAL_USER_LOCALE_LOADED = true; return; } // 전역에서 이미 로케일이 로드되었는지 확인 if ((window as any).__GLOBAL_USER_LOCALE_LOADED) { const globalLocale = (window as any).__GLOBAL_USER_LANG; console.log("🌐 전역에서 사용자 로케일 사용:", globalLocale); setUserLang(globalLocale); globalUserLang = globalLocale; return; } // 이미 로케일이 설정되어 있으면 중복 호출 방지 if (globalUserLang) { setUserLang(globalUserLang); return; } // 전역 로케일이 아직 로드되지 않았으면 대기 console.log("⏳ 전역 로케일 로드 대기 중..."); // 주기적으로 전역 로케일 확인 (더 빠른 간격으로) const checkInterval = setInterval(() => { if ((window as any).__GLOBAL_USER_LOCALE_LOADED) { const globalLocale = (window as any).__GLOBAL_USER_LANG; console.log("🌐 전역에서 사용자 로케일 확인됨:", globalLocale); setUserLang(globalLocale); globalUserLang = globalLocale; clearInterval(checkInterval); } }, 50); // 50ms로 단축 // 3초 후 타임아웃 (더 빠른 타임아웃) setTimeout(() => { clearInterval(checkInterval); if (!userLang) { console.warn("⚠️ 전역 로케일 로드 타임아웃, 기본값 사용"); setUserLang("KR"); globalUserLang = "KR"; } }, 3000); // 3초로 단축 return () => clearInterval(checkInterval); }, []); // 다국어 텍스트 가져오기 (배치 조회 방식) const getText = async (menuCode: string, langKey: string, fallback?: string): Promise => { console.log("🔍 다국어 텍스트 요청 (배치 방식):", { menuCode, langKey, userLang, companyCode }); try { // 배치 조회 API 사용 const response = await apiClient.post( "/multilang/batch", { langKeys: [langKey], }, { params: { companyCode, menuCode, userLang, }, }, ); console.log("📡 배치 API 응답 상태:", response.status, response.statusText); if (response.data.success && response.data.data && response.data.data[langKey]) { // 번역 텍스트를 캐시에 저장 const cacheKey = `${menuCode}.${langKey}`; const currentCache = (window as any).__TRANSLATION_CACHE || {}; currentCache[cacheKey] = response.data.data[langKey]; (window as any).__TRANSLATION_CACHE = currentCache; return response.data.data[langKey]; } // 실패 시 fallback 또는 키 반환 console.log("🔄 배치 API 성공했지만 데이터 없음, fallback 반환:", fallback || langKey); return fallback || langKey; } catch (error) { console.error("❌ 다국어 텍스트 배치 조회 실패:", error); console.log("🔄 에러 시 fallback 반환:", fallback || langKey); return fallback || langKey; } }; // 🎯 효율적인 언어 변경 함수 (콜백 방식) const changeLang = async (newLang: string) => { console.log("🚀 useMultiLang changeLang 호출:", { newLang, userLang }); // 같은 언어로 변경하려는 경우 무시 if (newLang === userLang) { console.log("🔄 같은 언어로 변경 시도 무시:", newLang); return; } try { console.log("🔄 언어 변경 시작:", { from: userLang, to: newLang }); // 백엔드에 사용자 로케일 설정 요청 const response = await apiClient.post("/admin/user-locale", { locale: newLang, }); if (response.data.success) { // 🎯 핵심: 즉시 모든 컴포넌트에 알림 (폴링 없이!) notifyLanguageChange(newLang); console.log("✅ 언어 변경 완료 및 모든 컴포넌트에 즉시 알림:", newLang); } else { console.error("❌ 사용자 로케일 변경 실패:", response.data.message); } } catch (error) { console.error("❌ 사용자 로케일 변경 중 오류:", error); // 오류 시에도 클라이언트 상태는 변경 notifyLanguageChange(newLang); } }; // 전역 언어 상태 접근자 const getGlobalUserLang = () => globalUserLang; const setGlobalChangeLangCallback = (callback: (lang: string) => void) => { globalChangeLangCallback = callback; }; return { userLang, getText, changeLang, companyCode, getGlobalUserLang, setGlobalChangeLangCallback, }; };