- Welcome to {appName}
+ {t("login.welcome", { appName, defaultValue: "Welcome to {{appName}}" })}
- Sign in to access your account.
+ {t("login.subtitle", "Sign in to access your account.")}
@@ -165,16 +172,16 @@ export function LoginPage() {
{enableSignup && (
- Don't have an account?{" "}
+ {t("login.noAccount", "Don't have an account? ")}
- Create one
+ {t("login.createOne", "Create one")}
)}
diff --git a/libs/auth/src/lib/pages/SignupPage.tsx b/libs/auth/src/lib/pages/SignupPage.tsx
index 4ef7fb9d0..2ce426e1f 100644
--- a/libs/auth/src/lib/pages/SignupPage.tsx
+++ b/libs/auth/src/lib/pages/SignupPage.tsx
@@ -25,6 +25,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { useNavigate, Link } from 'react-router-dom';
import { useDispatch } from 'react-redux';
+import { useTranslation } from 'react-i18next';
import { useApiMutation } from '@ema-platform/api';
import { useErrorHandler, passwordSchema, PasswordRequirements } from '@ema-platform/ui';
import { AuthShell } from '../components/AuthShell';
@@ -32,24 +33,6 @@ import { loginSuccess, setUser } from '../store/auth.slice';
import type { AuthUser } from '../types/auth.types';
import { useAuthConfig } from '../AuthConfig';
-const schema = z
- .object({
- email: z.string().email(),
- username: z.string().min(3, { message: 'Username must be at least 3 characters' }),
- phoneNumber: z.string().min(1, { message: 'Phone number is required' }),
- userType: z.literal('individual'),
- nameEn: z.string().min(1, { message: 'Name (English) is required' }),
- nameAm: z.string().optional(),
- password: passwordSchema(8),
- confirmPassword: z.string().min(1, { message: 'Confirm your password' }),
- })
- .refine((data) => data.password === data.confirmPassword, {
- message: 'Passwords do not match',
- path: ['confirmPassword'],
- });
-
-type FormValues = z.infer;
-
interface SignupPayload {
email: string;
username: string;
@@ -66,6 +49,7 @@ interface SignupPayload {
export function SignupPage() {
const navigate = useNavigate();
const dispatch = useDispatch();
+ const { t } = useTranslation();
const { appName, loginRedirectPath } = useAuthConfig();
const [agreed, setAgreed] = useState(false);
const [serverError, setServerError] = useState(null);
@@ -77,6 +61,37 @@ export function SignupPage() {
}>();
const [meTrigger] = useApiMutation();
+ // Order matches passwordRules' default list: length, lowercase, uppercase,
+ // number, special character. Shared between the zod schema (field error)
+ // and the live checklist below, so both agree on the wording.
+ const passwordRuleLabels = [
+ t('signup.passwordRule.minLength', { min: 8, defaultValue: 'At least {{min}} characters' }),
+ t('signup.passwordRule.lowercase', 'One lowercase letter'),
+ t('signup.passwordRule.uppercase', 'One uppercase letter'),
+ t('signup.passwordRule.number', 'One number'),
+ t('signup.passwordRule.special', 'One special character'),
+ ];
+
+ // Built inside the component (not module scope) so validation messages
+ // pick up the active language — same pattern as ProfilePage's forms.
+ const schema = z
+ .object({
+ email: z.string().email(),
+ username: z.string().min(3, { message: t('signup.usernameMinLength', 'Username must be at least 3 characters') }),
+ phoneNumber: z.string().min(1, { message: t('signup.phoneRequired', 'Phone number is required') }),
+ userType: z.literal('individual'),
+ nameEn: z.string().min(1, { message: t('signup.nameEnRequired', 'Name (English) is required') }),
+ nameAm: z.string().optional(),
+ password: passwordSchema(8, passwordRuleLabels),
+ confirmPassword: z.string().min(1, { message: t('signup.confirmPasswordRequired', 'Confirm your password') }),
+ })
+ .refine((data) => data.password === data.confirmPassword, {
+ message: t('signup.passwordsDontMatch', 'Passwords do not match'),
+ path: ['confirmPassword'],
+ });
+
+ type FormValues = z.infer;
+
const {
register,
handleSubmit,
@@ -133,16 +148,19 @@ export function SignupPage() {
return (
- Create account
+ {t('signup.title', 'Create account')}
- It only takes a minute to get started.
+ {t('signup.subtitle', 'It only takes a minute to get started.')}