diff --git a/frontend/src/i18n/locales/de/common.json b/frontend/src/i18n/locales/de/common.json index 87916db8..2dc2c5d9 100644 --- a/frontend/src/i18n/locales/de/common.json +++ b/frontend/src/i18n/locales/de/common.json @@ -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", diff --git a/frontend/src/i18n/locales/en/common.json b/frontend/src/i18n/locales/en/common.json index bb36719a..e0921587 100644 --- a/frontend/src/i18n/locales/en/common.json +++ b/frontend/src/i18n/locales/en/common.json @@ -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", diff --git a/frontend/src/i18n/locales/es/common.json b/frontend/src/i18n/locales/es/common.json index 10d10df5..723edfaa 100644 --- a/frontend/src/i18n/locales/es/common.json +++ b/frontend/src/i18n/locales/es/common.json @@ -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", diff --git a/frontend/src/i18n/locales/fr/common.json b/frontend/src/i18n/locales/fr/common.json index 756d53e3..db45d76a 100644 --- a/frontend/src/i18n/locales/fr/common.json +++ b/frontend/src/i18n/locales/fr/common.json @@ -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", diff --git a/frontend/src/i18n/locales/it/common.json b/frontend/src/i18n/locales/it/common.json index b5b05cc5..d9d792cb 100644 --- a/frontend/src/i18n/locales/it/common.json +++ b/frontend/src/i18n/locales/it/common.json @@ -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", diff --git a/frontend/src/i18n/locales/ja/common.json b/frontend/src/i18n/locales/ja/common.json index 8543d946..1980efe9 100644 --- a/frontend/src/i18n/locales/ja/common.json +++ b/frontend/src/i18n/locales/ja/common.json @@ -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": "言語モード", diff --git a/frontend/src/i18n/locales/pt-br/common.json b/frontend/src/i18n/locales/pt-br/common.json index a78d25b2..9e03cb6a 100644 --- a/frontend/src/i18n/locales/pt-br/common.json +++ b/frontend/src/i18n/locales/pt-br/common.json @@ -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", diff --git a/frontend/src/i18n/locales/ru/common.json b/frontend/src/i18n/locales/ru/common.json index da86c765..05a23322 100644 --- a/frontend/src/i18n/locales/ru/common.json +++ b/frontend/src/i18n/locales/ru/common.json @@ -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": "Режим языка", diff --git a/frontend/src/i18n/locales/zh-cn/common.json b/frontend/src/i18n/locales/zh-cn/common.json index 661df906..d0f41114 100644 --- a/frontend/src/i18n/locales/zh-cn/common.json +++ b/frontend/src/i18n/locales/zh-cn/common.json @@ -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": "语言模式", diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 2f5aa52c..58db395d 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -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 (
-

Sign in

-

to continue to Velxio

+

{t('auth.login.title')}

+

{t('auth.login.subtitle')}

{error &&
{error}
}
- + { />
- + { />
-
or
+
{t('auth.login.or')}

- Don't have an account?{' '} - - Sign up + {t('auth.login.noAccount')}{' '} + + {t('header.auth.signUp')}

diff --git a/frontend/src/pages/RegisterPage.tsx b/frontend/src/pages/RegisterPage.tsx index 10cdb84a..cfcdffaa 100644 --- a/frontend/src/pages/RegisterPage.tsx +++ b/frontend/src/pages/RegisterPage.tsx @@ -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 (
-

Create account

-

Start building for free

+

{t('auth.register.title')}

+

{t('auth.register.subtitle')}

{error &&
{error}
}
- + { required className="ap-input" autoFocus - placeholder="e.g. alice" + placeholder={t('auth.register.usernamePlaceholder')} />
- + { />
- + setPassword(e.target.value)} required className="ap-input" - placeholder="At least 8 characters" + placeholder={t('auth.register.passwordPlaceholder')} />
-
or
+
{t('auth.login.or')}

- Already have an account?{' '} - - Sign in + {t('auth.register.haveAccount')}{' '} + + {t('header.auth.signIn')}