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:
parent
a5a1a2c1ad
commit
7dd13292a0
9 changed files with 188 additions and 47 deletions
|
|
@ -14,11 +14,14 @@ import caNavigation from "../../locales/ca/navigation.json";
|
||||||
import esExchange from "../../locales/es/exchange.json";
|
import esExchange from "../../locales/es/exchange.json";
|
||||||
import enExchange from "../../locales/en/exchange.json";
|
import enExchange from "../../locales/en/exchange.json";
|
||||||
import caExchange from "../../locales/ca/exchange.json";
|
import caExchange from "../../locales/ca/exchange.json";
|
||||||
|
import esAuth from "../../locales/es/auth.json";
|
||||||
|
import enAuth from "../../locales/en/auth.json";
|
||||||
|
import caAuth from "../../locales/ca/auth.json";
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
es: { common: esCommon, navigation: esNavigation, exchange: esExchange },
|
es: { common: esCommon, navigation: esNavigation, exchange: esExchange, auth: esAuth },
|
||||||
en: { common: enCommon, navigation: enNavigation, exchange: enExchange },
|
en: { common: enCommon, navigation: enNavigation, exchange: enExchange, auth: enAuth },
|
||||||
ca: { common: caCommon, navigation: caNavigation, exchange: caExchange },
|
ca: { common: caCommon, navigation: caNavigation, exchange: caExchange, auth: caAuth },
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IntlProviderProps {
|
interface IntlProviderProps {
|
||||||
|
|
|
||||||
|
|
@ -19,21 +19,21 @@ afterEach(() => cleanup());
|
||||||
|
|
||||||
test("renders login form with title", () => {
|
test("renders login form with title", () => {
|
||||||
renderWithProviders(<LoginPage />);
|
renderWithProviders(<LoginPage />);
|
||||||
expect(screen.getByText("Welcome back")).toBeDefined();
|
expect(screen.getByText("Bienvenido de nuevo")).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders email and password inputs", () => {
|
test("renders email and password inputs", () => {
|
||||||
renderWithProviders(<LoginPage />);
|
renderWithProviders(<LoginPage />);
|
||||||
expect(screen.getByLabelText("Email")).toBeDefined();
|
expect(screen.getByLabelText("Correo electrónico")).toBeDefined();
|
||||||
expect(screen.getByLabelText("Password")).toBeDefined();
|
expect(screen.getByLabelText("Contraseña")).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders sign in button", () => {
|
test("renders sign in button", () => {
|
||||||
renderWithProviders(<LoginPage />);
|
renderWithProviders(<LoginPage />);
|
||||||
expect(screen.getByRole("button", { name: "Sign in" })).toBeDefined();
|
expect(screen.getByRole("button", { name: "Iniciar sesión" })).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders link to signup", () => {
|
test("renders link to signup", () => {
|
||||||
renderWithProviders(<LoginPage />);
|
renderWithProviders(<LoginPage />);
|
||||||
expect(screen.getByText("Sign up")).toBeDefined();
|
expect(screen.getByText("Regístrate")).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
|
||||||
import { useAuth } from "../auth-context";
|
import { useAuth } from "../auth-context";
|
||||||
import { authFormStyles as styles } from "../styles/auth-form";
|
import { authFormStyles as styles } from "../styles/auth-form";
|
||||||
import { LanguageSelector } from "../components/LanguageSelector";
|
import { LanguageSelector } from "../components/LanguageSelector";
|
||||||
|
import { useTranslation } from "../hooks/useTranslation";
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
|
|
@ -13,6 +14,7 @@ export default function LoginPage() {
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const t = useTranslation("auth");
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -23,7 +25,7 @@ export default function LoginPage() {
|
||||||
await login(email, password);
|
await login(email, password);
|
||||||
router.push("/");
|
router.push("/");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Login failed");
|
setError(err instanceof Error ? err.message : t("login.loginFailed"));
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
|
|
@ -37,8 +39,8 @@ export default function LoginPage() {
|
||||||
<div style={styles.container}>
|
<div style={styles.container}>
|
||||||
<div style={styles.card}>
|
<div style={styles.card}>
|
||||||
<div style={styles.header}>
|
<div style={styles.header}>
|
||||||
<h1 style={styles.title}>Welcome back</h1>
|
<h1 style={styles.title}>{t("login.title")}</h1>
|
||||||
<p style={styles.subtitle}>Sign in to your account</p>
|
<p style={styles.subtitle}>{t("login.subtitle")}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} style={styles.form}>
|
<form onSubmit={handleSubmit} style={styles.form}>
|
||||||
|
|
@ -46,7 +48,7 @@ export default function LoginPage() {
|
||||||
|
|
||||||
<div style={styles.field}>
|
<div style={styles.field}>
|
||||||
<label htmlFor="email" style={styles.label}>
|
<label htmlFor="email" style={styles.label}>
|
||||||
Email
|
{t("login.email")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
|
|
@ -54,14 +56,14 @@ export default function LoginPage() {
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
placeholder="you@example.com"
|
placeholder={t("login.emailPlaceholder")}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={styles.field}>
|
<div style={styles.field}>
|
||||||
<label htmlFor="password" style={styles.label}>
|
<label htmlFor="password" style={styles.label}>
|
||||||
Password
|
{t("login.password")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
|
|
@ -69,7 +71,7 @@ export default function LoginPage() {
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
placeholder="••••••••"
|
placeholder={t("login.passwordPlaceholder")}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -82,14 +84,14 @@ export default function LoginPage() {
|
||||||
}}
|
}}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
{isSubmitting ? "Signing in..." : "Sign in"}
|
{isSubmitting ? t("login.signingIn") : t("login.signIn")}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p style={styles.footer}>
|
<p style={styles.footer}>
|
||||||
Don't have an account?{" "}
|
{t("login.noAccount")}{" "}
|
||||||
<a href="/signup" style={styles.link}>
|
<a href="/signup" style={styles.link}>
|
||||||
Sign up
|
{t("login.signUp")}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@ import { useEffect } from "react";
|
||||||
import { useRouter, useParams } from "next/navigation";
|
import { useRouter, useParams } from "next/navigation";
|
||||||
import { useAuth } from "../../auth-context";
|
import { useAuth } from "../../auth-context";
|
||||||
import { LanguageSelector } from "../../components/LanguageSelector";
|
import { LanguageSelector } from "../../components/LanguageSelector";
|
||||||
|
import { useTranslation } from "../../hooks/useTranslation";
|
||||||
|
|
||||||
export default function SignupWithCodePage() {
|
export default function SignupWithCodePage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { user, isLoading } = useAuth();
|
const { user, isLoading } = useAuth();
|
||||||
const code = params.code as string;
|
const code = params.code as string;
|
||||||
|
const t = useTranslation("auth");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Wait for auth check to complete before redirecting
|
// Wait for auth check to complete before redirecting
|
||||||
|
|
@ -40,7 +42,7 @@ export default function SignupWithCodePage() {
|
||||||
<div style={{ position: "absolute", top: "1rem", right: "1rem" }}>
|
<div style={{ position: "absolute", top: "1rem", right: "1rem" }}>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</div>
|
</div>
|
||||||
Redirecting...
|
{t("signup.redirecting")}
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,20 +21,20 @@ afterEach(() => cleanup());
|
||||||
test("renders signup form with title", () => {
|
test("renders signup form with title", () => {
|
||||||
renderWithProviders(<SignupPage />);
|
renderWithProviders(<SignupPage />);
|
||||||
// Step 1 shows "Join with Invite" title (invite code entry)
|
// Step 1 shows "Join with Invite" title (invite code entry)
|
||||||
expect(screen.getByRole("heading", { name: "Join with Invite" })).toBeDefined();
|
expect(screen.getByRole("heading", { name: "Únete con Invitación" })).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders invite code input", () => {
|
test("renders invite code input", () => {
|
||||||
renderWithProviders(<SignupPage />);
|
renderWithProviders(<SignupPage />);
|
||||||
expect(screen.getByLabelText("Invite Code")).toBeDefined();
|
expect(screen.getByLabelText("Código de Invitación")).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders continue button", () => {
|
test("renders continue button", () => {
|
||||||
renderWithProviders(<SignupPage />);
|
renderWithProviders(<SignupPage />);
|
||||||
expect(screen.getByRole("button", { name: "Continue" })).toBeDefined();
|
expect(screen.getByRole("button", { name: "Continuar" })).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders link to login", () => {
|
test("renders link to login", () => {
|
||||||
renderWithProviders(<SignupPage />);
|
renderWithProviders(<SignupPage />);
|
||||||
expect(screen.getByText("Sign in")).toBeDefined();
|
expect(screen.getByText("Iniciar sesión")).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { useAuth } from "../auth-context";
|
||||||
import { invitesApi } from "../api";
|
import { invitesApi } from "../api";
|
||||||
import { authFormStyles as styles } from "../styles/auth-form";
|
import { authFormStyles as styles } from "../styles/auth-form";
|
||||||
import { LanguageSelector } from "../components/LanguageSelector";
|
import { LanguageSelector } from "../components/LanguageSelector";
|
||||||
|
import { useTranslation } from "../hooks/useTranslation";
|
||||||
|
|
||||||
function SignupContent() {
|
function SignupContent() {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
@ -25,6 +26,7 @@ function SignupContent() {
|
||||||
|
|
||||||
const { user, register } = useAuth();
|
const { user, register } = useAuth();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const t = useTranslation("auth");
|
||||||
|
|
||||||
// Redirect if already logged in
|
// Redirect if already logged in
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -51,11 +53,11 @@ function SignupContent() {
|
||||||
setInviteError("");
|
setInviteError("");
|
||||||
} else {
|
} else {
|
||||||
setInviteValid(false);
|
setInviteValid(false);
|
||||||
setInviteError(response.error || "Invalid invite code");
|
setInviteError(response.error || t("signup.invalidInviteCode"));
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
setInviteValid(false);
|
setInviteValid(false);
|
||||||
setInviteError("Failed to verify invite code");
|
setInviteError(t("signup.failedToVerify"));
|
||||||
} finally {
|
} finally {
|
||||||
setIsCheckingInvite(false);
|
setIsCheckingInvite(false);
|
||||||
}
|
}
|
||||||
|
|
@ -78,12 +80,12 @@ function SignupContent() {
|
||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
if (password !== confirmPassword) {
|
||||||
setError("Passwords do not match");
|
setError(t("signup.passwordsDoNotMatch"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password.length < 6) {
|
if (password.length < 6) {
|
||||||
setError("Password must be at least 6 characters");
|
setError(t("signup.passwordTooShort"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +95,7 @@ function SignupContent() {
|
||||||
await register(email, password, inviteCode.trim());
|
await register(email, password, inviteCode.trim());
|
||||||
router.push("/");
|
router.push("/");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Registration failed");
|
setError(err instanceof Error ? err.message : t("signup.registrationFailed"));
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +116,7 @@ function SignupContent() {
|
||||||
<div style={styles.container}>
|
<div style={styles.container}>
|
||||||
<div style={styles.card}>
|
<div style={styles.card}>
|
||||||
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.6)" }}>
|
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.6)" }}>
|
||||||
Checking invite code...
|
{t("signup.checkingInviteCode")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -132,8 +134,8 @@ function SignupContent() {
|
||||||
<div style={styles.container}>
|
<div style={styles.container}>
|
||||||
<div style={styles.card}>
|
<div style={styles.card}>
|
||||||
<div style={styles.header}>
|
<div style={styles.header}>
|
||||||
<h1 style={styles.title}>Join with Invite</h1>
|
<h1 style={styles.title}>{t("signup.title")}</h1>
|
||||||
<p style={styles.subtitle}>Enter your invite code to get started</p>
|
<p style={styles.subtitle}>{t("signup.subtitle")}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleInviteSubmit} style={styles.form}>
|
<form onSubmit={handleInviteSubmit} style={styles.form}>
|
||||||
|
|
@ -141,7 +143,7 @@ function SignupContent() {
|
||||||
|
|
||||||
<div style={styles.field}>
|
<div style={styles.field}>
|
||||||
<label htmlFor="inviteCode" style={styles.label}>
|
<label htmlFor="inviteCode" style={styles.label}>
|
||||||
Invite Code
|
{t("signup.inviteCode")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="inviteCode"
|
id="inviteCode"
|
||||||
|
|
@ -153,7 +155,7 @@ function SignupContent() {
|
||||||
setInviteValid(null);
|
setInviteValid(null);
|
||||||
}}
|
}}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
placeholder="word-word-00"
|
placeholder={t("signup.inviteCodePlaceholder")}
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
|
|
@ -165,7 +167,7 @@ function SignupContent() {
|
||||||
display: "block",
|
display: "block",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Ask your inviter for this code
|
{t("signup.inviteHint")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -177,14 +179,14 @@ function SignupContent() {
|
||||||
}}
|
}}
|
||||||
disabled={isCheckingInvite || !inviteCode.trim()}
|
disabled={isCheckingInvite || !inviteCode.trim()}
|
||||||
>
|
>
|
||||||
{isCheckingInvite ? "Checking..." : "Continue"}
|
{isCheckingInvite ? t("signup.checking") : t("signup.continue")}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p style={styles.footer}>
|
<p style={styles.footer}>
|
||||||
Already have an account?{" "}
|
{t("signup.alreadyHaveAccount")}{" "}
|
||||||
<a href="/login" style={styles.link}>
|
<a href="/login" style={styles.link}>
|
||||||
Sign in
|
{t("signup.signIn")}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -202,9 +204,9 @@ function SignupContent() {
|
||||||
<div style={styles.container}>
|
<div style={styles.container}>
|
||||||
<div style={styles.card}>
|
<div style={styles.card}>
|
||||||
<div style={styles.header}>
|
<div style={styles.header}>
|
||||||
<h1 style={styles.title}>Create account</h1>
|
<h1 style={styles.title}>{t("signup.createAccountTitle")}</h1>
|
||||||
<p style={styles.subtitle}>
|
<p style={styles.subtitle}>
|
||||||
Using invite:{" "}
|
{t("signup.createAccountSubtitle")}{" "}
|
||||||
<code
|
<code
|
||||||
style={{
|
style={{
|
||||||
background: "rgba(255,255,255,0.1)",
|
background: "rgba(255,255,255,0.1)",
|
||||||
|
|
@ -223,7 +225,7 @@ function SignupContent() {
|
||||||
|
|
||||||
<div style={styles.field}>
|
<div style={styles.field}>
|
||||||
<label htmlFor="email" style={styles.label}>
|
<label htmlFor="email" style={styles.label}>
|
||||||
Email
|
{t("signup.email")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
|
|
@ -231,7 +233,7 @@ function SignupContent() {
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
placeholder="you@example.com"
|
placeholder={t("signup.emailPlaceholder")}
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
|
|
@ -239,7 +241,7 @@ function SignupContent() {
|
||||||
|
|
||||||
<div style={styles.field}>
|
<div style={styles.field}>
|
||||||
<label htmlFor="password" style={styles.label}>
|
<label htmlFor="password" style={styles.label}>
|
||||||
Password
|
{t("signup.password")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
|
|
@ -247,14 +249,14 @@ function SignupContent() {
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
placeholder="••••••••"
|
placeholder={t("signup.passwordPlaceholder")}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={styles.field}>
|
<div style={styles.field}>
|
||||||
<label htmlFor="confirmPassword" style={styles.label}>
|
<label htmlFor="confirmPassword" style={styles.label}>
|
||||||
Confirm Password
|
{t("signup.confirmPassword")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
|
|
@ -262,7 +264,7 @@ function SignupContent() {
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
placeholder="••••••••"
|
placeholder={t("signup.confirmPasswordPlaceholder")}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -275,7 +277,7 @@ function SignupContent() {
|
||||||
}}
|
}}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
{isSubmitting ? "Creating account..." : "Create account"}
|
{isSubmitting ? t("signup.creatingAccount") : t("signup.createAccount")}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
@ -293,7 +295,7 @@ function SignupContent() {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Use a different invite code
|
{t("signup.useDifferentCode")}
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
44
frontend/locales/ca/auth.json
Normal file
44
frontend/locales/ca/auth.json
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"login": {
|
||||||
|
"title": "Benvingut de nou",
|
||||||
|
"subtitle": "Inicia sessió al teu compte",
|
||||||
|
"email": "Correu electrònic",
|
||||||
|
"password": "Contrasenya",
|
||||||
|
"emailPlaceholder": "tu@exemple.com",
|
||||||
|
"passwordPlaceholder": "••••••••",
|
||||||
|
"signIn": "Iniciar sessió",
|
||||||
|
"signingIn": "Iniciant sessió...",
|
||||||
|
"noAccount": "No tens un compte?",
|
||||||
|
"signUp": "Registra't",
|
||||||
|
"loginFailed": "Error en iniciar sessió"
|
||||||
|
},
|
||||||
|
"signup": {
|
||||||
|
"title": "Uneix-te amb Invitació",
|
||||||
|
"subtitle": "Introdueix el teu codi d'invitació per començar",
|
||||||
|
"inviteCode": "Codi d'Invitació",
|
||||||
|
"inviteCodePlaceholder": "paraula-paraula-00",
|
||||||
|
"inviteHint": "Demana aquest codi al teu convidant",
|
||||||
|
"checking": "Comprovant...",
|
||||||
|
"continue": "Continuar",
|
||||||
|
"checkingInviteCode": "Comprovant codi d'invitació...",
|
||||||
|
"createAccount": "Crear compte",
|
||||||
|
"createAccountTitle": "Crear compte",
|
||||||
|
"createAccountSubtitle": "Utilitzant invitació:",
|
||||||
|
"email": "Correu electrònic",
|
||||||
|
"emailPlaceholder": "tu@exemple.com",
|
||||||
|
"password": "Contrasenya",
|
||||||
|
"passwordPlaceholder": "••••••••",
|
||||||
|
"confirmPassword": "Confirmar Contrasenya",
|
||||||
|
"confirmPasswordPlaceholder": "••••••••",
|
||||||
|
"creatingAccount": "Creant compte...",
|
||||||
|
"alreadyHaveAccount": "Ja tens un compte?",
|
||||||
|
"signIn": "Iniciar sessió",
|
||||||
|
"useDifferentCode": "Utilitzar un codi d'invitació diferent",
|
||||||
|
"redirecting": "Redirigint...",
|
||||||
|
"passwordsDoNotMatch": "Les contrasenyes no coincideixen",
|
||||||
|
"passwordTooShort": "La contrasenya ha de tenir almenys 6 caràcters",
|
||||||
|
"invalidInviteCode": "Codi d'invitació invàlid",
|
||||||
|
"failedToVerify": "Error en verificar codi d'invitació",
|
||||||
|
"registrationFailed": "Error en registrar-se"
|
||||||
|
}
|
||||||
|
}
|
||||||
44
frontend/locales/en/auth.json
Normal file
44
frontend/locales/en/auth.json
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"login": {
|
||||||
|
"title": "Welcome back",
|
||||||
|
"subtitle": "Sign in to your account",
|
||||||
|
"email": "Email",
|
||||||
|
"password": "Password",
|
||||||
|
"emailPlaceholder": "you@example.com",
|
||||||
|
"passwordPlaceholder": "••••••••",
|
||||||
|
"signIn": "Sign in",
|
||||||
|
"signingIn": "Signing in...",
|
||||||
|
"noAccount": "Don't have an account?",
|
||||||
|
"signUp": "Sign up",
|
||||||
|
"loginFailed": "Login failed"
|
||||||
|
},
|
||||||
|
"signup": {
|
||||||
|
"title": "Join with Invite",
|
||||||
|
"subtitle": "Enter your invite code to get started",
|
||||||
|
"inviteCode": "Invite Code",
|
||||||
|
"inviteCodePlaceholder": "word-word-00",
|
||||||
|
"inviteHint": "Ask your inviter for this code",
|
||||||
|
"checking": "Checking...",
|
||||||
|
"continue": "Continue",
|
||||||
|
"checkingInviteCode": "Checking invite code...",
|
||||||
|
"createAccount": "Create account",
|
||||||
|
"createAccountTitle": "Create account",
|
||||||
|
"createAccountSubtitle": "Using invite:",
|
||||||
|
"email": "Email",
|
||||||
|
"emailPlaceholder": "you@example.com",
|
||||||
|
"password": "Password",
|
||||||
|
"passwordPlaceholder": "••••••••",
|
||||||
|
"confirmPassword": "Confirm Password",
|
||||||
|
"confirmPasswordPlaceholder": "••••••••",
|
||||||
|
"creatingAccount": "Creating account...",
|
||||||
|
"alreadyHaveAccount": "Already have an account?",
|
||||||
|
"signIn": "Sign in",
|
||||||
|
"useDifferentCode": "Use a different invite code",
|
||||||
|
"redirecting": "Redirecting...",
|
||||||
|
"passwordsDoNotMatch": "Passwords do not match",
|
||||||
|
"passwordTooShort": "Password must be at least 6 characters",
|
||||||
|
"invalidInviteCode": "Invalid invite code",
|
||||||
|
"failedToVerify": "Failed to verify invite code",
|
||||||
|
"registrationFailed": "Registration failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
44
frontend/locales/es/auth.json
Normal file
44
frontend/locales/es/auth.json
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"login": {
|
||||||
|
"title": "Bienvenido de nuevo",
|
||||||
|
"subtitle": "Inicia sesión en tu cuenta",
|
||||||
|
"email": "Correo electrónico",
|
||||||
|
"password": "Contraseña",
|
||||||
|
"emailPlaceholder": "tu@ejemplo.com",
|
||||||
|
"passwordPlaceholder": "••••••••",
|
||||||
|
"signIn": "Iniciar sesión",
|
||||||
|
"signingIn": "Iniciando sesión...",
|
||||||
|
"noAccount": "¿No tienes una cuenta?",
|
||||||
|
"signUp": "Regístrate",
|
||||||
|
"loginFailed": "Error al iniciar sesión"
|
||||||
|
},
|
||||||
|
"signup": {
|
||||||
|
"title": "Únete con Invitación",
|
||||||
|
"subtitle": "Ingresa tu código de invitación para comenzar",
|
||||||
|
"inviteCode": "Código de Invitación",
|
||||||
|
"inviteCodePlaceholder": "palabra-palabra-00",
|
||||||
|
"inviteHint": "Pide este código a tu invitador",
|
||||||
|
"checking": "Verificando...",
|
||||||
|
"continue": "Continuar",
|
||||||
|
"checkingInviteCode": "Verificando código de invitación...",
|
||||||
|
"createAccount": "Crear cuenta",
|
||||||
|
"createAccountTitle": "Crear cuenta",
|
||||||
|
"createAccountSubtitle": "Usando invitación:",
|
||||||
|
"email": "Correo electrónico",
|
||||||
|
"emailPlaceholder": "tu@ejemplo.com",
|
||||||
|
"password": "Contraseña",
|
||||||
|
"passwordPlaceholder": "••••••••",
|
||||||
|
"confirmPassword": "Confirmar Contraseña",
|
||||||
|
"confirmPasswordPlaceholder": "••••••••",
|
||||||
|
"creatingAccount": "Creando cuenta...",
|
||||||
|
"alreadyHaveAccount": "¿Ya tienes una cuenta?",
|
||||||
|
"signIn": "Iniciar sesión",
|
||||||
|
"useDifferentCode": "Usar un código de invitación diferente",
|
||||||
|
"redirecting": "Redirigiendo...",
|
||||||
|
"passwordsDoNotMatch": "Las contraseñas no coinciden",
|
||||||
|
"passwordTooShort": "La contraseña debe tener al menos 6 caracteres",
|
||||||
|
"invalidInviteCode": "Código de invitación inválido",
|
||||||
|
"failedToVerify": "Error al verificar código de invitación",
|
||||||
|
"registrationFailed": "Error al registrarse"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue