Phase 5: Translate Auth Pages - login and signup

- Create auth.json translation files for es, en, ca
- Translate login page: title, subtitle, form labels, buttons, footer
- Translate signup page: invite code step and account creation step
- Translate signup/[code] redirect page
- Update IntlProvider to load auth namespace
- Update test expectations to match Spanish translations (default language)
- All frontend and e2e tests passing
This commit is contained in:
counterweight 2025-12-25 22:14:04 +01:00
parent a5a1a2c1ad
commit 7dd13292a0
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
9 changed files with 188 additions and 47 deletions

View file

@ -6,6 +6,7 @@ import { useAuth } from "../auth-context";
import { invitesApi } from "../api";
import { authFormStyles as styles } from "../styles/auth-form";
import { LanguageSelector } from "../components/LanguageSelector";
import { useTranslation } from "../hooks/useTranslation";
function SignupContent() {
const searchParams = useSearchParams();
@ -25,6 +26,7 @@ function SignupContent() {
const { user, register } = useAuth();
const router = useRouter();
const t = useTranslation("auth");
// Redirect if already logged in
useEffect(() => {
@ -51,11 +53,11 @@ function SignupContent() {
setInviteError("");
} else {
setInviteValid(false);
setInviteError(response.error || "Invalid invite code");
setInviteError(response.error || t("signup.invalidInviteCode"));
}
} catch {
setInviteValid(false);
setInviteError("Failed to verify invite code");
setInviteError(t("signup.failedToVerify"));
} finally {
setIsCheckingInvite(false);
}
@ -78,12 +80,12 @@ function SignupContent() {
setError("");
if (password !== confirmPassword) {
setError("Passwords do not match");
setError(t("signup.passwordsDoNotMatch"));
return;
}
if (password.length < 6) {
setError("Password must be at least 6 characters");
setError(t("signup.passwordTooShort"));
return;
}
@ -93,7 +95,7 @@ function SignupContent() {
await register(email, password, inviteCode.trim());
router.push("/");
} catch (err) {
setError(err instanceof Error ? err.message : "Registration failed");
setError(err instanceof Error ? err.message : t("signup.registrationFailed"));
} finally {
setIsSubmitting(false);
}
@ -114,7 +116,7 @@ function SignupContent() {
<div style={styles.container}>
<div style={styles.card}>
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.6)" }}>
Checking invite code...
{t("signup.checkingInviteCode")}
</div>
</div>
</div>
@ -132,8 +134,8 @@ function SignupContent() {
<div style={styles.container}>
<div style={styles.card}>
<div style={styles.header}>
<h1 style={styles.title}>Join with Invite</h1>
<p style={styles.subtitle}>Enter your invite code to get started</p>
<h1 style={styles.title}>{t("signup.title")}</h1>
<p style={styles.subtitle}>{t("signup.subtitle")}</p>
</div>
<form onSubmit={handleInviteSubmit} style={styles.form}>
@ -141,7 +143,7 @@ function SignupContent() {
<div style={styles.field}>
<label htmlFor="inviteCode" style={styles.label}>
Invite Code
{t("signup.inviteCode")}
</label>
<input
id="inviteCode"
@ -153,7 +155,7 @@ function SignupContent() {
setInviteValid(null);
}}
style={styles.input}
placeholder="word-word-00"
placeholder={t("signup.inviteCodePlaceholder")}
required
autoFocus
/>
@ -165,7 +167,7 @@ function SignupContent() {
display: "block",
}}
>
Ask your inviter for this code
{t("signup.inviteHint")}
</span>
</div>
@ -177,14 +179,14 @@ function SignupContent() {
}}
disabled={isCheckingInvite || !inviteCode.trim()}
>
{isCheckingInvite ? "Checking..." : "Continue"}
{isCheckingInvite ? t("signup.checking") : t("signup.continue")}
</button>
</form>
<p style={styles.footer}>
Already have an account?{" "}
{t("signup.alreadyHaveAccount")}{" "}
<a href="/login" style={styles.link}>
Sign in
{t("signup.signIn")}
</a>
</p>
</div>
@ -202,9 +204,9 @@ function SignupContent() {
<div style={styles.container}>
<div style={styles.card}>
<div style={styles.header}>
<h1 style={styles.title}>Create account</h1>
<h1 style={styles.title}>{t("signup.createAccountTitle")}</h1>
<p style={styles.subtitle}>
Using invite:{" "}
{t("signup.createAccountSubtitle")}{" "}
<code
style={{
background: "rgba(255,255,255,0.1)",
@ -223,7 +225,7 @@ function SignupContent() {
<div style={styles.field}>
<label htmlFor="email" style={styles.label}>
Email
{t("signup.email")}
</label>
<input
id="email"
@ -231,7 +233,7 @@ function SignupContent() {
value={email}
onChange={(e) => setEmail(e.target.value)}
style={styles.input}
placeholder="you@example.com"
placeholder={t("signup.emailPlaceholder")}
required
autoFocus
/>
@ -239,7 +241,7 @@ function SignupContent() {
<div style={styles.field}>
<label htmlFor="password" style={styles.label}>
Password
{t("signup.password")}
</label>
<input
id="password"
@ -247,14 +249,14 @@ function SignupContent() {
value={password}
onChange={(e) => setPassword(e.target.value)}
style={styles.input}
placeholder="••••••••"
placeholder={t("signup.passwordPlaceholder")}
required
/>
</div>
<div style={styles.field}>
<label htmlFor="confirmPassword" style={styles.label}>
Confirm Password
{t("signup.confirmPassword")}
</label>
<input
id="confirmPassword"
@ -262,7 +264,7 @@ function SignupContent() {
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
style={styles.input}
placeholder="••••••••"
placeholder={t("signup.confirmPasswordPlaceholder")}
required
/>
</div>
@ -275,7 +277,7 @@ function SignupContent() {
}}
disabled={isSubmitting}
>
{isSubmitting ? "Creating account..." : "Create account"}
{isSubmitting ? t("signup.creatingAccount") : t("signup.createAccount")}
</button>
</form>
@ -293,7 +295,7 @@ function SignupContent() {
padding: 0,
}}
>
Use a different invite code
{t("signup.useDifferentCode")}
</button>
</p>
</div>