diff --git a/public/locales/en/check.json b/public/locales/en/check.json new file mode 100644 index 0000000..f43b962 --- /dev/null +++ b/public/locales/en/check.json @@ -0,0 +1,30 @@ +{ + "back_to_home_page": "Back to Home Page", + "daily_checkin_checkout": "Daily Check-In/Check-Out", + "i_want_to": "I want to", + "check_in": "Check In", + "check_out": "Check Out", + "select_type": "Select type", + "individual": "Individual", + "group": "Group", + "your_email": "Your Email", + "continue": "Continue", + "back": "Back", + "shift_signup_info": "Here is your shift signup information:", + "shifts_choose_one": "Shift(s) (choose one)", + "start": "Start", + "end": "End", + "confirm_check_in": "Confirm Your Check-In", + "must_check_in_first": "You must check in before checking out again.", + "checkout_error": "There was an error during check-out.", + "group_already_checked_out": "This group has already checked out.", + "already_checked_in": "You have already checked in and must check out before checking in again.", + "checkin_error": "There was an error during check-in. Please try again.", + "check_success_message": "Hooray! You have checked {{action}} at {{time}}.", + "in": "in", + "out": "out", + "checkin_caption": "Do not forget to check out before you leave!", + "checkout_caption": "Thank you for your hard work today! We look forward to seeing you again soon.", + "back_to_first_page": "Back to First Page", + "organization_name": "Organization Name" +} diff --git a/public/locales/en/communication.json b/public/locales/en/communication.json new file mode 100644 index 0000000..6a3fffd --- /dev/null +++ b/public/locales/en/communication.json @@ -0,0 +1,15 @@ +{ + "send_email_to_volunteers": "Send Email To Volunteers", + "email_subject": "Subject", + "email_from": "Email From", + "default_email_note": "This is the default email.", + "email_body": "Email Body", + "email_reach_note": "This will be sent to all volunteers on the site.", + "email_example": "ex: Welcome to Bread & Roses!", + "click_to_upload": "Click to upload", + "type_email_content": "Type your email content", + "send_email": "Send Email", + "complete": "Complete", + "email_sent_confirmation": "Email sent! You will receive a copy via email.", + "back_to_original_page": "Back to the original page" +} diff --git a/public/locales/en/events.json b/public/locales/en/events.json new file mode 100644 index 0000000..6c86792 --- /dev/null +++ b/public/locales/en/events.json @@ -0,0 +1,45 @@ +{ + "events": "Events", + "sign_up_prompt": "Sign up for your volunteering time!", + "your_volunteer_hours": "Your Volunteer Hours", + "no_time_slots": "No time slots!", + "choose_your_time": "Choose Your Time", + "we_are_open_from": "We are open from", + "start_time": "Start Time", + "end_time": "End Time", + "add_time_slot": "Add This Time Slot", + "confirm": "Confirm", + "close": "Close", + "signup_success": "You have signed up! We look forward to seeing you!", + "time_slot_info_prompt": "Below is your time slot information. You can check it out on the homepage.", + "time": "Time", + "select_date": "Select Date", + "group_name": "Name of group", + "group_description": "Group description", + "group_signup_reason": "Reason(s) for group signup", + "capacity": "Capacity", + "send": "Send", + "group_signup_success": "Group sign up is successful! We hope to see you soon.", + "must_belong_to_org": "You must belong to an organization to create time slots.", + "create_time_slots_failed": "Failed to create time slots. Please try again.", + "time_slot_registrations": "Time Slot Registrations", + "opening_time": "Opening Time", + "total_individual_signups": "Total Individual Signups", + "volunteer": "Volunteer", + "volunteers": "Volunteers", + "individuals": "Individuals", + "groups": "Groups", + "page": "Page", + "previous": "Previous", + "next": "Next", + "no_individuals": "It seems like no individuals have signed up!", + "no_groups": "It seems like no groups have signed up!", + "add_event_title": "Add event title", + "start_date": "Start Date", + "event_description_optional": "Event description (if any)", + "enter_description": "Enter description...", + "add": "Add", + "event_saved_success": "Event saved successfully!", + "event_save_failed": "Failed to save event.", + "event_save_error": "An error occurred while saving." +} diff --git a/public/locales/en/home.json b/public/locales/en/home.json index 0dd83b3..0a1b75d 100644 --- a/public/locales/en/home.json +++ b/public/locales/en/home.json @@ -3,5 +3,24 @@ "welcome_subtitle": "What's the next event you want to join", "upcoming_times": "Your upcoming volunteer times", "volunteer_hours": "Personal volunteer hours", - "events_attended": "Events attended" + "events_attended": "Events attended", + "days_volunteered": "Days volunteered", + "no_upcoming_slots": "It seems like you have not signed up for any time slots yet!", + "opening_time": "Opening Time", + "total_volunteer_count": "Total volunteers", + "volunteers_list": "Volunteers List", + "upcoming_events": "Upcoming Events", + "manage": "Manage", + "stats_updated_by": "Stats updated by:", + "time_slot": "Time Slot", + "see_details": "See details", + "total_volunteer_hours": "Total volunteer hours", + "name": "Name", + "email_address": "Email Address", + "hours_volunteered": "Hours Volunteered", + "hour": "hour", + "hours": "hours", + "view": "View", + "times": "Time(s)", + "times_and_group_size": "Time(s) and Group Size" } diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json new file mode 100644 index 0000000..f6f143b --- /dev/null +++ b/public/locales/en/profile.json @@ -0,0 +1,41 @@ +{ + "volunteer_log": "Volunteer Log", + "personal_stats": "Personal Stats", + "volunteer_timesheet": "Volunteer Timesheet", + "personal_volunteer_hours": "Personal volunteer hours", + "days_volunteered": "Days volunteered", + "start_date": "Start Date", + "end_date": "End Date", + "date": "Date", + "total_hours_worked": "Total Hours Worked", + "volunteer_sessions": "Volunteer Session(s)", + "hours": "hours", + "page": "Page", + "previous": "Previous", + "next": "Next", + "personal_information": "Personal Information", + "edit": "Edit", + "organization": "Organization", + "over_14_question": "Are you over 14?", + "over_14_note": "Note: we require volunteers to be over 14 years old to work with us.", + "first_time_question": "Is this your first time volunteering with us?", + "address": "Address", + "drivers_license_question": "Do you have a driver's license?", + "speak_spanish_question": "Do you speak Spanish?", + "why_volunteer_question": "Why do you want to volunteer with us?", + "other_questions_prompt": "Do you have any other questions or comments?", + "yes": "Yes", + "no": "No", + "no_time_slots_in_range": "It looks like there are no time slots in this range!", + "edit_info": "Edit Info", + "cancel": "Cancel", + "save": "Save", + "name": "Name", + "group_log": "Group Log", + "group_stats": "Group Stats", + "group_timesheet": "Group Timesheet", + "people": "People", + "loading": "Loading", + "must_be_logged_in": "You must be logged in to edit your profile.", + "go_back": "Go back" +} diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 6afb31c..9d5c0ad 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -10,5 +10,11 @@ "profile": "Profile", "logout": "Logout", "see_all": "See all", - "ver_more": "See all" + "ver_more": "See all", + "customize_event": "Customize Event", + "sign_up_group": "Sign Up As a Group", + "admin": "Admin", + "communication": "Communication", + "organization": "Organization", + "check_in_out": "Check-in/out" } diff --git a/public/locales/en/volunteers.json b/public/locales/en/volunteers.json new file mode 100644 index 0000000..43677cf --- /dev/null +++ b/public/locales/en/volunteers.json @@ -0,0 +1,27 @@ +{ + "volunteer_list": "Volunteer List", + "organizations_list": "Organizations List", + "search": "Search", + "selected": "Selected", + "delete": "Delete", + "number_of_people": "Number of People", + "volunteer": "Volunteer", + "volunteer_lower": "volunteer", + "volunteers": "volunteers", + "organizations": "Organizations", + "list": "List", + "individuals": "Individuals", + "delete_confirmation_title": "Are you sure you want to delete {{count}} {{type}}?", + "delete_warning": "You will not be able to recover {{article}} deleted {{type}}.", + "user": "user", + "users": "users", + "organization": "organization", + "organizations_lower": "organizations", + "profile": "profile", + "profiles": "profiles", + "a": "a", + "cancel": "Cancel", + "no_individuals_found": "No individuals found!", + "no_organizations_found": "No organizations found!", + "loading": "Loading" +} diff --git a/public/locales/es/check.json b/public/locales/es/check.json new file mode 100644 index 0000000..3ff1ebc --- /dev/null +++ b/public/locales/es/check.json @@ -0,0 +1,30 @@ +{ + "back_to_home_page": "Volver a la página de inicio", + "daily_checkin_checkout": "Registro de entrada/salida diario", + "i_want_to": "Quiero", + "check_in": "Entrada", + "check_out": "Salida", + "select_type": "Seleccionar tipo", + "individual": "Individuo", + "group": "Grupo", + "your_email": "Tu correo electrónico", + "continue": "Continuar", + "back": "Volver", + "shift_signup_info": "Aquí está la información de tu turno:", + "shifts_choose_one": "Turno(s) (elige uno)", + "start": "Inicio", + "end": "Fin", + "confirm_check_in": "Confirma tu registro de entrada", + "must_check_in_first": "Debes registrar entrada antes de volver a registrar salida.", + "checkout_error": "Hubo un error durante el registro de salida.", + "group_already_checked_out": "Este grupo ya ha registrado salida.", + "already_checked_in": "Ya registraste entrada y debes registrar salida antes de volver a registrarte.", + "checkin_error": "Hubo un error durante el registro de entrada. Por favor, inténtalo de nuevo.", + "check_success_message": "¡Bien hecho! Has registrado {{action}} a las {{time}}.", + "in": "entrada", + "out": "salida", + "checkin_caption": "¡No olvides registrar salida antes de irte!", + "checkout_caption": "Gracias por tu gran trabajo hoy. ¡Esperamos verte de nuevo pronto!", + "back_to_first_page": "Volver a la primera página", + "organization_name": "Nombre de la organización" +} diff --git a/public/locales/es/communication.json b/public/locales/es/communication.json new file mode 100644 index 0000000..ad05870 --- /dev/null +++ b/public/locales/es/communication.json @@ -0,0 +1,15 @@ +{ + "send_email_to_volunteers": "Enviar correo a los voluntarios", + "email_subject": "Asunto", + "email_from": "Correo del remitente", + "default_email_note": "Este es el correo predeterminado.", + "email_body": "Cuerpo del correo", + "email_reach_note": "Este mensaje se enviará a todos los voluntarios en el sitio.", + "email_example": "ej: ¡Bienvenido a Bread & Roses!", + "click_to_upload": "Haz clic para subir", + "type_email_content": "Escribe el contenido del correo", + "send_email": "Enviar correo", + "complete": "Completado", + "email_sent_confirmation": "¡Correo enviado! Recibirás una copia por correo electrónico.", + "back_to_original_page": "Volver a la página original" +} diff --git a/public/locales/es/events.json b/public/locales/es/events.json new file mode 100644 index 0000000..29a5874 --- /dev/null +++ b/public/locales/es/events.json @@ -0,0 +1,45 @@ +{ + "events": "Eventos", + "sign_up_prompt": "¡Regístrate para tu turno de voluntariado!", + "your_volunteer_hours": "Tus horas de voluntariado", + "no_time_slots": "¡No hay turnos!", + "choose_your_time": "Elige tu horario", + "we_are_open_from": "Estamos abiertos desde", + "start_time": "Hora de inicio", + "end_time": "Hora de fin", + "add_time_slot": "Agregar este turno", + "confirm": "Confirmar", + "close": "Cerrar", + "signup_success": "¡Te has registrado! ¡Esperamos verte pronto!", + "time_slot_info_prompt": "A continuación está la información de tu turno. Puedes verla en la página principal.", + "time": "Hora", + "select_date": "Seleccionar fecha", + "group_name": "Nombre del grupo", + "group_description": "Descripción del grupo", + "group_signup_reason": "Razón(es) para el registro del grupo", + "capacity": "Capacidad", + "send": "Enviar", + "group_signup_success": "¡El registro del grupo fue exitoso! Esperamos verte pronto.", + "must_belong_to_org": "Debes pertenecer a una organización para crear turnos.", + "create_time_slots_failed": "No se pudieron crear los turnos. Por favor, inténtalo de nuevo.", + "time_slot_registrations": "Registros de turnos", + "opening_time": "Hora de apertura", + "total_individual_signups": "Total de inscripciones individuales", + "volunteer": "voluntario", + "volunteers": "voluntarios", + "individuals": "Individuos", + "groups": "Grupos", + "page": "Página", + "previous": "Anterior", + "next": "Siguiente", + "no_individuals": "¡Parece que ningún individuo se ha registrado!", + "no_groups": "¡Parece que ningún grupo se ha registrado!", + "add_event_title": "Agregar título del evento", + "start_date": "Fecha de inicio", + "event_description_optional": "Descripción del evento (opcional)", + "enter_description": "Ingresa una descripción...", + "add": "Agregar", + "event_saved_success": "¡Evento guardado con éxito!", + "event_save_failed": "No se pudo guardar el evento.", + "event_save_error": "Ocurrió un error al guardar." +} diff --git a/public/locales/es/home.json b/public/locales/es/home.json index 9deca7a..0df67af 100644 --- a/public/locales/es/home.json +++ b/public/locales/es/home.json @@ -3,5 +3,24 @@ "welcome_subtitle": "¿Cuál es el próximo evento al que quieres unirte?", "upcoming_times": "Tus próximos tiempos de voluntariado", "volunteer_hours": "Horas de voluntariado", - "events_attended": "Eventos asistidos" + "events_attended": "Eventos asistidos", + "days_volunteered": "Días como voluntario", + "no_upcoming_slots": "¡Parece que aún no te has registrado en ningún turno!", + "opening_time": "Hora de apertura", + "total_volunteer_count": "Total de voluntarios", + "volunteers_list": "Lista de voluntarios", + "upcoming_events": "Próximos eventos", + "manage": "Administrar", + "stats_updated_by": "Estadísticas actualizadas el:", + "time_slot": "Turno", + "see_details": "Ver detalles", + "total_volunteer_hours": "Total de horas voluntarias", + "name": "Nombre", + "email_address": "Correo electrónico", + "hours_volunteered": "Horas como voluntario", + "hour": "hora", + "hours": "horas", + "view": "Ver", + "times": "Hora(s)", + "times_and_group_size": "Hora(s) y tamaño del grupo" } diff --git a/public/locales/es/profile.json b/public/locales/es/profile.json new file mode 100644 index 0000000..c9aa378 --- /dev/null +++ b/public/locales/es/profile.json @@ -0,0 +1,41 @@ +{ + "volunteer_log": "Registro de voluntariado", + "personal_stats": "Estadísticas personales", + "volunteer_timesheet": "Hoja de tiempos de voluntariado", + "personal_volunteer_hours": "Horas de voluntariado personales", + "days_volunteered": "Días como voluntario", + "start_date": "Fecha de inicio", + "end_date": "Fecha de fin", + "date": "Fecha", + "total_hours_worked": "Total de horas trabajadas", + "volunteer_sessions": "Sesión(es) de voluntariado", + "hours": "horas", + "page": "Página", + "previous": "Anterior", + "next": "Siguiente", + "personal_information": "Información personal", + "edit": "Editar", + "organization": "Organización", + "over_14_question": "¿Tienes más de 14 años?", + "over_14_note": "Nota: requerimos que los voluntarios tengan más de 14 años para trabajar con nosotros.", + "first_time_question": "¿Es tu primera vez haciendo voluntariado con nosotros?", + "address": "Dirección", + "drivers_license_question": "¿Tienes licencia de conducir?", + "speak_spanish_question": "¿Hablas español?", + "why_volunteer_question": "¿Por qué quieres ser voluntario con nosotros?", + "other_questions_prompt": "¿Tienes alguna otra pregunta o comentario?", + "yes": "Sí", + "no": "No", + "no_time_slots_in_range": "¡Parece que no hay turnos en este rango de fechas!", + "edit_info": "Editar información", + "cancel": "Cancelar", + "save": "Guardar", + "name": "Nombre", + "group_log": "Registro del grupo", + "group_stats": "Estadísticas del grupo", + "group_timesheet": "Hoja de horas del grupo", + "people": "Personas", + "loading": "Cargando", + "must_be_logged_in": "Debes iniciar sesión para editar tu perfil.", + "go_back": "Volver" +} diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 0ca4510..ba31655 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -10,5 +10,11 @@ "profile": "Perfil", "logout": "Cerrar sesión", "see_all": "Ver más", - "ver_more": "Ver más" + "ver_more": "Ver más", + "customize_event": "Personalizar evento", + "sign_up_group": "Registrarse como grupo", + "admin": "Administrador", + "communication": "Comunicación", + "organization": "Organización", + "check_in_out": "Entrada/Salida" } diff --git a/public/locales/es/volunteers.json b/public/locales/es/volunteers.json new file mode 100644 index 0000000..a2856d1 --- /dev/null +++ b/public/locales/es/volunteers.json @@ -0,0 +1,28 @@ +{ + "volunteer_list": "Lista de voluntarios", + "organizations_list": "Lista de organizaciones", + "search": "Buscar", + "selected": "Seleccionado(s)", + "delete": "Eliminar", + "number_of_people": "Número de personas", + "volunteer": "Voluntario", + "volunteer_lower": "voluntario", + "volunteers": "voluntarios", + "organizations": "Organizaciones", + "list": "Lista", + "individuals": "Individuos", + "delete_confirmation_title": "¿Estás seguro de que deseas eliminar {{count}} {{type}}?", + "delete_warning": "No podrás recuperar {{article}} {{type}} eliminado{{pluralSuffix}}.", + "user": "usuario", + "users": "usuarios", + "organization": "organización", + "organizations_lower": "organizaciones", + "profile": "perfil", + "profiles": "perfiles", + "a": "un", + "cancel": "Cancelar", + "pluralSuffix": "s", + "no_individuals_found": "¡No se encontraron individuos!", + "no_organizations_found": "¡No se encontraron organizaciones!", + "loading": "Cargando" +} diff --git a/src/app/private/communication/page.tsx b/src/app/private/communication/page.tsx index 25c4418..60d2569 100644 --- a/src/app/private/communication/page.tsx +++ b/src/app/private/communication/page.tsx @@ -9,6 +9,7 @@ import FileUploadRoundedIcon from "@mui/icons-material/FileUploadRounded"; import UploadFileIcon from "@mui/icons-material/UploadFile"; import DeleteIcon from "@mui/icons-material/Delete"; import { Attachment } from "nodemailer/lib/mailer"; +import { useTranslation } from "react-i18next"; export default function CommunicationPage() { const [step, setStep] = useState(1); @@ -17,6 +18,7 @@ export default function CommunicationPage() { code: "SUCCESS" | "ERROR"; message: string; } + const { t } = useTranslation("communication"); const [subject, setSubject] = React.useState(""); const [fromEmail] = React.useState("breadandrosesjc@gmail.com"); @@ -104,18 +106,18 @@ export default function CommunicationPage() {
- Send Email To Volunteers + {t("send_email_to_volunteers")}
{step === 1 && ( <>
-

Subject

+

{t("email_subject")}

setSubject(e.target.value)} @@ -125,8 +127,8 @@ export default function CommunicationPage() {
-

Email From

-

This is the default email.

+

{t("email_from")}

+

{t("default_email_note")}

-

Email Body

-

- This will be sent to all volunteers on the site. -

+

{t("email_body")}

+

{t("email_reach_note")}

{formatFileSize(src.size)}
-
Complete
+
{t("complete")}
setText(e.target.value)} rows={10} @@ -222,7 +221,7 @@ export default function CommunicationPage() { } }} > - Send Email + {t("send_email")}
@@ -241,14 +240,14 @@ export default function CommunicationPage() { quality={100} />
- Email sent! You will receive a copy via email. + {t("email_sent_confirmation")}
diff --git a/src/app/private/events/page.tsx b/src/app/private/events/page.tsx index 4139405..9da8f2c 100644 --- a/src/app/private/events/page.tsx +++ b/src/app/private/events/page.tsx @@ -19,9 +19,11 @@ import { useSearchParams } from "next/navigation"; import { getStandardDate } from "../../utils"; import { getCustomDay } from "@api/customDay/route.client"; import useApiThrottle from "../../../hooks/useApiThrottle"; +import { useTranslation } from "react-i18next"; export default function EventsPage() { const { data: session } = useSession(); + const { t } = useTranslation(["translation", "events"]); const searchParams = useSearchParams(); const date = searchParams.get("date"); @@ -314,7 +316,7 @@ export default function EventsPage() { if (pageLoading || !session) { return (
- Loading... + {t("Loading", { ns: "translation" })}...
); } @@ -323,7 +325,7 @@ export default function EventsPage() {
- Events + {t("events", { ns: "events" })}
@@ -356,8 +358,8 @@ export default function EventsPage() {
{customDayTitle === "" ? !isPastOrToday(selectedDate) - ? `Sign up for your volunteering time!` - : "Your Volunteer Hours" + ? t("sign_up_prompt", { ns: "events" }) + : t("your_volunteer_hours", { ns: "events" }) : customDayTitle}
{customDayDescription !== "" && ( @@ -376,7 +378,12 @@ export default function EventsPage() {
{formattedDate ? !isPastOrToday(selectedDate) - ? `Choose Your Time (${formattedDate}). We are open from${" "} + ? `${t("choose_your_time", { + ns: "events", + })} (${formattedDate}). ${t( + "we_are_open_from", + { ns: "events" } + )}${" "} ${formatTime(customDayHours.start)} -${" "} ${formatTime(customDayHours.end)}.` : `${formattedDate} (${formatTime( @@ -392,7 +399,7 @@ export default function EventsPage() {
{isPastOrToday(selectedDate) && timeSlots.length === 1 ? ( -
No time slots!
+
{t("no_time_slots", { ns: "events" })}
) : ( timeSlots.map((slot, index) => (
@@ -434,7 +441,9 @@ export default function EventsPage() { type="time" variant="outlined" size="small" - label="Start Time" + label={t("start_time", { + ns: "events", + })} value={slot.start} onChange={(e) => { const newSlots = [...timeSlots]; @@ -466,7 +475,7 @@ export default function EventsPage() { type="time" variant="outlined" size="small" - label="End Time" + label={t("end_time", { ns: "events" })} value={slot.end} onChange={(e) => { const newSlots = [...timeSlots]; @@ -519,7 +528,9 @@ export default function EventsPage() { width="20" height="20" /> -
Add This Time Slot
+
+ {t("add_time_slot", { ns: "events" })} +
) : null} @@ -534,7 +545,7 @@ export default function EventsPage() { ) : (
- You have signed up! We look forward to seeing you! + {t("signup_success", { ns: "events" })}
- Below is your time slot information. You can check it out on - the homepage. + {t("time_slot_info_prompt", { ns: "events" })}
- {page === 0 ? "Confirm" : "Close"} + {page === 0 + ? t("confirm", { ns: "events" }) + : t("close", { ns: "events" })} ) : null}
@@ -624,7 +636,7 @@ export default function EventsPage() {
{customDayTitle === "" - ? "Time Slot Registrations" + ? t("time_slot_registrations", { ns: "events" }) : customDayTitle}
{customDayDescription !== "" && ( @@ -641,7 +653,8 @@ export default function EventsPage() { height="20" />
- Opening Time: {formatTime(customDayHours.start)} -{" "} + {t("opening_time", { ns: "events" })}:{" "} + {formatTime(customDayHours.start)} -{" "} {formatTime(customDayHours.end)}
@@ -653,10 +666,15 @@ export default function EventsPage() { height="20" />
- Total Individual Signups: {individuals.length}{" "} - {individuals.length === 1 ? "volunteer" : "volunteers"} /{" "} - Capacity: {customDayCapacity}{" "} - {customDayCapacity === 1 ? "volunteer" : "volunteers"} + {t("total_individual_signups", { ns: "events" })}:{" "} + {individuals.length}{" "} + {individuals.length === 1 + ? t("volunteer", { ns: "events" }) + : t("volunteers", { ns: "events" })}{" "} + / {t("capacity", { ns: "events" })}: {customDayCapacity}{" "} + {customDayCapacity === 1 + ? t("volunteer", { ns: "events" }) + : t("volunteers", { ns: "events" })}
@@ -683,7 +701,7 @@ export default function EventsPage() { width="20" height="20" /> -
{tab}
+
{t(tab.toLowerCase(), { ns: "events" })}
))}
@@ -699,7 +717,7 @@ export default function EventsPage() { />
- It seems like no individuals have signed up! + {t("no_individuals", { ns: "events" })}
) : ( @@ -721,7 +739,7 @@ export default function EventsPage() { />
- It seems like no groups have signed up! + {t("no_groups", { ns: "events" })}
) : ( diff --git a/src/app/private/organization/[organizationId]/page.tsx b/src/app/private/organization/[organizationId]/page.tsx index 0242bf8..fa7ffb5 100644 --- a/src/app/private/organization/[organizationId]/page.tsx +++ b/src/app/private/organization/[organizationId]/page.tsx @@ -14,11 +14,13 @@ import { OrganizationWithUsers } from "../../../types"; import { useSession } from "next-auth/react"; import { getOrganization } from "@api/organization/route.client"; import VolunteerTable from "@components/VolunteerTable"; +import { useTranslation } from "react-i18next"; export default function ProfileContent() { const { organizationId } = useParams(); const { data: session, status } = useSession(); const router = useRouter(); + const { t } = useTranslation("profile"); const startButtonRef = React.useRef(null); const startCalendarRef = React.useRef(null); @@ -128,7 +130,7 @@ export default function ProfileContent() { if (loading || !organization) { return (
- Loading... + {t("loading")}...
); } @@ -152,7 +154,9 @@ export default function ProfileContent() {
{organization.name}
-
Organization
+
+ {t("organization")} +
@@ -161,7 +165,7 @@ export default function ProfileContent() {
- Group Log + {t("group_log")}
@@ -170,7 +174,7 @@ export default function ProfileContent() { className="border border-gray-300 rounded-md px-3 py-2 w-[215.5px] focus:outline-none focus:ring-1 focus:ring-blue-500" placeholder="MM/DD/YYYY" variant="outlined" - label="Start Date" + label={t("start_date")} autoComplete="off" size="small" onFocus={() => setShowStartCalendar(!showStartCalendar)} @@ -229,7 +233,7 @@ export default function ProfileContent() { className="border border-gray-300 rounded-md px-3 py-2 w-[215.5px] focus:outline-none focus:ring-1 focus:ring-blue-500" placeholder="MM/DD/YYYY" variant="outlined" - label="End Date" + label={t("end_date")} autoComplete="off" size="small" onFocus={() => setShowEndCalendar(!showEndCalendar)} @@ -282,22 +286,22 @@ export default function ProfileContent() {
-
Group Stats
+
{t("group_stats")}
-
Group Timesheet
+
{t("group_timesheet")}
{filteredSessions.length === 0 ? (
@@ -310,7 +314,7 @@ export default function ProfileContent() { />
- It looks like there are no time slots in this range! + {t("no_time_slots_in_range")}
) : ( @@ -321,7 +325,9 @@ export default function ProfileContent() {
-
People
+
+ {t("people")} +
{organization.users?.length === 0 ? ( diff --git a/src/app/private/page.tsx b/src/app/private/page.tsx index a5b432a..afd98a2 100644 --- a/src/app/private/page.tsx +++ b/src/app/private/page.tsx @@ -157,7 +157,7 @@ export default function HomePage() { if (!session || pageLoading) { return (
- Loading... + {t("Loading")}...
); } @@ -192,7 +192,9 @@ export default function HomePage() { className="flex justify-end flex-row gap-x-2 bg-teal-600 px-3.5 py-1 text-white rounded-lg place-items-center text-[14px] font-semibold leading-[20px]" onClick={action} > - {session.user.role === Role.VOLUNTEER ? t("Manage") : t("See details")} + {session.user.role === Role.VOLUNTEER + ? t("manage", { ns: "home" }) + : t("see_details", { ns: "home" })} ); @@ -203,7 +205,7 @@ export default function HomePage() { {t("welcome_title", { ns: "home" })}, {session.user.firstName} 👋

- Stats updated by:{" "} + {t("stats_updated_by", { ns: "home" })}{" "} {(() => { const date = new Date().toLocaleDateString("en-GB", { weekday: "long", @@ -222,7 +224,7 @@ export default function HomePage() {
{session.user.role === Role.ADMIN && ( @@ -230,7 +232,7 @@ export default function HomePage() { {session.user.role === Role.VOLUNTEER && ( @@ -249,7 +251,7 @@ export default function HomePage() {

{session.user.role === Role.ADMIN - ? "Upcoming Events" + ? t("upcoming_events", { ns: "home" }) : t("upcoming_times", { ns: "home" })}

- It seems like you have not signed up for any time slots yet! + {t("no_upcoming_slots", { ns: "home" })}
) : ( @@ -288,7 +290,7 @@ export default function HomePage() { {timeSlots.slice(0, 6).map((timeSlot, index) => (

- Volunteers List + {t("volunteers_list", { ns: "home" })}

(null); const [volunteerDetails, setVolunteerDetails] = @@ -171,7 +173,7 @@ export default function EditProfilePage() { if (status === "loading" || loading) { return (
- Loading... + {t("loading", { ns: "profile" })}...
); } @@ -179,7 +181,7 @@ export default function EditProfilePage() { if (!session) { return (
- You must be logged in to edit your profile. + {t("must_be_logged_in")}
); } @@ -192,7 +194,7 @@ export default function EditProfilePage() { onClick={() => router.push(`/private/profile/${userId}`)} className="px-4 py-2 bg-gray-200 rounded" > - Go back + {t("go_back")}
); @@ -201,7 +203,7 @@ export default function EditProfilePage() { if (!user) { return (
- Loading... + {t("loading", { ns: "profile" })}...
); } @@ -209,14 +211,16 @@ export default function EditProfilePage() { return (
-

Edit Info

+

+ {t("edit_info", { ns: "profile" })} +

@@ -232,7 +236,7 @@ export default function EditProfilePage() { {/* First Name / Last Name */}
- Name * + {t("name", { ns: "profile" })} *
@@ -262,7 +266,9 @@ export default function EditProfilePage() { <>
-
Organization
+
+ {t("organization", { ns: "profile" })} +
- Are you over 14? * + {t("over_14_question", { ns: "profile" })}{" "} + *
- Note: we require volunteers to be over 14 years old to work with - us. + {t("over_14_note", { ns: "profile" })}
handleChange("ageOver14", true)} /> @@ -328,12 +334,12 @@ export default function EditProfilePage() { {/* First time volunteering? */}
handleChange("firstTime", true)} /> @@ -350,7 +356,8 @@ export default function EditProfilePage() { {/* Address Row */}
- Address * + {t("address", { ns: "profile" })}{" "} + *
handleChange("hasLicense", true)} /> @@ -429,11 +436,12 @@ export default function EditProfilePage() { {/* Speak Spanish? */}
handleChange("speaksEsp", true)} /> @@ -451,7 +459,7 @@ export default function EditProfilePage() {
@@ -469,7 +477,7 @@ export default function EditProfilePage() {
- Do you have any other questions or comments? + {t("other_questions_prompt", { ns: "profile" })}
diff --git a/src/app/private/profile/[userId]/page.tsx b/src/app/private/profile/[userId]/page.tsx index cec8df1..c5d13b5 100644 --- a/src/app/private/profile/[userId]/page.tsx +++ b/src/app/private/profile/[userId]/page.tsx @@ -7,11 +7,13 @@ import ProfileContent from "@components/ProfileContent"; import { UserWithVolunteerDetail } from "../../../types"; import { VolunteerSession } from "@prisma/client"; import { getUser } from "@api/user/route.client"; +import { useTranslation } from "react-i18next"; export default function UserProfilePage() { const { userId } = useParams(); const { data: session, status } = useSession(); const router = useRouter(); + const { t } = useTranslation("profile"); const [user, setUser] = useState(null); const [sessions, setSessions] = useState([]); @@ -48,7 +50,7 @@ export default function UserProfilePage() { if (loading || !user) { return (
- Loading... + {t("loading")}...
); } diff --git a/src/app/private/profile/page.tsx b/src/app/private/profile/page.tsx index b45f2d4..0ba06fc 100644 --- a/src/app/private/profile/page.tsx +++ b/src/app/private/profile/page.tsx @@ -3,10 +3,12 @@ import { useEffect } from "react"; import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; +import { useTranslation } from "react-i18next"; export default function ProfilePage() { const { data: session, status } = useSession(); const router = useRouter(); + const { t } = useTranslation("profile"); useEffect(() => { if (status === "loading") return; @@ -17,7 +19,7 @@ export default function ProfilePage() { return (
- Loading... + {t("loading")}...
); } diff --git a/src/app/private/volunteers/page.tsx b/src/app/private/volunteers/page.tsx index a0f8132..68b5191 100644 --- a/src/app/private/volunteers/page.tsx +++ b/src/app/private/volunteers/page.tsx @@ -15,8 +15,10 @@ import { getOrganizations, } from "@api/organization/route.client"; import OrganizationTable from "@components/OrganizationTable"; +import { useTranslation } from "react-i18next"; export default function VolunteersPage() { + const { t } = useTranslation("volunteers"); const [pageLoading, setPageLoading] = React.useState(true); const [users, setUsers] = React.useState(); const [organizations, setOrganizations] = React.useState(); @@ -27,6 +29,21 @@ export default function VolunteersPage() { "Individuals" | "Organizations" >("Individuals"); + const isIndividual = activeTab === "Individuals"; + const isSingular = selected.length === 1; + + const typeKey = isIndividual + ? isSingular + ? "user" + : "users" + : isSingular + ? "organization" + : "organizations"; + + const profileKey = isSingular ? "profile" : "profiles"; + + const article = isSingular ? t("a") : ""; + React.useEffect(() => { const fetchData = async () => { try { @@ -122,7 +139,7 @@ export default function VolunteersPage() { if (pageLoading) { return (
- Loading... + {t("loading")}...
); } @@ -133,7 +150,10 @@ export default function VolunteersPage() {
- {activeTab === "Individuals" ? "Volunteer" : "Organizations"} List ( + {`${t( + activeTab === "Individuals" ? "volunteer" : "organizations" + )} ${t("list")}`}{" "} + ( {activeTab === "Individuals" && users ? users.length : organizations @@ -144,7 +164,9 @@ export default function VolunteersPage() {
{selected.length > 0 ? (
-
{selected.length} Selected
+
+ {selected.length} {t("selected")} +
) : ( @@ -195,7 +217,7 @@ export default function VolunteersPage() { width="24" height="24" /> -
{tab}
+
{t(tab.toLowerCase())}
))}
@@ -230,8 +252,9 @@ export default function VolunteersPage() { />
- No {activeTab === "Individuals" ? "individuals" : "organizations"}{" "} - found! + {activeTab === "Individuals" + ? t("no_individuals_found") + : t("no_organizations_found")}
)} @@ -240,19 +263,17 @@ export default function VolunteersPage() {
- Are you sure you want to delete {selected.length}{" "} - {activeTab === "Individuals" - ? selected.length === 1 - ? "user" - : "users" - : selected.length === 1 - ? "organization" - : "organizations"} - ? + {t("delete_confirmation_title", { + count: selected.length, + type: t(typeKey), + })}
- You will not be able to recover {selected.length === 1 ? "a" : ""}{" "} - deleted {selected.length === 1 ? "profile" : "profiles"}. + {t("delete_warning", { + article, + type: t(profileKey), + pluralSuffix: isSingular ? "" : t("pluralSuffix"), + })}
diff --git a/src/components/CheckInOut.tsx b/src/components/CheckInOut.tsx index f420d8e..cfdaa31 100644 --- a/src/components/CheckInOut.tsx +++ b/src/components/CheckInOut.tsx @@ -19,9 +19,11 @@ import { useRouter } from "next/navigation"; import useApiThrottle from "../hooks/useApiThrottle"; import { OrganizationWithUsers, UserWithVolunteerDetail } from "../app/types"; import { getOrganizationsByDate } from "@api/organization/route.client"; +import { useTranslation } from "react-i18next"; export default function CheckInOutForm() { const router = useRouter(); + const { t } = useTranslation("check"); const [email, setEmail] = useState(""); const [activeButton, setActiveButton] = useState< @@ -120,10 +122,10 @@ export default function CheckInOutForm() { } catch (err) { const errorData = JSON.parse((err as Error).message); if (errorData.code === "ALREADY_CHECKED_OUT") { - alert("You must check in before checking out again."); + alert(t("must_check_in_first")); } else { console.error("Check-out failed:", errorData.message); - alert("There was an error during check-out."); + alert(t("checkout_error")); } } } @@ -161,10 +163,10 @@ export default function CheckInOutForm() { } catch (err) { const errorData = JSON.parse((err as Error).message); if (errorData.code === "ALREADY_CHECKED_OUT") { - alert("This group has already checked out."); + alert(t("group_already_checked_out")); } else { console.error("Group check-out failed:", errorData.message); - alert("There was an error during check-out."); + alert(t("checkout_error")); } } } @@ -233,12 +235,10 @@ export default function CheckInOutForm() { const errorData = JSON.parse(err.message); if (errorData.code === "ALREADY_CHECKED_IN") { - alert( - "You have already checked in and must check out before checking in again." - ); + alert(t("already_checked_in")); } else { console.error("Check-in failed:", errorData.message); - alert("There was an error during check-in. Please try again."); + alert(t("checkin_error")); } } else { console.error("Unexpected error:", err); @@ -275,7 +275,7 @@ export default function CheckInOutForm() { }} > - Back + {t("back")}
- Daily Check-In/Check-Out + {t("daily_checkin_checkout")}
{new Date().toLocaleDateString("en-US", { @@ -310,9 +310,9 @@ export default function CheckInOutForm() {
- Here is your shift signup information: + {t("shift_signup_info")}
- Shift(s) (choose one) + {t("shifts_choose_one")} {timeSlots.map((slot, index) => { const start = new Date(slot.startTime); const end = new Date(slot.endTime); @@ -343,7 +343,7 @@ export default function CheckInOutForm() { /> - Confirm Your Check-In + {t("confirm_check_in")}
@@ -387,23 +387,22 @@ export default function CheckInOutForm() { } else if (stage === "confirmation") { return ( { setEmail(""); @@ -428,7 +427,7 @@ export default function CheckInOutForm() { }} > - Back to Home Page + {t("back_to_home_page")}
- Daily Check-In/Check-Out + {t("daily_checkin_checkout")}
{new Date().toLocaleDateString("en-US", { @@ -463,7 +462,7 @@ export default function CheckInOutForm() {
- I want to + {t("i_want_to")}
- Select type + {t("select_type")}
{activeTab === "individual" ? ( <> -
Your Email
+
{t("your_email")}
) : ( <> -
Organization Name
+
{t("organization_name")}
- Continue + {t("continue")}
diff --git a/src/components/CustomizeEventModal.tsx b/src/components/CustomizeEventModal.tsx index c23adad..a986102 100644 --- a/src/components/CustomizeEventModal.tsx +++ b/src/components/CustomizeEventModal.tsx @@ -10,6 +10,7 @@ import { format } from "date-fns"; import { Icon } from "@iconify/react/dist/iconify.js"; import { addCustomDay, getCustomDay } from "@api/customDay/route.client"; import useApiThrottle from "../hooks/useApiThrottle"; +import { useTranslation } from "react-i18next"; interface CustomizeEventProps { modalVisible: boolean; @@ -18,6 +19,7 @@ interface CustomizeEventProps { const CustomizeEventModal = (props: CustomizeEventProps) => { const { modalVisible, setModalVisible } = props; + const { t } = useTranslation("events"); const modalRef = useRef(null); const buttonRef = React.useRef(null); @@ -155,13 +157,13 @@ const CustomizeEventModal = (props: CustomizeEventProps) => { if (result.code === "SUCCESS") { setModalVisible(false); - alert("Event saved successfully!"); + alert(t("event_saved_success")); } else { - alert("Failed to save event."); + alert(t("event_save_failed")); } } catch (err) { console.error(err); - alert("An error occurred while saving."); + alert(t("event_save_error")); } }; @@ -188,7 +190,7 @@ const CustomizeEventModal = (props: CustomizeEventProps) => { />
{ />
- Time + {t("time")}
@@ -223,7 +225,7 @@ const CustomizeEventModal = (props: CustomizeEventProps) => { className="border border-gray-300 rounded-md px-3 py-2 w-[215.5px] focus:outline-none focus:ring-1 focus:ring-blue-500" placeholder="MM/DD/YYYY" variant="outlined" - label="Start Date" + label={t("start_date")} autoComplete="off" size="small" onFocus={() => setShowCalendar(!showCalendar)} @@ -279,7 +281,7 @@ const CustomizeEventModal = (props: CustomizeEventProps) => {
- Event description (if any) + {t("event_description_optional")}