feat: Translate various strings and comments to Spanish for better localization

- Updated error messages and comments in main.rs, openapi.rs, portfolio.rs, predictive.rs, ai.rs, health.rs, middleware.rs, models.rs, token_limits.rs, and webhooks.rs to Spanish.
- Enhanced user experience by providing localized content for Spanish-speaking users.
This commit is contained in:
2026-04-10 10:26:26 -04:00
parent 7c48b3b1a9
commit 53e5ef4d0b
35 changed files with 1135 additions and 1144 deletions
+33 -33
View File
@@ -36,32 +36,32 @@ async fn main() {
dotenv().ok();
tracing_subscriber::fmt::init();
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL debe estar configurada");
let pool = PgPoolOptions::new()
.max_connections(10)
.min_connections(2)
.acquire_timeout(Duration::from_secs(30))
.connect(&db_url)
.await
.expect("Failed to connect to database");
.expect("Error al conectar con la base de datos");
// Initialize health state
// Inicializar el estado de salud
let health_state = HealthState::default();
sqlx::migrate!("./migrations")
.run(&pool)
.await
.expect("Failed to run migrations");
.expect("Error al ejecutar las migraciones");
// Sync default organization branding from environment
// Sincronizar la marca de la organización por defecto desde el entorno
sync_default_organization(&pool).await;
// Start AI Background Worker
// Iniciar el trabajador de IA en segundo plano
let worker_pool = pool.clone();
tokio::spawn(async move {
tracing::info!("AI Background Worker started");
tracing::info!("Trabajador de IA en segundo plano iniciado");
loop {
// Check for queued transcriptions
// Buscar transcripciones en cola
let queued_lessons: Vec<sqlx::types::Uuid> = match sqlx::query_scalar(
"SELECT id FROM lessons WHERE transcription_status = 'queued' LIMIT 5",
)
@@ -70,18 +70,18 @@ async fn main() {
{
Ok(ids) => ids,
Err(e) => {
tracing::error!("Failed to fetch queued lessons: {}", e);
tracing::error!("Error al obtener lecciones en cola: {}", e);
tokio::time::sleep(Duration::from_secs(10)).await;
continue;
}
};
for lesson_id in queued_lessons {
tracing::info!("Processing transcription for lesson: {}", lesson_id);
tracing::info!("Procesando transcripción para la lección: {}", lesson_id);
if let Err(e) =
handlers::run_transcription_task(worker_pool.clone(), lesson_id).await
{
tracing::error!("Transcription task failed for lesson {}: {}", lesson_id, e);
tracing::error!("La tarea de transcripción falló para la lección {}: {}", lesson_id, e);
let _ = sqlx::query(
"UPDATE lessons SET transcription_status = 'failed' WHERE id = $1",
)
@@ -97,15 +97,15 @@ async fn main() {
}
});
// CORS configuration - Allow multiple origins for development and production
// Using a predicate closure to support wildcard subdomains for norteamericano.cl
// Configuración de CORS - Permitir múltiples orígenes para desarrollo y producción
// Uso de un cierre de predicado para soportar subdominios comodín para norteamericano.cl
use tower_http::cors::AllowOrigin;
let cors = CorsLayer::new()
.allow_origin(AllowOrigin::predicate(|origin: &http::HeaderValue, _request: &http::request::Parts| -> bool {
let origin_str = origin.to_str().unwrap_or("");
// Development origins
// Orígenes de desarrollo
let allowed_origins = [
"http://localhost:3000",
"http://localhost:3003",
@@ -114,7 +114,7 @@ async fn main() {
"http://192.168.0.254:3000",
"http://192.168.0.254:3003",
"http://192.168.0.254",
// Production - Norteamericano domains (.cl and .com)
// Producción - Dominios de Norteamericano (.cl y .com)
"http://studio.norteamericano.com",
"https://studio.norteamericano.com",
"http://learning.norteamericano.com",
@@ -125,12 +125,12 @@ async fn main() {
"https://learning.norteamericano.cl",
];
// Check exact matches
// Comprobar coincidencias exactas
if allowed_origins.contains(&origin_str) {
return true;
}
// Check wildcard for subdomains in norteamericano.cl/.com over HTTP(S)
// Comprobar comodín para subdominios en norteamericano.cl/.com sobre HTTP(S)
for scheme in ["http://", "https://"] {
for domain in [".norteamericano.cl", ".norteamericano.com"] {
if origin_str.starts_with(scheme) && origin_str.ends_with(domain) {
@@ -140,7 +140,7 @@ async fn main() {
.strip_suffix(domain)
.unwrap_or("");
// Allow any subdomain (e.g., api., cdn., admin., etc.)
// Permitir cualquier subdominio (ej., api., cdn., admin., etc.)
if !subdomain.is_empty() && !subdomain.contains('/') {
return true;
}
@@ -296,7 +296,7 @@ async fn main() {
"/organization/branding",
axum::routing::put(handlers_branding::update_organization_branding),
)
// Content Libraries routes
// Rutas de librerías de contenido
.route(
"/library/blocks",
get(handlers_library::list_library_blocks).post(handlers_library::create_library_block),
@@ -311,7 +311,7 @@ async fn main() {
"/library/blocks/{id}/increment-usage",
post(handlers_library::increment_block_usage),
)
// Advanced Grading (Rubrics) routes
// Rutas de calificación avanzada (rúbricas)
.route(
"/courses/{id}/rubrics",
get(handlers_rubrics::list_course_rubrics).post(handlers_rubrics::create_rubric),
@@ -347,7 +347,7 @@ async fn main() {
"/lessons/{id}/rubrics",
get(handlers_rubrics::get_lesson_rubrics),
)
// Learning Sequences (Dependencies) routes
// Rutas de secuencias de aprendizaje (dependencias)
.route(
"/lessons/{id}/dependencies",
get(handlers_dependencies::list_lesson_dependencies)
@@ -357,7 +357,7 @@ async fn main() {
"/lessons/{id}/dependencies/{prerequisite_id}",
delete(handlers_dependencies::remove_dependency),
)
// Test Templates routes
// Rutas de plantillas de pruebas
.route(
"/test-templates",
get(handlers_test_templates::list_test_templates)
@@ -393,7 +393,7 @@ async fn main() {
"/test-templates/generate-with-rag",
post(handlers_test_templates::generate_questions_with_rag),
)
// Question Bank routes
// Rutas del banco de preguntas
.route(
"/question-bank",
get(handlers_question_bank::list_questions)
@@ -433,7 +433,7 @@ async fn main() {
"/question-bank/ai-generate",
post(handlers_question_bank::ai_generate_question),
)
// Embedding routes for semantic search
// Rutas de embeddings para búsqueda semántica
.route(
"/question-bank/embeddings/generate",
post(handlers_embeddings::generate_question_embeddings),
@@ -450,7 +450,7 @@ async fn main() {
"/question-bank/{id}/embedding/regenerate",
post(handlers_embeddings::regenerate_question_embedding),
)
// SAM Integration routes
// Rutas de integración con SAM
.route(
"/sam/sync-all",
post(handlers_sam::sync_all_sam),
@@ -471,7 +471,7 @@ async fn main() {
"/sam/students/{student_id}/courses",
get(handlers_sam::get_sam_student_courses),
)
// Admin routes
// Rutas de administración
.route(
"/admin/token-usage",
get(handlers_admin::get_token_usage),
@@ -527,12 +527,12 @@ async fn main() {
"/api/assets/s3-proxy/{bucket}/{*key}",
get(handlers_assets::public_s3_proxy),
)
// Health check routes
// Rutas de verificación de salud
.merge(health::health_routes(pool.clone()).with_state(health_state))
.nest_service("/assets", tower_http::services::ServeDir::new("uploads"))
.merge(auth_routes)
.merge(protected_routes)
// Security headers
// Cabeceras de seguridad
.layer(SetResponseHeaderLayer::overriding(
http::header::STRICT_TRANSPORT_SECURITY,
http::HeaderValue::from_static("max-age=31536000; includeSubDomains"),
@@ -553,14 +553,14 @@ async fn main() {
http::header::REFERRER_POLICY,
http::HeaderValue::from_static("strict-origin-when-cross-origin"),
))
// CORS layer - MUST be last to execute first on response
// Capa CORS - DEBE ser la última para ejecutarse primero en la respuesta
.layer(cors)
// Trace layer for logging requests/responses
// Capa de trazado para registrar solicitudes/respuestas
.layer(TraceLayer::new_for_http())
.with_state(pool);
let addr = SocketAddr::from(([0, 0, 0, 0], 3001));
tracing::info!("CMS Service listening on {} with rate limiting and security headers", addr);
tracing::info!("Servicio CMS escuchando en {} con limitación de tasa y cabeceras de seguridad", addr);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, public_routes).await.unwrap();
}
@@ -597,8 +597,8 @@ async fn sync_default_organization(pool: &sqlx::PgPool) {
.await;
match result {
Ok(_) => tracing::info!("Default organization branding synced from .env"),
Err(e) => tracing::error!("Failed to sync default organization branding: {}", e),
Ok(_) => tracing::info!("Marca de la organización por defecto sincronizada desde .env"),
Err(e) => tracing::error!("Error al sincronizar la marca de la organización por defecto: {}", e),
}
}