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>
10 KiB
Implementación de Internacionalización (i18n) y Responsividad
Resumen de la Implementación
Fecha: 20 de Marzo, 2026
Estado: ✅ Completado
🌍 Internacionalización (i18n)
Características Implementadas
-
Detección Automática de Idioma
- Detección del idioma del navegador al primer ingreso
- Soporte para múltiples idiomas del navegador (
navigator.languages) - Fallback a español si el idioma no está soportado
-
Idiomas Soportados
- 🇪🇸 Español (es)
- 🇬🇧 Inglés (en)
- 🇵🇹 Portugués (pt)
-
Selector Manual de Idioma
- Disponible en AppHeader (Experience) y Navbar (Studio)
- Persistencia en localStorage
- Cambio instantáneo sin recargar la página
-
Configuración de Idioma por Curso
- Modo Automático: Detecta el idioma del usuario
- Modo Fijo: Usa siempre el idioma configurado en el curso
- Ideal para cursos de idiomas (ej: curso de inglés siempre en inglés)
📱 Responsividad (Mobile-First)
Breakpoints Utilizados
Mobile: < 640px (default)
sm: ≥ 640px
md: ≥ 768px
lg: ≥ 1024px
xl: ≥ 1280px
2xl: ≥ 1536px
Componentes Responsivos
AppHeader (Experience)
Mobile (< 768px):
- Logo compacto
- Íconos de notificación y tema
- Botón de menú hamburguesa
- Sidebar deslizante con navegación completa
Desktop (≥ 768px):
- Logo completo
- Navegación horizontal visible
- Selector de idioma visible
- Perfil de usuario visible
Navbar (Studio)
Mobile (< 768px):
- Logo compacto
- Dropdowns colapsados
- Menú hamburguesa
- Sidebar deslizante
Desktop (≥ 768px):
- Logo completo
- Dropdowns visibles
- Información de usuario visible
🗂️ Archivos de Traducción
Experience (web/experience/src/lib/locales/)
Archivos:
es.json- Español (completo)en.json- Inglés (completo)pt.json- Portugués (completo)
Categorías:
common- Textos comunes (loading, error, save, cancel)nav- Navegación (catalog, profile, signOut)course- Cursos (modules, lessons, progress)lesson- Lecciones (summary, transcription, complete)auth- Autenticación (login, register, password)dashboard- Dashboard (welcome, stats)profile- Perfil (edit, avatar, settings)gamification- Gamificación (level, xp, badges)grading- Calificaciones (grade, score, feedback)quiz- Cuestionarios (start, submit, attempts)forum- Foros (discussions, threads, replies)payments- Pagos (purchase, payment method)accessibility- Accesibilidad (contrast, text size)language- Idiomas (select, course, interface)errors- Errores (notFound, unauthorized)dates- Fechas (today, yesterday, daysAgo)
Studio (web/studio/src/lib/locales/)
Archivos:
es.json- Español (completo - administración)en.json- Inglés (básico - pendiente completar)pt.json- Portugués (básico - pendiente completar)
Categorías Adicionales:
course- Gestión de cursos (create, edit, modules, lessons)content- Tipos de contenido (video, audio, quiz, code)ai- Funciones de IA (generate, model, tokens)grading- Sistema de calificación (categories, weights)students- Estudiantes (enrollments, progress)analytics- Analíticas (overview, retention)settings- Configuración (branding, integrations)user- Usuario (profile, preferences)validation- Validación de formularios
🗄️ Base de Datos
Migración: Course Language Configuration
Archivo: services/cms-service/migrations/20260320000002_add_course_language_config.sql
Campos Agregados a courses:
language_setting VARCHAR(20) DEFAULT 'auto'
- 'auto': Detectar idioma del usuario
- 'fixed': Usar idioma fijo
fixed_language VARCHAR(5) DEFAULT NULL
- 'es': Español
- 'en': Inglés
- 'pt': Portugués
- NULL: Cuando language_setting es 'auto'
Constraints:
chk_language_setting: language_setting IN ('auto', 'fixed')
chk_fixed_language: fixed_language IS NULL OR fixed_language IN ('es', 'en', 'pt')
Índice:
idx_courses_language: (language_setting, fixed_language)
🔌 Backend API
LMS Service (Port 3002)
Endpoint: Course Language Config
GET /courses/{id}/language-config
Authorization: Bearer {token}
Respuesta:
{
"language_setting": "auto",
"fixed_language": null
}
Ejemplos de Uso:
// Curso en modo automático (usa idioma del usuario)
{
"language_setting": "auto",
"fixed_language": null
}
// Curso de inglés (siempre en inglés)
{
"language_setting": "fixed",
"fixed_language": "en"
}
// Curso de español (siempre en español)
{
"language_setting": "fixed",
"fixed_language": "es"
}
⚙️ Contextos y Hooks
I18nContext (Experience y Studio)
Funciones:
interface I18nContextType {
language: string;
setLanguage: (lang: string) => void;
t: (path: string) => string;
detectBrowserLanguage: () => string;
}
Uso:
import { useTranslation } from '@/context/I18nContext';
function MyComponent() {
const { language, setLanguage, t } = useTranslation();
return (
<div>
<h1>{t('dashboard.welcome')}</h1>
<select
value={language}
onChange={(e) => setLanguage(e.target.value)}
>
<option value="es">ES</option>
<option value="en">EN</option>
<option value="pt">PT</option>
</select>
</div>
);
}
useCourseLanguage Hook (Experience)
Hook para idioma por curso:
import { useCourseLanguage } from '@/hooks/useCourseLanguage';
function CoursePage({ courseId }) {
const {
courseLanguage,
isFixedLanguage,
isLoading
} = useCourseLanguage(courseId);
if (isLoading) return <Loading />;
return (
<div>
<p>Idioma del curso: {courseLanguage}</p>
{isFixedLanguage() && (
<p>Este curso usa idioma fijo</p>
)}
</div>
);
}
Funciones:
courseLanguage: Idioma actual del cursoisFixedLanguage(): Boolean - ¿el curso tiene idioma fijo?isLoading: Boolean - ¿cargando configuración?refreshConfig(): Recargar configuración
useCourseLanguageSwitcher Hook (Experience)
Hook para cambiar idioma (solo si es permitido):
import { useCourseLanguageSwitcher } from '@/hooks/useCourseLanguage';
function LanguageSelector({ courseId }) {
const {
currentLanguage,
canChangeLanguage,
changeLanguage,
isFixed
} = useCourseLanguageSwitcher(courseId);
return (
<select
value={currentLanguage}
onChange={(e) => changeLanguage(e.target.value)}
disabled={!canChangeLanguage}
>
<option value="es">Español</option>
<option value="en">English</option>
<option value="pt">Português</option>
</select>
);
}
📋 Guía de Responsividad
Archivo: RESPONSIVIDAD_GUIA.md
Principios Mobile-First
- Diseñar primero para móvil
- Mejorar progresivamente para pantallas más grandes
- Usar clases responsivas de Tailwind
Patrones Comunes
Grid de Cursos
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{courses.map(course => (
<CourseCard key={course.id} course={course} />
))}
</div>
Tablas Responsivas
<div className="overflow-x-auto">
<table className="min-w-full">
{/* Tabla completa */}
</table>
</div>
Tipografía Fluida
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold">
Título Responsivo
</h1>
Espaciado Responsivo
<div className="px-4 md:px-6 lg:px-8 py-4 md:py-6 lg:py-8">
{/* Contenido */}
</div>
🧪 Pruebas
Checklist de Responsividad
- Navegación funciona en mobile
- Menús desplegables accesibles
- Formularios usables en pantallas pequeñas
- Tablas con scroll horizontal
- Imágenes se escalan correctamente
- Texto legible sin zoom
- Botones táctiles (mínimo 44x44px)
- Sin overflow horizontal no intencional
Dispositivos de Prueba (Chrome DevTools)
- iPhone SE (375x667)
- iPhone 12 Pro (390x844)
- iPad Air (820x1180)
- iPad Pro (1024x1366)
- Desktop (1920x1080)
🚀 Comandos Útiles
Ver Logs de i18n
# Experience
docker logs openccb-experience-1 | grep -i "language\|i18n"
# Studio
docker logs openccb-studio-1 | grep -i "language\|i18n"
Probar Detección de Idioma
// Console del navegador
console.log(navigator.languages);
console.log(navigator.language);
localStorage.removeItem('experience_language');
location.reload();
Ver Configuración de Curso
# SQL
SELECT id, title, language_setting, fixed_language
FROM courses
WHERE id = '{course-id}';
📝 Tareas Futuras (Opcionales)
-
Completar traducciones de Studio
- Agregar claves faltantes en
en.jsonypt.json
- Agregar claves faltantes en
-
Agregar más idiomas
- Francés (fr)
- Alemán (de)
- Italiano (it)
-
Traducción de contenido de cursos
- Sistema de traducción de lecciones
- Contenido multi-idioma por lección
-
Mejoras de accesibilidad
- Aumentar contraste
- Texto de mayor tamaño
- Navegación por teclado
-
Optimización de rendimiento
- Lazy loading de traducciones
- Code splitting por idioma
📞 Referencias
Implementado por: Equipo de Desarrollo OpenCCB
Versión: 1.0
Última actualización: 2026-03-20