feat(auth): translate LoginPage + RegisterPage to 9 locales
Both auth forms now go through t('auth.login.*') and
t('auth.register.*'):
- Title + subtitle, email / password / username labels with
username placeholder and password-min-length placeholder.
- Submit button toggles between idle and loading states.
- "or" divider, "Continue with Google" button, and the
switch-to-other-form footer link.
- Inline validation errors: reserved username, username regex,
password length, plus the generic catch-all from the API.
Internal /editor and /login / /register links wrapped in
localize() so a Spanish user who registers stays at /es/editor
after success.
AboutPage (519 lines) deferred to its own commit — too dense for
the same change.
This commit is contained in:
parent
ecc35f72cb
commit
20b22b1398
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "Über PayPal spenden"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "Anmelden",
|
||||
"subtitle": "um zu Velxio fortzufahren",
|
||||
"email": "E-Mail",
|
||||
"password": "Passwort",
|
||||
"signIn": "Anmelden",
|
||||
"signingIn": "Anmelden…",
|
||||
"or": "oder",
|
||||
"continueGoogle": "Mit Google fortfahren",
|
||||
"noAccount": "Noch keinen Account?",
|
||||
"failed": "Anmeldung fehlgeschlagen."
|
||||
},
|
||||
"register": {
|
||||
"title": "Konto erstellen",
|
||||
"subtitle": "Kostenlos loslegen",
|
||||
"username": "Benutzername",
|
||||
"usernamePlaceholder": "z. B. alice",
|
||||
"passwordPlaceholder": "Mindestens 8 Zeichen",
|
||||
"create": "Konto erstellen",
|
||||
"creating": "Konto wird erstellt…",
|
||||
"haveAccount": "Schon einen Account?",
|
||||
"errors": {
|
||||
"reserved": "Dieser Benutzername ist reserviert.",
|
||||
"usernameRules": "Benutzername muss 3-30 Zeichen lang sein (a-z, 0-9, _, -)",
|
||||
"passwordTooShort": "Das Passwort muss mindestens 8 Zeichen haben.",
|
||||
"failed": "Registrierung fehlgeschlagen."
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "Sprachmodus",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "Donate via PayPal"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"subtitle": "to continue to Velxio",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"signIn": "Sign in",
|
||||
"signingIn": "Signing in…",
|
||||
"or": "or",
|
||||
"continueGoogle": "Continue with Google",
|
||||
"noAccount": "Don't have an account?",
|
||||
"failed": "Login failed."
|
||||
},
|
||||
"register": {
|
||||
"title": "Create account",
|
||||
"subtitle": "Start building for free",
|
||||
"username": "Username",
|
||||
"usernamePlaceholder": "e.g. alice",
|
||||
"passwordPlaceholder": "At least 8 characters",
|
||||
"create": "Create account",
|
||||
"creating": "Creating account…",
|
||||
"haveAccount": "Already have an account?",
|
||||
"errors": {
|
||||
"reserved": "That username is reserved.",
|
||||
"usernameRules": "Username must be 3-30 chars (a-z, 0-9, _, -)",
|
||||
"passwordTooShort": "Password must be at least 8 characters.",
|
||||
"failed": "Registration failed."
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "Language mode",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "Donar con PayPal"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "Iniciar sesión",
|
||||
"subtitle": "para continuar en Velxio",
|
||||
"email": "Correo electrónico",
|
||||
"password": "Contraseña",
|
||||
"signIn": "Iniciar sesión",
|
||||
"signingIn": "Iniciando sesión…",
|
||||
"or": "o",
|
||||
"continueGoogle": "Continuar con Google",
|
||||
"noAccount": "¿No tienes cuenta?",
|
||||
"failed": "Error al iniciar sesión."
|
||||
},
|
||||
"register": {
|
||||
"title": "Crear cuenta",
|
||||
"subtitle": "Empieza a construir gratis",
|
||||
"username": "Nombre de usuario",
|
||||
"usernamePlaceholder": "ej. alice",
|
||||
"passwordPlaceholder": "Al menos 8 caracteres",
|
||||
"create": "Crear cuenta",
|
||||
"creating": "Creando cuenta…",
|
||||
"haveAccount": "¿Ya tienes cuenta?",
|
||||
"errors": {
|
||||
"reserved": "Ese nombre de usuario está reservado.",
|
||||
"usernameRules": "El usuario debe tener 3-30 caracteres (a-z, 0-9, _, -)",
|
||||
"passwordTooShort": "La contraseña debe tener al menos 8 caracteres.",
|
||||
"failed": "Error al registrarse."
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "Modo de lenguaje",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "Faire un don via PayPal"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "Se connecter",
|
||||
"subtitle": "pour continuer sur Velxio",
|
||||
"email": "E-mail",
|
||||
"password": "Mot de passe",
|
||||
"signIn": "Se connecter",
|
||||
"signingIn": "Connexion…",
|
||||
"or": "ou",
|
||||
"continueGoogle": "Continuer avec Google",
|
||||
"noAccount": "Pas encore de compte ?",
|
||||
"failed": "Échec de la connexion."
|
||||
},
|
||||
"register": {
|
||||
"title": "Créer un compte",
|
||||
"subtitle": "Commencez à créer gratuitement",
|
||||
"username": "Nom d'utilisateur",
|
||||
"usernamePlaceholder": "ex. alice",
|
||||
"passwordPlaceholder": "Au moins 8 caractères",
|
||||
"create": "Créer un compte",
|
||||
"creating": "Création du compte…",
|
||||
"haveAccount": "Vous avez déjà un compte ?",
|
||||
"errors": {
|
||||
"reserved": "Ce nom d'utilisateur est réservé.",
|
||||
"usernameRules": "Le nom d'utilisateur doit faire 3 à 30 caractères (a-z, 0-9, _, -)",
|
||||
"passwordTooShort": "Le mot de passe doit faire au moins 8 caractères.",
|
||||
"failed": "L'inscription a échoué."
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "Mode de langage",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "Dona con PayPal"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "Accedi",
|
||||
"subtitle": "per continuare su Velxio",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"signIn": "Accedi",
|
||||
"signingIn": "Accesso…",
|
||||
"or": "o",
|
||||
"continueGoogle": "Continua con Google",
|
||||
"noAccount": "Non hai un account?",
|
||||
"failed": "Accesso non riuscito."
|
||||
},
|
||||
"register": {
|
||||
"title": "Crea account",
|
||||
"subtitle": "Inizia a costruire gratis",
|
||||
"username": "Nome utente",
|
||||
"usernamePlaceholder": "es. alice",
|
||||
"passwordPlaceholder": "Almeno 8 caratteri",
|
||||
"create": "Crea account",
|
||||
"creating": "Creazione account…",
|
||||
"haveAccount": "Hai già un account?",
|
||||
"errors": {
|
||||
"reserved": "Questo nome utente è riservato.",
|
||||
"usernameRules": "Il nome utente deve essere di 3-30 caratteri (a-z, 0-9, _, -)",
|
||||
"passwordTooShort": "La password deve avere almeno 8 caratteri.",
|
||||
"failed": "Registrazione non riuscita."
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "Modalità linguaggio",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "PayPal で寄付する"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "サインイン",
|
||||
"subtitle": "Velxio を続けるには",
|
||||
"email": "メールアドレス",
|
||||
"password": "パスワード",
|
||||
"signIn": "サインイン",
|
||||
"signingIn": "サインイン中…",
|
||||
"or": "または",
|
||||
"continueGoogle": "Google で続ける",
|
||||
"noAccount": "アカウントをお持ちでないですか?",
|
||||
"failed": "サインインに失敗しました。"
|
||||
},
|
||||
"register": {
|
||||
"title": "アカウント作成",
|
||||
"subtitle": "無料で始めましょう",
|
||||
"username": "ユーザー名",
|
||||
"usernamePlaceholder": "例:alice",
|
||||
"passwordPlaceholder": "8 文字以上",
|
||||
"create": "アカウント作成",
|
||||
"creating": "アカウントを作成中…",
|
||||
"haveAccount": "すでにアカウントをお持ちですか?",
|
||||
"errors": {
|
||||
"reserved": "このユーザー名は予約されています。",
|
||||
"usernameRules": "ユーザー名は 3〜30 文字(a-z、0-9、_、-)で指定してください",
|
||||
"passwordTooShort": "パスワードは 8 文字以上にしてください。",
|
||||
"failed": "登録に失敗しました。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "言語モード",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "Doar via PayPal"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "Entrar",
|
||||
"subtitle": "para continuar no Velxio",
|
||||
"email": "E-mail",
|
||||
"password": "Senha",
|
||||
"signIn": "Entrar",
|
||||
"signingIn": "Entrando…",
|
||||
"or": "ou",
|
||||
"continueGoogle": "Continuar com Google",
|
||||
"noAccount": "Não tem uma conta?",
|
||||
"failed": "Falha ao entrar."
|
||||
},
|
||||
"register": {
|
||||
"title": "Criar conta",
|
||||
"subtitle": "Comece a construir grátis",
|
||||
"username": "Nome de usuário",
|
||||
"usernamePlaceholder": "ex. alice",
|
||||
"passwordPlaceholder": "Pelo menos 8 caracteres",
|
||||
"create": "Criar conta",
|
||||
"creating": "Criando conta…",
|
||||
"haveAccount": "Já tem uma conta?",
|
||||
"errors": {
|
||||
"reserved": "Este nome de usuário é reservado.",
|
||||
"usernameRules": "O usuário deve ter 3-30 caracteres (a-z, 0-9, _, -)",
|
||||
"passwordTooShort": "A senha deve ter pelo menos 8 caracteres.",
|
||||
"failed": "Falha no cadastro."
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "Modo de linguagem",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "Пожертвовать через PayPal"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "Войти",
|
||||
"subtitle": "чтобы продолжить в Velxio",
|
||||
"email": "Email",
|
||||
"password": "Пароль",
|
||||
"signIn": "Войти",
|
||||
"signingIn": "Вход…",
|
||||
"or": "или",
|
||||
"continueGoogle": "Продолжить с Google",
|
||||
"noAccount": "Нет аккаунта?",
|
||||
"failed": "Не удалось войти."
|
||||
},
|
||||
"register": {
|
||||
"title": "Создать аккаунт",
|
||||
"subtitle": "Начните бесплатно",
|
||||
"username": "Имя пользователя",
|
||||
"usernamePlaceholder": "напр. alice",
|
||||
"passwordPlaceholder": "Минимум 8 символов",
|
||||
"create": "Создать аккаунт",
|
||||
"creating": "Создание аккаунта…",
|
||||
"haveAccount": "Уже есть аккаунт?",
|
||||
"errors": {
|
||||
"reserved": "Это имя пользователя зарезервировано.",
|
||||
"usernameRules": "Имя пользователя должно быть 3-30 символов (a-z, 0-9, _, -)",
|
||||
"passwordTooShort": "Пароль должен быть не менее 8 символов.",
|
||||
"failed": "Регистрация не удалась."
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "Режим языка",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@
|
|||
"ctaPaypal": "通过 PayPal 捐赠"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
"title": "登录",
|
||||
"subtitle": "继续使用 Velxio",
|
||||
"email": "邮箱",
|
||||
"password": "密码",
|
||||
"signIn": "登录",
|
||||
"signingIn": "正在登录…",
|
||||
"or": "或",
|
||||
"continueGoogle": "使用 Google 继续",
|
||||
"noAccount": "还没有账户?",
|
||||
"failed": "登录失败。"
|
||||
},
|
||||
"register": {
|
||||
"title": "创建账户",
|
||||
"subtitle": "免费开始构建",
|
||||
"username": "用户名",
|
||||
"usernamePlaceholder": "如 alice",
|
||||
"passwordPlaceholder": "至少 8 个字符",
|
||||
"create": "创建账户",
|
||||
"creating": "正在创建账户…",
|
||||
"haveAccount": "已有账户?",
|
||||
"errors": {
|
||||
"reserved": "该用户名已被保留。",
|
||||
"usernameRules": "用户名必须为 3-30 个字符(a-z、0-9、_、-)",
|
||||
"passwordTooShort": "密码至少需要 8 个字符。",
|
||||
"failed": "注册失败。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"toolbar": {
|
||||
"languageMode": "语言模式",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import { useState } from 'react';
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { login, initiateGoogleLogin } from '../services/authService';
|
||||
import { useAuthStore } from '../store/useAuthStore';
|
||||
import { useLocalizedHref } from '../i18n/useLocalizedNavigate';
|
||||
import { useSEO } from '../utils/useSEO';
|
||||
import { trackLogin } from '../utils/analytics';
|
||||
|
||||
export const LoginPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const localize = useLocalizedHref();
|
||||
useSEO({
|
||||
title: 'Sign In — Velxio',
|
||||
description:
|
||||
|
|
@ -29,9 +33,9 @@ export const LoginPage: React.FC = () => {
|
|||
const user = await login(email, password);
|
||||
trackLogin('email');
|
||||
setUser(user);
|
||||
navigate(searchParams.get('redirect') || '/');
|
||||
navigate(searchParams.get('redirect') || localize('/'));
|
||||
} catch (err: any) {
|
||||
setError(err?.response?.data?.detail || 'Login failed.');
|
||||
setError(err?.response?.data?.detail || t('auth.login.failed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
|
@ -40,14 +44,14 @@ export const LoginPage: React.FC = () => {
|
|||
return (
|
||||
<div className="ap-page">
|
||||
<div className="ap-card">
|
||||
<h1 className="ap-card-title">Sign in</h1>
|
||||
<p className="ap-card-sub">to continue to Velxio</p>
|
||||
<h1 className="ap-card-title">{t('auth.login.title')}</h1>
|
||||
<p className="ap-card-sub">{t('auth.login.subtitle')}</p>
|
||||
|
||||
{error && <div className="ap-error">{error}</div>}
|
||||
|
||||
<form onSubmit={handleSubmit} className="ap-form">
|
||||
<div className="ap-field">
|
||||
<label className="ap-label">Email</label>
|
||||
<label className="ap-label">{t('auth.login.email')}</label>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
|
|
@ -58,7 +62,7 @@ export const LoginPage: React.FC = () => {
|
|||
/>
|
||||
</div>
|
||||
<div className="ap-field">
|
||||
<label className="ap-label">Password</label>
|
||||
<label className="ap-label">{t('auth.login.password')}</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
|
|
@ -68,11 +72,11 @@ export const LoginPage: React.FC = () => {
|
|||
/>
|
||||
</div>
|
||||
<button type="submit" disabled={loading} className="ap-btn-primary">
|
||||
{loading ? 'Signing in…' : 'Sign in'}
|
||||
{loading ? t('auth.login.signingIn') : t('auth.login.signIn')}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="ap-divider">or</div>
|
||||
<div className="ap-divider">{t('auth.login.or')}</div>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
|
|
@ -99,13 +103,13 @@ export const LoginPage: React.FC = () => {
|
|||
d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.18 1.48-4.97 2.31-8.16 2.31-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
|
||||
/>
|
||||
</svg>
|
||||
Continue with Google
|
||||
{t('auth.login.continueGoogle')}
|
||||
</button>
|
||||
|
||||
<p className="ap-footer">
|
||||
Don't have an account?{' '}
|
||||
<Link to="/register" className="ap-link">
|
||||
Sign up
|
||||
{t('auth.login.noAccount')}{' '}
|
||||
<Link to={localize('/register')} className="ap-link">
|
||||
{t('header.auth.signUp')}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
import { useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { register, initiateGoogleLogin } from '../services/authService';
|
||||
import { useAuthStore } from '../store/useAuthStore';
|
||||
import { useLocalizedHref } from '../i18n/useLocalizedNavigate';
|
||||
import { RESERVED_USERNAMES } from '../utils/reservedUsernames';
|
||||
import { useSEO } from '../utils/useSEO';
|
||||
import { trackSignUp } from '../utils/analytics';
|
||||
|
||||
export const RegisterPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const localize = useLocalizedHref();
|
||||
useSEO({
|
||||
title: 'Create Account — Velxio',
|
||||
description:
|
||||
|
|
@ -24,8 +28,8 @@ export const RegisterPage: React.FC = () => {
|
|||
|
||||
const validateUsername = (name: string): string | null => {
|
||||
const lower = name.toLowerCase();
|
||||
if (RESERVED_USERNAMES.has(lower)) return 'That username is reserved.';
|
||||
if (!/^[a-z0-9_-]{3,30}$/.test(lower)) return 'Username must be 3-30 chars (a-z, 0-9, _, -)';
|
||||
if (RESERVED_USERNAMES.has(lower)) return t('auth.register.errors.reserved');
|
||||
if (!/^[a-z0-9_-]{3,30}$/.test(lower)) return t('auth.register.errors.usernameRules');
|
||||
return null;
|
||||
};
|
||||
|
||||
|
|
@ -38,7 +42,7 @@ export const RegisterPage: React.FC = () => {
|
|||
return;
|
||||
}
|
||||
if (password.length < 8) {
|
||||
setError('Password must be at least 8 characters.');
|
||||
setError(t('auth.register.errors.passwordTooShort'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -47,9 +51,9 @@ export const RegisterPage: React.FC = () => {
|
|||
const user = await register(username.toLowerCase(), email, password);
|
||||
trackSignUp('email');
|
||||
setUser(user);
|
||||
navigate('/editor');
|
||||
navigate(localize('/editor'));
|
||||
} catch (err: any) {
|
||||
setError(err?.response?.data?.detail || 'Registration failed.');
|
||||
setError(err?.response?.data?.detail || t('auth.register.errors.failed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
|
@ -58,14 +62,14 @@ export const RegisterPage: React.FC = () => {
|
|||
return (
|
||||
<div className="ap-page">
|
||||
<div className="ap-card">
|
||||
<h1 className="ap-card-title">Create account</h1>
|
||||
<p className="ap-card-sub">Start building for free</p>
|
||||
<h1 className="ap-card-title">{t('auth.register.title')}</h1>
|
||||
<p className="ap-card-sub">{t('auth.register.subtitle')}</p>
|
||||
|
||||
{error && <div className="ap-error">{error}</div>}
|
||||
|
||||
<form onSubmit={handleSubmit} className="ap-form">
|
||||
<div className="ap-field">
|
||||
<label className="ap-label">Username</label>
|
||||
<label className="ap-label">{t('auth.register.username')}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
|
|
@ -73,11 +77,11 @@ export const RegisterPage: React.FC = () => {
|
|||
required
|
||||
className="ap-input"
|
||||
autoFocus
|
||||
placeholder="e.g. alice"
|
||||
placeholder={t('auth.register.usernamePlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
<div className="ap-field">
|
||||
<label className="ap-label">Email</label>
|
||||
<label className="ap-label">{t('auth.login.email')}</label>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
|
|
@ -87,22 +91,22 @@ export const RegisterPage: React.FC = () => {
|
|||
/>
|
||||
</div>
|
||||
<div className="ap-field">
|
||||
<label className="ap-label">Password</label>
|
||||
<label className="ap-label">{t('auth.login.password')}</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
className="ap-input"
|
||||
placeholder="At least 8 characters"
|
||||
placeholder={t('auth.register.passwordPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" disabled={loading} className="ap-btn-primary">
|
||||
{loading ? 'Creating account…' : 'Create account'}
|
||||
{loading ? t('auth.register.creating') : t('auth.register.create')}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="ap-divider">or</div>
|
||||
<div className="ap-divider">{t('auth.login.or')}</div>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
|
|
@ -129,13 +133,13 @@ export const RegisterPage: React.FC = () => {
|
|||
d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.18 1.48-4.97 2.31-8.16 2.31-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
|
||||
/>
|
||||
</svg>
|
||||
Continue with Google
|
||||
{t('auth.login.continueGoogle')}
|
||||
</button>
|
||||
|
||||
<p className="ap-footer">
|
||||
Already have an account?{' '}
|
||||
<Link to="/login" className="ap-link">
|
||||
Sign in
|
||||
{t('auth.register.haveAccount')}{' '}
|
||||
<Link to={localize('/login')} className="ap-link">
|
||||
{t('header.auth.signIn')}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue