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:
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user