""" Canonical Languages API Provides the single source of truth for all supported languages and their features """ import logging from typing import List, Dict, Any, Optional from fastapi import APIRouter, HTTPException, Depends from pydantic import BaseModel, Field from config.settings import settings logger = logging.getLogger(__name__) router = APIRouter(prefix="", tags=["languages"]) class LanguageFeature(BaseModel): """Language feature availability""" translation: bool = Field(description="Text translation support") tts: bool = Field(description="Text-to-speech support") stt: bool = Field(description="Speech-to-text support") learning: bool = Field(description="Language learning features") audio_translation: bool = Field(description="Audio translation support") real_time: bool = Field(description="Real-time translation support") class LanguageInfo(BaseModel): """Complete language information""" code: str = Field(description="ISO 639-1/639-3 language code") name: str = Field(description="English name of the language") native_name: str = Field(description="Native name of the language") flag_emoji: str = Field(description="Flag emoji for the language") features: LanguageFeature = Field(description="Available features") models: List[str] = Field(description="Available ML models") tts_engines: List[str] = Field(description="Available TTS engines") stt_engines: List[str] = Field(description="Available STT engines") difficulty_level: int = Field(description="Learning difficulty (1-5)") cultural_notes: Optional[str] = Field(description="Cultural context notes") sample_phrases: List[str] = Field(description="Sample phrases for learning") class LanguagesResponse(BaseModel): """Response model for languages endpoint""" languages: List[LanguageInfo] = Field(description="List of supported languages") total_count: int = Field(description="Total number of supported languages") last_updated: str = Field(description="Last update timestamp") version: str = Field(description="API version") def get_language_data() -> List[LanguageInfo]: """Get comprehensive language data with feature availability""" # Core supported languages with full feature set core_languages = [ { "code": "en", "name": "English", "native_name": "English", "flag_emoji": "🇺🇸", "features": { "translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True }, "models": ["nllb-200", "whisper-large-v3", "m2m100"], "tts_engines": ["coqui", "gtts", "pyttsx3"], "stt_engines": ["whisper", "vosk", "speech_recognition"], "difficulty_level": 1, "cultural_notes": "Global lingua franca with many regional variants", "sample_phrases": ["Hello", "How are you?", "Thank you", "Goodbye"] }, { "code": "rw", "name": "Kinyarwanda", "native_name": "Ikinyarwanda", "flag_emoji": "🇷🇼", "features": { "translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True }, "models": ["nllb-200", "whisper-base"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 3, "cultural_notes": "Official language of Rwanda, Bantu language with rich cultural heritage", "sample_phrases": ["Muraho", "Amakuru?", "Murakoze", "Mwirirwe"] }, { "code": "es", "name": "Spanish", "native_name": "Español", "flag_emoji": "🇪🇸", "features": { "translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True }, "models": ["nllb-200", "whisper-large-v3", "marianmt"], "tts_engines": ["coqui", "gtts", "pyttsx3"], "stt_engines": ["whisper", "vosk", "speech_recognition"], "difficulty_level": 2, "cultural_notes": "Second most spoken language globally, rich literary tradition", "sample_phrases": ["Hola", "¿Cómo estás?", "Gracias", "Adiós"] }, { "code": "fr", "name": "French", "native_name": "Français", "flag_emoji": "🇫🇷", "features": { "translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True }, "models": ["nllb-200", "whisper-large-v3", "marianmt"], "tts_engines": ["coqui", "gtts", "pyttsx3"], "stt_engines": ["whisper", "vosk", "speech_recognition"], "difficulty_level": 3, "cultural_notes": "Language of diplomacy, rich cultural and literary heritage", "sample_phrases": ["Bonjour", "Comment allez-vous?", "Merci", "Au revoir"] }, { "code": "de", "name": "German", "native_name": "Deutsch", "flag_emoji": "🇩🇪", "features": { "translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True }, "models": ["nllb-200", "whisper-large-v3", "marianmt"], "tts_engines": ["coqui", "gtts", "pyttsx3"], "stt_engines": ["whisper", "vosk", "speech_recognition"], "difficulty_level": 4, "cultural_notes": "Important European language with complex grammar", "sample_phrases": ["Hallo", "Wie geht es dir?", "Danke", "Auf Wiedersehen"] }, { "code": "sw", "name": "Swahili", "native_name": "Kiswahili", "flag_emoji": "🇰🇪", "features": { "translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True }, "models": ["nllb-200", "whisper-base"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 2, "cultural_notes": "Lingua franca of East Africa, Bantu language", "sample_phrases": ["Hujambo", "Habari yako?", "Asante", "Kwaheri"] } ] # Extended language list with basic features extended_languages = [ { "code": "it", "name": "Italian", "native_name": "Italiano", "flag_emoji": "🇮🇹", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-large-v3"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 3, "cultural_notes": "Romance language with rich cultural heritage", "sample_phrases": ["Ciao", "Come stai?", "Grazie", "Arrivederci"] }, { "code": "pt", "name": "Portuguese", "native_name": "Português", "flag_emoji": "🇵🇹", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-large-v3"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 3, "cultural_notes": "Spoken in Brazil, Portugal, and other countries", "sample_phrases": ["Olá", "Como está?", "Obrigado", "Tchau"] }, { "code": "ru", "name": "Russian", "native_name": "Русский", "flag_emoji": "🇷🇺", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-large-v3"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 4, "cultural_notes": "Slavic language with Cyrillic script", "sample_phrases": ["Привет", "Как дела?", "Спасибо", "До свидания"] }, { "code": "zh", "name": "Chinese", "native_name": "中文", "flag_emoji": "🇨🇳", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-large-v3"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 5, "cultural_notes": "Most spoken language globally, tonal language", "sample_phrases": ["你好", "你好吗?", "谢谢", "再见"] }, { "code": "ja", "name": "Japanese", "native_name": "日本語", "flag_emoji": "🇯🇵", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-large-v3"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 5, "cultural_notes": "Complex writing system with hiragana, katakana, and kanji", "sample_phrases": ["こんにちは", "元気ですか?", "ありがとう", "さようなら"] }, { "code": "ko", "name": "Korean", "native_name": "한국어", "flag_emoji": "🇰🇷", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-large-v3"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 4, "cultural_notes": "Hangul script, honorific system", "sample_phrases": ["안녕하세요", "어떻게 지내세요?", "감사합니다", "안녕히 가세요"] }, { "code": "ar", "name": "Arabic", "native_name": "العربية", "flag_emoji": "🇸🇦", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-large-v3"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 4, "cultural_notes": "Right-to-left script, rich literary tradition", "sample_phrases": ["مرحبا", "كيف حالك?", "شكرا", "وداعا"] }, { "code": "hi", "name": "Hindi", "native_name": "हिन्दी", "flag_emoji": "🇮🇳", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-large-v3"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 3, "cultural_notes": "Official language of India, Devanagari script", "sample_phrases": ["नमस्ते", "आप कैसे हैं?", "धन्यवाद", "अलविदा"] } ] # Additional African languages with Kinyarwanda emphasis african_languages = [ { "code": "yo", "name": "Yoruba", "native_name": "Yorùbá", "flag_emoji": "🇳🇬", "features": {"translation": True, "tts": True, "stt": False, "learning": True, "audio_translation": False, "real_time": True}, "models": ["nllb-200"], "tts_engines": ["coqui"], "stt_engines": ["whisper"], "difficulty_level": 3, "cultural_notes": "Niger-Congo language spoken in Nigeria", "sample_phrases": ["Bawo", "Bawo ni?", "E se", "O dabọ"] }, { "code": "ig", "name": "Igbo", "native_name": "Igbo", "flag_emoji": "🇳🇬", "features": {"translation": True, "tts": True, "stt": False, "learning": True, "audio_translation": False, "real_time": True}, "models": ["nllb-200"], "tts_engines": ["coqui"], "stt_engines": ["whisper"], "difficulty_level": 3, "cultural_notes": "Niger-Congo language spoken in Nigeria", "sample_phrases": ["Ndewo", "Kedu ka ị mere?", "Daalụ", "Ka ọ dị"] }, { "code": "ha", "name": "Hausa", "native_name": "Hausa", "flag_emoji": "🇳🇬", "features": {"translation": True, "tts": True, "stt": False, "learning": True, "audio_translation": False, "real_time": True}, "models": ["nllb-200"], "tts_engines": ["coqui"], "stt_engines": ["whisper"], "difficulty_level": 2, "cultural_notes": "Chadic language, lingua franca in West Africa", "sample_phrases": ["Sannu", "Yaya kuke?", "Na gode", "Sai an jima"] }, { "code": "zu", "name": "Zulu", "native_name": "isiZulu", "flag_emoji": "🇿🇦", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-base"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 3, "cultural_notes": "Bantu language, official in South Africa", "sample_phrases": ["Sawubona", "Unjani?", "Ngiyabonga", "Hamba kahle"] }, { "code": "xh", "name": "Xhosa", "native_name": "isiXhosa", "flag_emoji": "🇿🇦", "features": {"translation": True, "tts": True, "stt": True, "learning": True, "audio_translation": True, "real_time": True}, "models": ["nllb-200", "whisper-base"], "tts_engines": ["coqui", "gtts"], "stt_engines": ["whisper", "vosk"], "difficulty_level": 3, "cultural_notes": "Bantu language with click consonants", "sample_phrases": ["Molo", "Unjani?", "Enkosi", "Hamba kakuhle"] }, { "code": "am", "name": "Amharic", "native_name": "አማርኛ", "flag_emoji": "🇪🇹", "features": {"translation": True, "tts": True, "stt": False, "learning": True, "audio_translation": False, "real_time": True}, "models": ["nllb-200"], "tts_engines": ["coqui"], "stt_engines": ["whisper"], "difficulty_level": 4, "cultural_notes": "Semitic language, official in Ethiopia", "sample_phrases": ["ሰላም", "እንዴት ነህ?", "አመሰግናለሁ", "ቻይ"] } ] # Combine all languages all_languages = core_languages + extended_languages + african_languages # Convert to LanguageInfo objects return [LanguageInfo(**lang) for lang in all_languages] @router.get("/languages", response_model=LanguagesResponse) async def get_supported_languages( include_features: bool = True, language_codes: Optional[str] = None ): """ Get all supported languages with their features and capabilities. This is the canonical endpoint for language information across the platform. Args: include_features: Whether to include detailed feature information language_codes: Comma-separated list of language codes to filter (optional) Returns: LanguagesResponse with complete language information """ try: logger.info("Languages API requested", extra={ "include_features": include_features, "language_codes": language_codes }) # Get all language data languages = get_language_data() # Filter by language codes if provided if language_codes: requested_codes = [code.strip().lower() for code in language_codes.split(",")] languages = [lang for lang in languages if lang.code.lower() in requested_codes] # Ensure Kinyarwanda is always included and prominently featured rw_language = next((lang for lang in languages if lang.code == "rw"), None) if rw_language and rw_language not in languages[:5]: # Move to top 5 if not already languages = [rw_language] + [lang for lang in languages if lang.code != "rw"] # Log the response logger.info(f"Returning {len(languages)} languages", extra={ "total_count": len(languages), "kinyarwanda_included": any(lang.code == "rw" for lang in languages), "kinyarwanda_features": rw_language.features.dict() if rw_language else None }) return LanguagesResponse( languages=languages, total_count=len(languages), last_updated="2025-01-01T00:00:00Z", version="1.0.0" ) except Exception as e: logger.error(f"Error in languages API: {str(e)}") raise HTTPException(status_code=500, detail=f"Failed to fetch languages: {str(e)}") @router.get("/languages/{language_code}", response_model=LanguageInfo) async def get_language_info(language_code: str): """ Get detailed information for a specific language. Args: language_code: ISO 639-1/639-3 language code (e.g., 'rw', 'en', 'es') Returns: LanguageInfo with complete details for the requested language """ try: language_code = language_code.lower() languages = get_language_data() language = next((lang for lang in languages if lang.code.lower() == language_code), None) if not language: raise HTTPException( status_code=404, detail=f"Language '{language_code}' not supported. Use /api/languages to see available languages." ) logger.info(f"Language info requested for {language_code}", extra={ "language_name": language.name, "features": language.features.dict() }) return language except HTTPException: raise except Exception as e: logger.error(f"Error fetching language info for {language_code}: {str(e)}") raise HTTPException(status_code=500, detail=f"Failed to fetch language info: {str(e)}") @router.get("/languages/features/summary") async def get_language_features_summary(): """ Get a summary of language features across all supported languages. Returns: Summary statistics of language capabilities """ try: languages = get_language_data() # Calculate feature statistics total_languages = len(languages) translation_support = sum(1 for lang in languages if lang.features.translation) tts_support = sum(1 for lang in languages if lang.features.tts) stt_support = sum(1 for lang in languages if lang.features.stt) learning_support = sum(1 for lang in languages if lang.features.learning) audio_translation_support = sum(1 for lang in languages if lang.features.audio_translation) real_time_support = sum(1 for lang in languages if lang.features.real_time) # Language families represented language_families = { "Indo-European": ["en", "es", "fr", "de", "it", "pt", "ru", "hi"], "Sino-Tibetan": ["zh"], "Japonic": ["ja"], "Koreanic": ["ko"], "Afro-Asiatic": ["ar", "am"], "Niger-Congo": ["rw", "sw", "yo", "ig", "ha", "zu", "xh"], "Constructed": [] } families_count = {} for family, codes in language_families.items(): count = sum(1 for lang in languages if lang.code in codes) if count > 0: families_count[family] = count return { "total_languages": total_languages, "feature_support": { "translation": {"count": translation_support, "percentage": round(translation_support/total_languages*100, 1)}, "tts": {"count": tts_support, "percentage": round(tts_support/total_languages*100, 1)}, "stt": {"count": stt_support, "percentage": round(stt_support/total_languages*100, 1)}, "learning": {"count": learning_support, "percentage": round(learning_support/total_languages*100, 1)}, "audio_translation": {"count": audio_translation_support, "percentage": round(audio_translation_support/total_languages*100, 1)}, "real_time": {"count": real_time_support, "percentage": round(real_time_support/total_languages*100, 1)} }, "language_families": families_count, "special_emphasis": { "kinyarwanda": { "fully_supported": True, "features": ["translation", "tts", "stt", "learning", "audio_translation", "real_time"], "models": ["nllb-200", "whisper-base"], "cultural_notes": "Official language of Rwanda with rich Bantu heritage" } } } except Exception as e: logger.error(f"Error generating language features summary: {str(e)}") raise HTTPException(status_code=500, detail=f"Failed to generate summary: {str(e)}")