'use client'; import { useState, useEffect, useCallback, useMemo } from 'react'; import type { UseTranslationConfigReturn, Language, TranslationMode, Provider, TranslationConfig, AvailableProvider, } from './types'; import { API_BASE } from '@/lib/config'; import { useTranslationStore } from '@/lib/store'; /** Fallback when API fails — Google is always available server-side */ const FALLBACK_PROVIDERS: AvailableProvider[] = [ { id: 'google', label: 'Google Traduction', description: 'Traduction rapide, 130+ langues', mode: 'classic' }, ]; const FALLBACK_LANGUAGES: Language[] = [ // Top 5 — dominant on the internet { code: 'en', name: 'English' }, { code: 'es', name: 'Spanish' }, { code: 'de', name: 'German' }, { code: 'fr', name: 'French' }, { code: 'ja', name: 'Japanese' }, // Top 6-15 { code: 'pt', name: 'Portuguese' }, { code: 'ru', name: 'Russian' }, { code: 'it', name: 'Italian' }, { code: 'zh-CN', name: 'Chinese (Simplified)' }, { code: 'zh-TW', name: 'Chinese (Traditional)' }, { code: 'pl', name: 'Polish' }, { code: 'nl', name: 'Dutch' }, { code: 'tr', name: 'Turkish' }, { code: 'ko', name: 'Korean' }, { code: 'ar', name: 'Arabic' }, // Top 16-25 { code: 'fa', name: 'Persian (Farsi)' }, { code: 'vi', name: 'Vietnamese' }, { code: 'id', name: 'Indonesian' }, { code: 'uk', name: 'Ukrainian' }, { code: 'sv', name: 'Swedish' }, { code: 'cs', name: 'Czech' }, { code: 'el', name: 'Greek' }, { code: 'he', name: 'Hebrew' }, { code: 'hi', name: 'Hindi' }, { code: 'ro', name: 'Romanian' }, // Others { code: 'da', name: 'Danish' }, { code: 'fi', name: 'Finnish' }, { code: 'no', name: 'Norwegian' }, { code: 'hu', name: 'Hungarian' }, { code: 'th', name: 'Thai' }, { code: 'sk', name: 'Slovak' }, { code: 'bg', name: 'Bulgarian' }, { code: 'hr', name: 'Croatian' }, { code: 'ca', name: 'Catalan' }, { code: 'ms', name: 'Malay' }, ]; export function useTranslationConfig(hasFile: boolean): UseTranslationConfigReturn { const { settings } = useTranslationStore(); const [sourceLang, setSourceLang] = useState('auto'); const [targetLang, setTargetLang] = useState(settings.defaultTargetLanguage || ''); const [provider, setProvider] = useState(null); const [glossaryId, setGlossaryId] = useState(null); const [availableProviders, setAvailableProviders] = useState([]); const [isLoadingProviders, setIsLoadingProviders] = useState(false); const [languages, setLanguages] = useState([]); const [isPro, setIsPro] = useState(false); const [isLoadingLanguages, setIsLoadingLanguages] = useState(false); const [languagesError, setLanguagesError] = useState(null); // Sync with store default target language useEffect(() => { if (settings.defaultTargetLanguage && !targetLang) { setTargetLang(settings.defaultTargetLanguage); } }, [settings.defaultTargetLanguage]); // eslint-disable-line react-hooks/exhaustive-deps // Reset glossary selection when source language changes useEffect(() => { setGlossaryId(null); }, [sourceLang]); // eslint-disable-line react-hooks/exhaustive-deps // Fetch available (admin-configured) providers useEffect(() => { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 8000); const fetchProviders = async () => { setIsLoadingProviders(true); try { const token = localStorage.getItem('token'); const headers: Record = {}; if (token) headers['Authorization'] = `Bearer ${token}`; const response = await fetch(`${API_BASE}/api/v1/providers/available`, { headers, signal: controller.signal, }); if (response.ok) { const data = await response.json(); const list = data.providers || []; setAvailableProviders(list.length > 0 ? list : FALLBACK_PROVIDERS); } else { setAvailableProviders(FALLBACK_PROVIDERS); } } catch { // Backend down or timeout — use fallback so user can still try setAvailableProviders(FALLBACK_PROVIDERS); } finally { clearTimeout(timeoutId); setIsLoadingProviders(false); } }; fetchProviders(); return () => { controller.abort(); clearTimeout(timeoutId); }; }, []); // Auto-select first classic provider for non-Pro users useEffect(() => { if (isLoadingProviders) return; if (provider !== null) return; if (isPro) return; if (availableProviders.length === 0) return; const firstClassic = availableProviders.find((p) => p.mode === 'classic'); if (firstClassic) setProvider(firstClassic.id); }, [availableProviders, isLoadingProviders, isPro, provider]); // Fetch supported languages useEffect(() => { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 8000); const fetchLanguages = async () => { setIsLoadingLanguages(true); setLanguagesError(null); try { const token = localStorage.getItem('token'); const headers: Record = { 'Content-Type': 'application/json' }; if (token) headers['Authorization'] = `Bearer ${token}`; const response = await fetch(`${API_BASE}/api/v1/languages`, { headers, signal: controller.signal, }); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const data = await response.json(); const langList: Language[] = Object.entries(data.supported_languages || {}).map( ([code, name]) => ({ code, name: name as string }) ); setLanguages(langList.length > 0 ? langList : FALLBACK_LANGUAGES); } catch (error) { if (error instanceof DOMException && error.name === 'AbortError') { console.warn('Language fetch timed out, using fallback list'); } else { setLanguagesError(error instanceof Error ? error.message : 'Failed to load languages'); } setLanguages(FALLBACK_LANGUAGES); } finally { clearTimeout(timeoutId); setIsLoadingLanguages(false); } }; fetchLanguages(); return () => { controller.abort(); clearTimeout(timeoutId); }; }, []); // Check user tier useEffect(() => { const checkTier = async () => { const isProTier = (u: any) => ['pro', 'business', 'enterprise'].includes(u?.plan ?? u?.tier ?? ''); const userStr = localStorage.getItem('user'); if (userStr) { try { const user = JSON.parse(userStr); if (user?.plan || user?.tier) { setIsPro(isProTier(user)); } } catch { /* continue */ } } try { const token = localStorage.getItem('token'); if (!token) { setIsPro(false); return; } const response = await fetch(`${API_BASE}/api/v1/auth/me`, { headers: { 'Authorization': `Bearer ${token}` }, }); if (response.ok) { const result = await response.json(); const user = result.data; setIsPro(isProTier(user)); localStorage.setItem('user', JSON.stringify(user)); } else { setIsPro(false); } } catch { setIsPro(false); } }; checkTier(); }, []); // Mode is derived from the selected provider, never set manually. const mode = useMemo(() => { if (!provider) return 'classic'; const p = availableProviders.find((ap) => ap.id === provider); return p?.mode === 'llm' ? 'llm' : 'classic'; }, [provider, availableProviders]); const isConfigValid = useMemo(() => { if (!hasFile || !targetLang) return false; if (!provider) return false; return true; }, [hasFile, targetLang, provider]); const getConfig = useCallback((): TranslationConfig => ({ sourceLang, targetLang, mode, provider: provider ?? undefined, glossaryId, }), [sourceLang, targetLang, mode, provider, glossaryId]); return { sourceLang, targetLang, mode, provider, availableProviders, isLoadingProviders, languages, isPro, isConfigValid, isLoadingLanguages, languagesError, setSourceLang, setTargetLang, setProvider, glossaryId, setGlossaryId, getConfig, }; }