From a77aefa440ed7255602a87fee3518bc2604139f2 Mon Sep 17 00:00:00 2001 From: ialcarazsalazar Date: Sun, 15 Feb 2026 01:23:52 +0000 Subject: [PATCH] feat: add react-i18next bilingual support (ES/EN) Co-Authored-By: Claude Opus 4.6 --- frontend/Frontend-Hotel/package.json | 3 + .../src/context/LenguageContext.jsx | 14 +- frontend/Frontend-Hotel/src/i18n/index.js | 21 ++ .../Frontend-Hotel/src/i18n/locales/en.json | 226 ++++++++++++++++++ .../Frontend-Hotel/src/i18n/locales/es.json | 226 ++++++++++++++++++ frontend/Frontend-Hotel/src/main.jsx | 1 + 6 files changed, 483 insertions(+), 8 deletions(-) create mode 100644 frontend/Frontend-Hotel/src/i18n/index.js create mode 100644 frontend/Frontend-Hotel/src/i18n/locales/en.json create mode 100644 frontend/Frontend-Hotel/src/i18n/locales/es.json diff --git a/frontend/Frontend-Hotel/package.json b/frontend/Frontend-Hotel/package.json index 63fb9b7..7bc0353 100644 --- a/frontend/Frontend-Hotel/package.json +++ b/frontend/Frontend-Hotel/package.json @@ -15,8 +15,11 @@ "bootstrap": "^5.3.8", "multer": "^2.0.2", "prop-types": "^15.8.1", + "i18next": "^23.16.8", + "i18next-browser-languagedetector": "^8.0.4", "react": "^19.1.1", "react-dom": "^19.1.1", + "react-i18next": "^15.4.1", "react-hook-form": "^7.66.1", "react-icons": "^5.5.0", "react-router-dom": "^7.8.2", diff --git a/frontend/Frontend-Hotel/src/context/LenguageContext.jsx b/frontend/Frontend-Hotel/src/context/LenguageContext.jsx index dcb5cd0..e29995f 100644 --- a/frontend/Frontend-Hotel/src/context/LenguageContext.jsx +++ b/frontend/Frontend-Hotel/src/context/LenguageContext.jsx @@ -1,18 +1,16 @@ -import { createContext, useState, useEffect, useContext } from "react"; +import { createContext, useContext } from "react"; +import { useTranslation } from "react-i18next"; export const langContext = createContext(); export const LangProvider = ({ children }) => { - const [lang, setLang] = useState("en"); // Estado para el idioma + const { i18n } = useTranslation(); + const lang = i18n.language?.startsWith('es') ? 'es' : 'en'; - // Ahora 'event' es el objeto de evento de React const toggleLang = (event) => { - // Extraemos el valor de la opción seleccionada (ej: "es" o "en") const newLang = event.target.value; - console.log("Nuevo idioma seleccionado:", newLang); - // Establecemos el estado 'lang' con el valor seleccionado - setLang(newLang); - } + i18n.changeLanguage(newLang); + }; return ( diff --git a/frontend/Frontend-Hotel/src/i18n/index.js b/frontend/Frontend-Hotel/src/i18n/index.js new file mode 100644 index 0000000..1abe493 --- /dev/null +++ b/frontend/Frontend-Hotel/src/i18n/index.js @@ -0,0 +1,21 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import en from './locales/en.json'; +import es from './locales/es.json'; + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + resources: { + en: { translation: en }, + es: { translation: es }, + }, + fallbackLng: 'en', + interpolation: { + escapeValue: false, + }, + }); + +export default i18n; diff --git a/frontend/Frontend-Hotel/src/i18n/locales/en.json b/frontend/Frontend-Hotel/src/i18n/locales/en.json new file mode 100644 index 0000000..c817238 --- /dev/null +++ b/frontend/Frontend-Hotel/src/i18n/locales/en.json @@ -0,0 +1,226 @@ +{ + "nav": { + "dashboards": "Dashboards", + "roomDashboard": "Room Dashboard", + "reservations": "Reservations", + "guests": "Guests", + "housekeeping": "Housekeeping", + "roomService": "Room Service", + "eventsVenues": "Events & Venues", + "schedules": "Schedules", + "operationalReports": "Operational Reports", + "income": "Income", + "expenses": "Expenses", + "expensesApproval": "Expenses to be approved", + "inventory": "Inventory", + "payroll": "Payroll", + "hotel": "Hotel", + "housekeeper": "Housekeeper", + "services": "Services", + "operations": "Operations", + "staff": "Staff" + }, + "common": { + "save": "Save", + "cancel": "Cancel", + "delete": "Delete", + "edit": "Edit", + "create": "Create", + "search": "Search", + "filter": "Filter", + "loading": "Loading...", + "noResults": "No results found", + "confirm": "Confirm", + "back": "Back", + "actions": "Actions", + "status": "Status", + "date": "Date", + "total": "Total", + "notes": "Notes", + "name": "Name", + "phone": "Phone", + "email": "Email", + "description": "Description", + "price": "Price", + "quantity": "Quantity", + "category": "Category", + "type": "Type", + "priority": "Priority", + "room": "Room", + "all": "All" + }, + "rooms": { + "title": "Room Dashboard", + "available": "Available", + "occupied": "Occupied", + "cleaning": "Cleaning", + "maintenance": "Maintenance", + "occupancyRate": "Occupancy Rate", + "availableRooms": "Available Rooms", + "todayCheckIns": "Today's Check-ins", + "todayCheckOuts": "Today's Check-outs", + "dailyRevenue": "Daily Revenue", + "floor": "Floor", + "roomNumber": "Room Number", + "roomType": "Room Type", + "pricePerNight": "Price per Night", + "amenities": "Amenities", + "guestInfo": "Guest Information" + }, + "reservations": { + "title": "Reservations", + "newReservation": "New Reservation", + "checkIn": "Check-in", + "checkOut": "Check-out", + "guestName": "Guest Name", + "roomType": "Room Type", + "channel": "Channel", + "duration": "Duration", + "nights": "nights", + "adults": "Adults", + "children": "Children", + "totalAmount": "Total Amount", + "status": { + "pending": "Pending", + "confirmed": "Confirmed", + "checkedIn": "Checked In", + "checkedOut": "Checked Out", + "cancelled": "Cancelled" + }, + "channels": { + "direct": "Direct", + "booking": "Booking.com", + "expedia": "Expedia", + "airbnb": "Airbnb", + "other": "Other" + }, + "actions": { + "confirm": "Confirm", + "checkIn": "Check In", + "checkOut": "Check Out", + "cancel": "Cancel" + } + }, + "guests": { + "title": "Guests", + "newGuest": "New Guest", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "phone": "Phone", + "idType": "ID Type", + "idNumber": "ID Number", + "nationality": "Nationality", + "address": "Address", + "stayHistory": "Stay History", + "currentRoom": "Current Room", + "totalStays": "Total Stays", + "totalSpent": "Total Spent" + }, + "housekeeping": { + "title": "Housekeeping", + "pendingTasks": "Pending Tasks", + "inProgress": "In Progress", + "completedTasks": "Completed", + "assignTo": "Assign to", + "priority": { + "high": "High", + "normal": "Normal", + "low": "Low" + }, + "type": { + "checkout": "Checkout", + "maintenance": "Maintenance", + "deepClean": "Deep Clean", + "turndown": "Turndown" + }, + "startTask": "Start", + "completeTask": "Complete", + "staffAvailability": "Staff Availability" + }, + "roomService": { + "title": "Room Service", + "activeOrders": "Active Orders", + "orderHistory": "Order History", + "newOrder": "New Order", + "menuManagement": "Menu Management", + "status": { + "pending": "Pending", + "preparing": "Preparing", + "delivering": "Delivering", + "delivered": "Delivered", + "cancelled": "Cancelled" + }, + "menuItem": "Menu Item", + "price": "Price", + "quantity": "Quantity", + "category": "Category", + "addItem": "Add Item", + "orderTotal": "Order Total" + }, + "events": { + "title": "Events & Venues", + "venues": "Venues", + "upcomingEvents": "Upcoming Events", + "newEvent": "New Event", + "newVenue": "New Venue", + "venueName": "Venue Name", + "capacity": "Capacity", + "area": "Area (m\u00b2)", + "pricePerHour": "Price per Hour", + "eventName": "Event Name", + "organizer": "Organizer", + "guestCount": "Guest Count", + "eventDate": "Date", + "startTime": "Start Time", + "endTime": "End Time", + "venueStatus": { + "available": "Available", + "reserved": "Reserved" + } + }, + "schedules": { + "title": "Schedules", + "shifts": { + "morning": "Morning", + "afternoon": "Afternoon", + "night": "Night", + "off": "Off" + }, + "shiftTimes": { + "morning": "7:00 - 15:00", + "afternoon": "15:00 - 23:00", + "night": "23:00 - 7:00" + }, + "department": "Department", + "employee": "Employee", + "week": "Week", + "saveSchedule": "Save Schedule" + }, + "reports": { + "title": "Operational Reports", + "occupancyRate": "Occupancy Rate", + "revenue": "Revenue", + "guestSatisfaction": "Guest Satisfaction", + "bookingSources": "Booking Sources", + "period": { + "week": "Week", + "month": "Month", + "quarter": "Quarter", + "year": "Year" + }, + "vsTarget": "vs Target", + "trend": "Trend", + "revenueByRoomType": "Revenue by Room Type", + "bookingSourceDistribution": "Booking Source Distribution" + }, + "auth": { + "login": "Login", + "logout": "Logout", + "email": "Email", + "password": "Password", + "forgotPassword": "Forgot Password?", + "signIn": "Sign In", + "welcome": "Welcome" + } +} diff --git a/frontend/Frontend-Hotel/src/i18n/locales/es.json b/frontend/Frontend-Hotel/src/i18n/locales/es.json new file mode 100644 index 0000000..9a788df --- /dev/null +++ b/frontend/Frontend-Hotel/src/i18n/locales/es.json @@ -0,0 +1,226 @@ +{ + "nav": { + "dashboards": "Tableros", + "roomDashboard": "Panel de Habitaciones", + "reservations": "Reservaciones", + "guests": "Huespedes", + "housekeeping": "Limpieza", + "roomService": "Servicio a Habitacion", + "eventsVenues": "Eventos y Salones", + "schedules": "Horarios", + "operationalReports": "Reportes Operativos", + "income": "Ingresos", + "expenses": "Gastos", + "expensesApproval": "Gastos por aprobar", + "inventory": "Inventario", + "payroll": "Nomina", + "hotel": "Hotel", + "housekeeper": "Cuidador de Habitaciones", + "services": "Servicios", + "operations": "Operaciones", + "staff": "Personal" + }, + "common": { + "save": "Guardar", + "cancel": "Cancelar", + "delete": "Eliminar", + "edit": "Editar", + "create": "Crear", + "search": "Buscar", + "filter": "Filtrar", + "loading": "Cargando...", + "noResults": "Sin resultados", + "confirm": "Confirmar", + "back": "Regresar", + "actions": "Acciones", + "status": "Estado", + "date": "Fecha", + "total": "Total", + "notes": "Notas", + "name": "Nombre", + "phone": "Telefono", + "email": "Correo", + "description": "Descripcion", + "price": "Precio", + "quantity": "Cantidad", + "category": "Categoria", + "type": "Tipo", + "priority": "Prioridad", + "room": "Habitacion", + "all": "Todos" + }, + "rooms": { + "title": "Panel de Habitaciones", + "available": "Disponible", + "occupied": "Ocupada", + "cleaning": "Limpieza", + "maintenance": "Mantenimiento", + "occupancyRate": "Tasa de Ocupacion", + "availableRooms": "Habitaciones Disponibles", + "todayCheckIns": "Check-ins de Hoy", + "todayCheckOuts": "Check-outs de Hoy", + "dailyRevenue": "Ingreso Diario", + "floor": "Piso", + "roomNumber": "Numero de Habitacion", + "roomType": "Tipo de Habitacion", + "pricePerNight": "Precio por Noche", + "amenities": "Amenidades", + "guestInfo": "Informacion del Huesped" + }, + "reservations": { + "title": "Reservaciones", + "newReservation": "Nueva Reservacion", + "checkIn": "Entrada", + "checkOut": "Salida", + "guestName": "Nombre del Huesped", + "roomType": "Tipo de Habitacion", + "channel": "Canal", + "duration": "Duracion", + "nights": "noches", + "adults": "Adultos", + "children": "Ninos", + "totalAmount": "Monto Total", + "status": { + "pending": "Pendiente", + "confirmed": "Confirmada", + "checkedIn": "Registrado", + "checkedOut": "Check-out", + "cancelled": "Cancelada" + }, + "channels": { + "direct": "Directo", + "booking": "Booking.com", + "expedia": "Expedia", + "airbnb": "Airbnb", + "other": "Otro" + }, + "actions": { + "confirm": "Confirmar", + "checkIn": "Registrar Entrada", + "checkOut": "Registrar Salida", + "cancel": "Cancelar" + } + }, + "guests": { + "title": "Huespedes", + "newGuest": "Nuevo Huesped", + "firstName": "Nombre", + "lastName": "Apellido", + "email": "Correo", + "phone": "Telefono", + "idType": "Tipo de ID", + "idNumber": "Numero de ID", + "nationality": "Nacionalidad", + "address": "Direccion", + "stayHistory": "Historial de Estadias", + "currentRoom": "Habitacion Actual", + "totalStays": "Total de Estadias", + "totalSpent": "Total Gastado" + }, + "housekeeping": { + "title": "Limpieza", + "pendingTasks": "Tareas Pendientes", + "inProgress": "En Progreso", + "completedTasks": "Completadas", + "assignTo": "Asignar a", + "priority": { + "high": "Alta", + "normal": "Normal", + "low": "Baja" + }, + "type": { + "checkout": "Check-out", + "maintenance": "Mantenimiento", + "deepClean": "Limpieza Profunda", + "turndown": "Preparacion Nocturna" + }, + "startTask": "Iniciar", + "completeTask": "Completar", + "staffAvailability": "Disponibilidad del Personal" + }, + "roomService": { + "title": "Servicio a Habitacion", + "activeOrders": "Ordenes Activas", + "orderHistory": "Historial de Ordenes", + "newOrder": "Nueva Orden", + "menuManagement": "Gestion del Menu", + "status": { + "pending": "Pendiente", + "preparing": "Preparando", + "delivering": "Entregando", + "delivered": "Entregado", + "cancelled": "Cancelado" + }, + "menuItem": "Platillo", + "price": "Precio", + "quantity": "Cantidad", + "category": "Categoria", + "addItem": "Agregar Platillo", + "orderTotal": "Total de la Orden" + }, + "events": { + "title": "Eventos y Salones", + "venues": "Salones", + "upcomingEvents": "Proximos Eventos", + "newEvent": "Nuevo Evento", + "newVenue": "Nuevo Salon", + "venueName": "Nombre del Salon", + "capacity": "Capacidad", + "area": "Area (m\u00b2)", + "pricePerHour": "Precio por Hora", + "eventName": "Nombre del Evento", + "organizer": "Organizador", + "guestCount": "Numero de Invitados", + "eventDate": "Fecha", + "startTime": "Hora de Inicio", + "endTime": "Hora de Fin", + "venueStatus": { + "available": "Disponible", + "reserved": "Reservado" + } + }, + "schedules": { + "title": "Horarios", + "shifts": { + "morning": "Matutino", + "afternoon": "Vespertino", + "night": "Nocturno", + "off": "Descanso" + }, + "shiftTimes": { + "morning": "7:00 - 15:00", + "afternoon": "15:00 - 23:00", + "night": "23:00 - 7:00" + }, + "department": "Departamento", + "employee": "Empleado", + "week": "Semana", + "saveSchedule": "Guardar Horario" + }, + "reports": { + "title": "Reportes Operativos", + "occupancyRate": "Tasa de Ocupacion", + "revenue": "Ingresos", + "guestSatisfaction": "Satisfaccion del Huesped", + "bookingSources": "Fuentes de Reservacion", + "period": { + "week": "Semana", + "month": "Mes", + "quarter": "Trimestre", + "year": "Ano" + }, + "vsTarget": "vs Objetivo", + "trend": "Tendencia", + "revenueByRoomType": "Ingresos por Tipo de Habitacion", + "bookingSourceDistribution": "Distribucion de Fuentes de Reservacion" + }, + "auth": { + "login": "Iniciar Sesion", + "logout": "Cerrar Sesion", + "email": "Correo", + "password": "Contrasena", + "forgotPassword": "Olvido su contrasena?", + "signIn": "Ingresar", + "welcome": "Bienvenido" + } +} diff --git a/frontend/Frontend-Hotel/src/main.jsx b/frontend/Frontend-Hotel/src/main.jsx index 29e5407..2371292 100644 --- a/frontend/Frontend-Hotel/src/main.jsx +++ b/frontend/Frontend-Hotel/src/main.jsx @@ -1,3 +1,4 @@ +import './i18n'; import React from "react"; import ReactDOM from "react-dom/client"; import { BrowserRouter } from "react-router-dom";