diff --git a/lang/en/auth.php b/lang/en/auth.php index 989bff4..126adc7 100644 --- a/lang/en/auth.php +++ b/lang/en/auth.php @@ -12,5 +12,53 @@ 'remember' => 'Remember me', 'submit' => 'Sign in', ], + 'forgot_password' => [ + 'title' => 'Forgot password', + 'description' => 'Enter your email to receive a password reset link', + 'email' => 'Email address', + 'email_placeholder' => 'email@example.com', + 'submit' => 'Email password reset link', + 'return_prefix' => 'Or, return to', + 'login_link' => 'log in', + ], + 'two_factor' => [ + 'head' => 'Two-Factor Authentication', + 'auth_title' => 'Authentication Code', + 'auth_description' => 'Enter the authentication code provided by your authenticator application.', + 'auth_toggle' => 'login using a recovery code', + 'recovery_title' => 'Recovery Code', + 'recovery_description' => 'Please confirm access to your account by entering one of your emergency recovery codes.', + 'recovery_toggle' => 'login using an authentication code', + 'recovery_placeholder' => 'Enter recovery code', + 'continue' => 'Continue', + 'or_you_can' => 'or you can', + ], + 'verify_email' => [ + 'title' => 'Verify email', + 'description' => 'Please verify your email address by clicking on the link we just emailed to you.', + 'head' => 'Email verification', + 'link_sent' => 'A new verification link has been sent to the email address you provided during registration.', + 'resend' => 'Resend verification email', + 'logout' => 'Log out', + ], + 'confirm_password' => [ + 'title' => 'Confirm your password', + 'description' => 'This is a secure area of the application. Please confirm your password before continuing.', + 'head' => 'Confirm password', + 'password' => 'Password', + 'password_placeholder' => 'Password', + 'submit' => 'Confirm password', + ], + 'reset_password' => [ + 'title' => 'Reset password', + 'description' => 'Please enter your new password below', + 'head' => 'Reset password', + 'email' => 'Email', + 'password' => 'Password', + 'password_placeholder' => 'Password', + 'confirm' => 'Confirm password', + 'confirm_placeholder' => 'Confirm password', + 'submit' => 'Reset password', + ], 'logout' => 'Log out', ]; diff --git a/lang/en/timetable.php b/lang/en/timetable.php new file mode 100644 index 0000000..7b7a64c --- /dev/null +++ b/lang/en/timetable.php @@ -0,0 +1,36 @@ + 'Timetable', + 'title' => 'Timetable', + 'subtitle' => 'Organize class slots per class for the active year.', + 'teacher_view' => 'Teacher view', + 'choose_class' => 'Choose a class', + 'export_pdf' => 'Export to PDF', + 'select_class_prompt' => 'Select a class to display its timetable.', + 'add_slot' => 'Add a slot', + 'subject_fallback' => 'Subject', + 'edit_slot' => 'Edit slot', + 'new_slot' => 'New slot', + 'day' => 'Day', + 'start' => 'Start', + 'end' => 'End', + 'subject' => 'Subject', + 'choose' => 'Choose', + 'teacher' => 'Teacher', + 'room' => 'Room', + 'room_ph' => 'e.g. Room 12', + 'cancel' => 'Cancel', + 'update' => 'Update', + 'add' => 'Add', + + 'head_teacher' => 'Timetable — by teacher', + 'subtitle_teacher' => 'A teacher\'s schedule for the active year, across all classes.', + 'choose_teacher' => 'Choose a teacher', + 'select_teacher_prompt' => 'Select a teacher to display their timetable.', + 'no_slots_teacher' => 'No slots for this teacher in the active year.', + 'slot_one' => 'slot', + 'slot_other' => 'slots', + 'class_one' => 'class', + 'class_other' => 'classes', +]; diff --git a/lang/fr/auth.php b/lang/fr/auth.php index 284f3e5..d9e1c51 100644 --- a/lang/fr/auth.php +++ b/lang/fr/auth.php @@ -12,5 +12,53 @@ 'remember' => 'Se souvenir de moi', 'submit' => 'Se connecter', ], + 'forgot_password' => [ + 'title' => 'Mot de passe oublié', + 'description' => 'Saisissez votre e-mail pour recevoir un lien de réinitialisation', + 'email' => 'Adresse e-mail', + 'email_placeholder' => 'email@exemple.com', + 'submit' => 'Envoyer le lien de réinitialisation', + 'return_prefix' => 'Ou revenez à la', + 'login_link' => 'connexion', + ], + 'two_factor' => [ + 'head' => 'Authentification à deux facteurs', + 'auth_title' => 'Code d\'authentification', + 'auth_description' => 'Saisissez le code d\'authentification fourni par votre application d\'authentification.', + 'auth_toggle' => 'se connecter avec un code de récupération', + 'recovery_title' => 'Code de récupération', + 'recovery_description' => 'Confirmez l\'accès à votre compte en saisissant l\'un de vos codes de récupération d\'urgence.', + 'recovery_toggle' => 'se connecter avec un code d\'authentification', + 'recovery_placeholder' => 'Saisir le code de récupération', + 'continue' => 'Continuer', + 'or_you_can' => 'ou vous pouvez', + ], + 'verify_email' => [ + 'title' => 'Vérifier l\'e-mail', + 'description' => 'Veuillez vérifier votre adresse e-mail en cliquant sur le lien que nous venons de vous envoyer.', + 'head' => 'Vérification de l\'e-mail', + 'link_sent' => 'Un nouveau lien de vérification a été envoyé à l\'adresse e-mail que vous avez fournie lors de l\'inscription.', + 'resend' => 'Renvoyer l\'e-mail de vérification', + 'logout' => 'Déconnexion', + ], + 'confirm_password' => [ + 'title' => 'Confirmez votre mot de passe', + 'description' => 'Ceci est une zone sécurisée de l\'application. Veuillez confirmer votre mot de passe avant de continuer.', + 'head' => 'Confirmer le mot de passe', + 'password' => 'Mot de passe', + 'password_placeholder' => 'Mot de passe', + 'submit' => 'Confirmer le mot de passe', + ], + 'reset_password' => [ + 'title' => 'Réinitialiser le mot de passe', + 'description' => 'Veuillez saisir votre nouveau mot de passe ci-dessous', + 'head' => 'Réinitialiser le mot de passe', + 'email' => 'E-mail', + 'password' => 'Mot de passe', + 'password_placeholder' => 'Mot de passe', + 'confirm' => 'Confirmer le mot de passe', + 'confirm_placeholder' => 'Confirmer le mot de passe', + 'submit' => 'Réinitialiser le mot de passe', + ], 'logout' => 'Déconnexion', ]; diff --git a/lang/fr/timetable.php b/lang/fr/timetable.php new file mode 100644 index 0000000..55bf260 --- /dev/null +++ b/lang/fr/timetable.php @@ -0,0 +1,36 @@ + 'Emploi du temps', + 'title' => 'Emploi du temps', + 'subtitle' => 'Organisez les créneaux de cours par classe pour l\'année active.', + 'teacher_view' => 'Vue par enseignant', + 'choose_class' => 'Choisir une classe', + 'export_pdf' => 'Exporter en PDF', + 'select_class_prompt' => 'Sélectionnez une classe pour afficher son emploi du temps.', + 'add_slot' => 'Ajouter un créneau', + 'subject_fallback' => 'Matière', + 'edit_slot' => 'Modifier le créneau', + 'new_slot' => 'Nouveau créneau', + 'day' => 'Jour', + 'start' => 'Début', + 'end' => 'Fin', + 'subject' => 'Matière', + 'choose' => 'Choisir', + 'teacher' => 'Enseignant', + 'room' => 'Salle', + 'room_ph' => 'Ex : Salle 12', + 'cancel' => 'Annuler', + 'update' => 'Mettre à jour', + 'add' => 'Ajouter', + + 'head_teacher' => 'Emploi du temps — par enseignant', + 'subtitle_teacher' => 'Programmation d\'un enseignant pour l\'année active, toutes classes confondues.', + 'choose_teacher' => 'Choisir un enseignant', + 'select_teacher_prompt' => 'Sélectionnez un enseignant pour afficher son emploi du temps.', + 'no_slots_teacher' => 'Aucun créneau pour cet enseignant sur l\'année active.', + 'slot_one' => 'créneau', + 'slot_other' => 'créneaux', + 'class_one' => 'classe', + 'class_other' => 'classes', +]; diff --git a/resources/js/pages/Eleves/Timetable/Index.tsx b/resources/js/pages/Eleves/Timetable/Index.tsx index 3c134f1..ace6803 100644 --- a/resources/js/pages/Eleves/Timetable/Index.tsx +++ b/resources/js/pages/Eleves/Timetable/Index.tsx @@ -6,6 +6,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/u import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { route } from '@/helpers/route'; +import { useTranslation } from '@/helpers/i18n'; import AppLayout from '@/layouts/app-layout'; interface Option { id: string; name: string; code?: string; } @@ -43,6 +44,7 @@ const emptyForm = { }; export default function Index({ classrooms, subjects, teachers, slots, days, filters, canManage }: Readonly) { + const { t } = useTranslation(); const [classId, setClassId] = useState(filters.class_id ?? ''); const [dialogOpen, setDialogOpen] = useState(false); const [editing, setEditing] = useState(false); @@ -91,16 +93,16 @@ export default function Index({ classrooms, subjects, teachers, slots, days, fil return ( - +
-

Emploi du temps

-

Organisez les créneaux de cours par classe pour l'année active.

+

{t('timetable.title')}

+

{t('timetable.subtitle')}

@@ -109,7 +111,7 @@ export default function Index({ classrooms, subjects, teachers, slots, days, fil
setData('day_of_week', Number(v))}> @@ -187,20 +189,20 @@ export default function Index({ classrooms, subjects, teachers, slots, days, fil
- + setData('start_time', e.target.value)} />
- + setData('end_time', e.target.value)} className={errors.end_time ? 'border-red-400' : ''} /> {errors.end_time &&

{errors.end_time}

}
- + setData('teacher_id', v === 'none' ? '' : v)}> - + — - {teachers.map(t => {t.name})} + {teachers.map(tc => {tc.name})}
- - setData('room', e.target.value)} placeholder="Ex: Salle 12" /> + + setData('room', e.target.value)} placeholder={t('timetable.room_ph')} />
- - + +
diff --git a/resources/js/pages/Eleves/Timetable/Teacher.tsx b/resources/js/pages/Eleves/Timetable/Teacher.tsx index 55cc00a..c931c37 100644 --- a/resources/js/pages/Eleves/Timetable/Teacher.tsx +++ b/resources/js/pages/Eleves/Timetable/Teacher.tsx @@ -3,6 +3,7 @@ import { ArrowLeft, CalendarRange, Clock, Download, MapPin, User, BookOpen } fro import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { route } from '@/helpers/route'; +import { useTranslation } from '@/helpers/i18n'; import AppLayout from '@/layouts/app-layout'; interface Option { id: string; name: string; } @@ -26,6 +27,7 @@ interface Props { } export default function Teacher({ teachers, teacher, slots, days, filters }: Readonly) { + const { t } = useTranslation(); const teacherId = filters.teacher_id ?? ''; const dayKeys = Object.keys(days).map(Number); @@ -41,7 +43,7 @@ export default function Teacher({ teachers, teacher, slots, days, filters }: Rea return ( - +
{/* Header */} @@ -57,9 +59,9 @@ export default function Teacher({ teachers, teacher, slots, days, filters }: Rea

- Emploi du temps — par enseignant + {t('timetable.head_teacher')}

-

Programmation d'un enseignant pour l'année active, toutes classes confondues.

+

{t('timetable.subtitle_teacher')}

@@ -75,9 +77,9 @@ export default function Teacher({ teachers, teacher, slots, days, filters }: Rea
@@ -85,7 +87,7 @@ export default function Teacher({ teachers, teacher, slots, days, filters }: Rea {!teacherId ? (
- Sélectionnez un enseignant pour afficher son emploi du temps. + {t('timetable.select_teacher_prompt')}
) : ( <> @@ -97,16 +99,16 @@ export default function Teacher({ teachers, teacher, slots, days, filters }: Rea
{teacher.name} · - {slots.length} créneau{slots.length > 1 ? 'x' : ''} + {slots.length} {slots.length > 1 ? t('timetable.slot_other') : t('timetable.slot_one')} · - {distinctClasses} classe{distinctClasses > 1 ? 's' : ''} + {distinctClasses} {distinctClasses > 1 ? t('timetable.class_other') : t('timetable.class_one')} )} {slots.length === 0 ? (
- Aucun créneau pour cet enseignant sur l'année active. + {t('timetable.no_slots_teacher')}
) : (
@@ -123,7 +125,7 @@ export default function Teacher({ teachers, teacher, slots, days, filters }: Rea {slot.start_time}–{slot.end_time} -

{slot.subject_name ?? 'Matière'}

+

{slot.subject_name ?? t('timetable.subject_fallback')}

{slot.class_name &&

{slot.class_name}

} {slot.room &&

{slot.room}

}
diff --git a/resources/js/pages/auth/confirm-password.tsx b/resources/js/pages/auth/confirm-password.tsx index dd3daa3..3aa261b 100644 --- a/resources/js/pages/auth/confirm-password.tsx +++ b/resources/js/pages/auth/confirm-password.tsx @@ -4,27 +4,29 @@ import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Spinner } from '@/components/ui/spinner'; +import { useTranslation } from '@/helpers/i18n'; import AuthLayout from '@/layouts/auth-layout'; import { store } from '@/routes/password/confirm'; export default function ConfirmPassword() { + const { t } = useTranslation(); return ( - +
{({ processing, errors }) => (
- + @@ -39,7 +41,7 @@ export default function ConfirmPassword() { data-test="confirm-password-button" > {processing && } - Confirm password + {t('auth.confirm_password.submit')}
diff --git a/resources/js/pages/auth/forgot-password.tsx b/resources/js/pages/auth/forgot-password.tsx index e3803c4..6c7f304 100644 --- a/resources/js/pages/auth/forgot-password.tsx +++ b/resources/js/pages/auth/forgot-password.tsx @@ -6,17 +6,19 @@ import TextLink from '@/components/text-link'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; +import { useTranslation } from '@/helpers/i18n'; import AuthLayout from '@/layouts/auth-layout'; import { login } from '@/routes'; import { email } from '@/routes/password'; export default function ForgotPassword({ status }: { status?: string }) { + const { t } = useTranslation(); return ( - + {status && (
@@ -29,14 +31,14 @@ export default function ForgotPassword({ status }: { status?: string }) { {({ processing, errors }) => ( <>
- + @@ -51,7 +53,7 @@ export default function ForgotPassword({ status }: { status?: string }) { {processing && ( )} - Email password reset link + {t('auth.forgot_password.submit')}
@@ -59,8 +61,8 @@ export default function ForgotPassword({ status }: { status?: string }) {
- Or, return to - log in + {t('auth.forgot_password.return_prefix')} + {t('auth.forgot_password.login_link')}
diff --git a/resources/js/pages/auth/reset-password.tsx b/resources/js/pages/auth/reset-password.tsx index 4a58a4f..f071ec8 100644 --- a/resources/js/pages/auth/reset-password.tsx +++ b/resources/js/pages/auth/reset-password.tsx @@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Spinner } from '@/components/ui/spinner'; +import { useTranslation } from '@/helpers/i18n'; import AuthLayout from '@/layouts/auth-layout'; import { update } from '@/routes/password'; @@ -15,14 +16,15 @@ type Props = { }; export default function ResetPassword({ token, email }: Props) { + const { t } = useTranslation(); const [password, setPassword] = useState(''); return ( - +
(
- +
- + setPassword(e.target.value)} /> @@ -66,7 +68,7 @@ export default function ResetPassword({ token, email }: Props) {
{processing && } - Reset password + {t('auth.reset_password.submit')}
)} diff --git a/resources/js/pages/auth/two-factor-challenge.tsx b/resources/js/pages/auth/two-factor-challenge.tsx index 011d0c7..9c99de7 100644 --- a/resources/js/pages/auth/two-factor-challenge.tsx +++ b/resources/js/pages/auth/two-factor-challenge.tsx @@ -10,10 +10,12 @@ import { InputOTPSlot, } from '@/components/ui/input-otp'; import { OTP_MAX_LENGTH } from '@/hooks/use-two-factor-auth'; +import { useTranslation } from '@/helpers/i18n'; import AuthLayout from '@/layouts/auth-layout'; import { store } from '@/routes/two-factor/login'; export default function TwoFactorChallenge() { + const { t } = useTranslation(); const [showRecoveryInput, setShowRecoveryInput] = useState(false); const [code, setCode] = useState(''); @@ -24,20 +26,18 @@ export default function TwoFactorChallenge() { }>(() => { if (showRecoveryInput) { return { - title: 'Recovery Code', - description: - 'Please confirm access to your account by entering one of your emergency recovery codes.', - toggleText: 'login using an authentication code', + title: t('auth.two_factor.recovery_title'), + description: t('auth.two_factor.recovery_description'), + toggleText: t('auth.two_factor.recovery_toggle'), }; } return { - title: 'Authentication Code', - description: - 'Enter the authentication code provided by your authenticator application.', - toggleText: 'login using a recovery code', + title: t('auth.two_factor.auth_title'), + description: t('auth.two_factor.auth_description'), + toggleText: t('auth.two_factor.auth_toggle'), }; - }, [showRecoveryInput]); + }, [showRecoveryInput, t]); const toggleRecoveryMode = (clearErrors: () => void): void => { setShowRecoveryInput(!showRecoveryInput); @@ -50,7 +50,7 @@ export default function TwoFactorChallenge() { title={authConfigContent.title} description={authConfigContent.description} > - +
@@ -107,11 +107,11 @@ export default function TwoFactorChallenge() { className="w-full" disabled={processing} > - Continue + {t('auth.two_factor.continue')}
- or you can + {t('auth.two_factor.or_you_can')} - Log out + {t('auth.verify_email.logout')} )}