feat: i18n full support, responsive UI, multi-model AI config, and bug fixes

Major Features:
- Internationalization (i18n) with auto-detection for ES/EN/PT
- Mobile-first responsive design for Studio and Experience
- Multi-model AI configuration (llama3.2:3b, qwen3.5:9b, gpt-oss:latest)
- Course language configuration (auto-detect or fixed per course)

Backend Changes:
- shared/common: ModelType enum for intelligent model selection
- LMS: log_ai_usage function migration (fix chat tutor 500 error)
- LMS/CMS: course language config fields (language_setting, fixed_language)
- LMS: /courses/{id}/language-config endpoint for language detection

Frontend Changes:
- Experience: Enhanced i18n with browser language detection
- Experience: Audio recording with HTTPS check and error handling
- Studio: Memory game with unique pair IDs and debug logging
- Studio: Expanded translations (250+ keys for ES, EN, PT)
- Both: Language selector in headers (mobile responsive)

Documentation:
- AI_MODELS_CONFIG.md: Multi-model configuration guide
- RESPONSIVIDAD_GUIA.md: Mobile-first design patterns
- I18N_RESPONSIVIDAD_IMPLEMENTACION.md: Implementation details
- DEBUG_AUDIO_RECORDING.md: Audio troubleshooting guide
- DEBUG_MEMORY_GAME.md: Memory game debugging steps

Bug Fixes:
- Fix chat tutor 500 error (missing log_ai_usage function)
- Fix audio recording (HTTPS check, browser compatibility)
- Fix memory game pair IDs (unique ID generation)
- Fix HotspotBlock TypeScript errors

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-03-23 12:24:22 -03:00
parent 0598fc4865
commit 2ff06ee7ae
26 changed files with 2993 additions and 124 deletions
+13
View File
@@ -11,6 +11,19 @@ const getApiBaseUrl = (defaultPort: string, envVar?: string) => {
export const API_BASE_URL = getApiBaseUrl("3001", process.env.NEXT_PUBLIC_CMS_API_URL);
export const LMS_API_BASE_URL = getApiBaseUrl("3002", process.env.NEXT_PUBLIC_LMS_API_URL);
// Polyfill for crypto.randomUUID() for non-HTTPS contexts
export function generateUUID(): string {
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
return crypto.randomUUID();
}
// Fallback for non-secure contexts
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
export const getImageUrl = (path?: string) => {
if (!path) return '';
if (path.startsWith('http')) return path;
+235 -21
View File
@@ -1,39 +1,253 @@
{
"common": {
"loading": "Cargando...",
"back": "Volver",
"next": "Siguiente",
"finish": "Finalizar",
"error": "Error",
"retry": "Reintentar",
"save": "Guardar",
"cancel": "Cancelar",
"delete": "Eliminar",
"edit": "Editar",
"close": "Cerrar",
"confirm": "Confirmar",
"success": "Éxito",
"warning": "Advertencia",
"create": "Crear",
"loading": "Cargando...",
"add": "Agregar",
"remove": "Eliminar",
"update": "Actualizar",
"search": "Buscar",
"back": "Volver",
"next": "Siguiente",
"filter": "Filtrar",
"export": "Exportar",
"import": "Importar",
"download": "Descargar",
"upload": "Subir",
"preview": "Vista Previa",
"publish": "Publicar",
"preview": "Previsualizar"
"draft": "Borrador",
"archive": "Archivar",
"duplicate": "Duplicar",
"settings": "Configuración"
},
"nav": {
"dashboard": "Dashboard",
"courses": "Cursos",
"globalControl": "Control Global",
"webhooks": "Webhooks",
"library": "Librería",
"questionBank": "Banco de Preguntas",
"testTemplates": "Plantillas de Pruebas",
"analytics": "Analíticas",
"students": "Estudiantes",
"instructors": "Instructores",
"settings": "Configuración",
"profile": "Perfil",
"signOut": "Cerrar Sesión"
"signOut": "Cerrar Sesión",
"selectLanguage": "Seleccionar Idioma",
"controlPanel": "Panel de Control",
"webhooks": "Webhooks",
"globalControl": "Control Global"
},
"dashboard": {
"title": "Panel de Control",
"newCourse": "Nuevo Curso",
"aiBuilder": "Constructor AI",
"noCourses": "No hay cursos disponibles."
"course": {
"createCourse": "Crear Curso",
"editCourse": "Editar Curso",
"deleteCourse": "Eliminar Curso",
"courseTitle": "Título del Curso",
"courseDescription": "Descripción del Curso",
"courseLanguage": "Idioma del Curso",
"languageAuto": "Automático (detectar del usuario)",
"languageFixed": "Fijo (siempre este idioma)",
"languageEnglish": "Inglés",
"languageSpanish": "Español",
"languagePortuguese": "Portugués",
"modules": "Módulos",
"addModule": "Agregar Módulo",
"editModule": "Editar Módulo",
"deleteModule": "Eliminar Módulo",
"moduleName": "Nombre del Módulo",
"moduleDescription": "Descripción del Módulo",
"lessons": "Lecciones",
"addLesson": "Agregar Lección",
"editLesson": "Editar Lección",
"deleteLesson": "Eliminar Lección",
"lessonTitle": "Título de la Lección",
"lessonType": "Tipo de Lección",
"blocks": "Bloques",
"addBlock": "Agregar Bloque",
"blockType": "Tipo de Bloque",
"reorderBlocks": "Reordenar Bloques",
"moveUp": "Mover Arriba",
"moveDown": "Mover Abajo",
"pricing": "Precios",
"currency": "Moneda",
"enrollment": "Inscripciones",
"published": "Publicado",
"unpublished": "No Publicado",
"visibility": "Visibilidad",
"public": "Público",
"private": "Privado"
},
"editor": {
"outline": "Estructura",
"settings": "Configuración",
"newModule": "Nuevo Módulo",
"newLesson": "Nueva Lección",
"addBlock": "Añadir Bloque de Contenido",
"publishToOrg": "Publicar en Organización",
"reading": "Lectura"
"content": {
"video": "Video",
"audio": "Audio",
"text": "Texto",
"image": "Imagen",
"document": "Documento",
"quiz": "Cuestionario",
"codeExercise": "Ejercicio de Código",
"memoryGame": "Juego de Memoria",
"dragToCube": "Arrastrar al Cubo",
"hotspots": "Puntos Calientes",
"videoMarkers": "Marcadores de Video",
"audioResponse": "Respuesta de Audio",
"fillBlanks": "Completar Espacios",
"matching": "Emparejamiento",
"ordering": "Ordenamiento",
"shortAnswer": "Respuesta Corta",
"multipleChoice": "Opción Múltiple",
"trueFalse": "Verdadero/Falso",
"essay": "Ensayo",
"uploadMedia": "Subir Archivo",
"dropFiles": "Soltar archivos aquí",
"browseFiles": "Examinar archivos",
"uploading": "Subiendo...",
"uploadComplete": "Subida Completa",
"uploadFailed": "Subida Fallida"
},
"ai": {
"generateWithAI": "Generar con IA",
"generateCourse": "Generar Curso con IA",
"generateQuiz": "Generar Cuestionario con IA",
"generateSummary": "Generar Resumen con IA",
"generateTranscription": "Transcribir con IA",
"generateQuestions": "Generar Preguntas con IA",
"topicPrompt": "Ingresa el tema del curso",
"generating": "Generando...",
"generationComplete": "Generación Completa",
"generationFailed": "Generación Fallida",
"aiPowered": "Potenciado por IA",
"model": "Modelo de IA",
"tokens": "Tokens",
"cost": "Costo"
},
"grading": {
"gradingSystem": "Sistema de Calificación",
"categories": "Categorías",
"weights": "Pesos",
"passingGrade": "Nota de Aprobación",
"percentage": "Porcentaje",
"dropLowest": "Eliminar Notas Más Bajas",
"maxAttempts": "Intentos Máximos",
"autoGrade": "Calificación Automática",
"manualGrade": "Calificación Manual",
"rubric": "Rúbrica",
"feedback": "Retroalimentación",
"grades": "Notas",
"exportGrades": "Exportar Notas",
"importGrades": "Importar Notas"
},
"students": {
"enrollments": "Inscripciones",
"enrollStudent": "Inscribir Estudiante",
"bulkEnroll": "Inscripción Masiva",
"studentProgress": "Progreso del Estudiante",
"studentGrades": "Notas del Estudiante",
"studentActivity": "Actividad del Estudiante",
"cohort": "Cohorte",
"groups": "Grupos",
"retention": "Retención",
"dropoutRisk": "Riesgo de Abandono",
"engagement": "Compromiso"
},
"analytics": {
"overview": "Resumen",
"enrollments": "Inscripciones",
"completions": "Completaciones",
"averageGrade": "Nota Promedio",
"engagement": "Compromiso",
"retention": "Retención",
"revenue": "Ingresos",
"aiUsage": "Uso de IA",
"exportReport": "Exportar Reporte",
"dateRange": "Rango de Fechas",
"compareCourses": "Comparar Cursos",
"studentPerformance": "Rendimiento Estudiantil"
},
"settings": {
"general": "Configuración General",
"branding": "Marca",
"logo": "Logotipo",
"favicon": "Favicon",
"colors": "Colores",
"platformName": "Nombre de la Plataforma",
"notifications": "Notificaciones",
"emailTemplates": "Plantillas de Email",
"integrations": "Integraciones",
"lti": "LTI",
"sso": "SSO",
"webhooks": "Webhooks",
"apiKeys": "Claves API",
"security": "Seguridad",
"backup": "Respaldo",
"export": "Exportar Datos",
"import": "Importar Datos"
},
"user": {
"profile": "Perfil",
"editProfile": "Editar Perfil",
"changePassword": "Cambiar Contraseña",
"preferences": "Preferencias",
"language": "Idioma",
"timezone": "Zona Horaria",
"notifications": "Notificaciones",
"avatar": "Avatar",
"bio": "Biografía",
"role": "Rol",
"admin": "Administrador",
"instructor": "Instructor",
"student": "Estudiante"
},
"errors": {
"required": "Campo requerido",
"invalidEmail": "Email inválido",
"minLength": "Longitud mínima: {{length}}",
"maxLength": "Longitud máxima: {{length}}",
"invalidFormat": "Formato inválido",
"alreadyExists": "Ya existe",
"notFound": "No encontrado",
"unauthorized": "No autorizado",
"serverError": "Error del servidor",
"networkError": "Error de red",
"uploadFailed": "Subida fallida",
"generationFailed": "Generación fallida"
},
"validation": {
"required": "Este campo es requerido",
"email": "Debe ser un email válido",
"url": "Debe ser una URL válida",
"number": "Debe ser un número",
"min": "Debe ser mayor o igual a {{min}}",
"max": "Debe ser menor o igual a {{max}}",
"pattern": "Formato inválido"
},
"dates": {
"today": "Hoy",
"yesterday": "Ayer",
"tomorrow": "Mañana",
"lastWeek": "Semana Pasada",
"lastMonth": "Mes Pasado",
"lastYear": "Año Pasado",
"custom": "Personalizado",
"startDate": "Fecha de Inicio",
"endDate": "Fecha de Fin",
"dueDate": "Fecha Límite",
"publishedDate": "Fecha de Publicación",
"createdDate": "Fecha de Creación",
"updatedDate": "Fecha de Actualización"
},
"accessibility": {
"skipToContent": "Saltar al contenido",
"increaseContrast": "Aumentar Contraste",
"screenReader": "Lector de Pantalla"
}
}
}