added english language

This commit is contained in:
SlimusMinus
2026-07-09 00:09:45 +03:00
parent 3b503623f6
commit fffcdaa526
33 changed files with 454 additions and 687 deletions

View File

@@ -1,4 +1,5 @@
import { useState, useCallback } from "react";
import { useTranslation } from 'react-i18next';
import CloseIcon from "./icons/CloseIcon";
import { useAuthContext } from "../context/AuthContext";
import { API_URL } from "../config/authConstants";
@@ -8,6 +9,7 @@ import { API_URL } from "../config/authConstants";
* Открывается когда неавторизованный пользователь пытается создать объявление.
*/
export default function AuthModal({ onClose }) {
const { t } = useTranslation();
const [mode, setMode] = useState("login"); // "login" | "register"
const { login: saveAuth } = useAuthContext();
@@ -17,7 +19,7 @@ export default function AuthModal({ onClose }) {
{/* Header */}
<div className="f-modal-head">
<span className="f-modal-title">
{mode === "login" ? "Вход в аккаунт" : "Регистрация"}
{mode === "login" ? t('auth.loginModalTitle') : t('auth.registerModalTitle')}
</span>
<button className="f-close-btn" onClick={onClose}>
<CloseIcon />
@@ -43,13 +45,14 @@ export default function AuthModal({ onClose }) {
// ─── Login form (inline modal) ──────────────────────────────────────────────
function LoginFormInline({ saveAuth, onClose, onSwitch }) {
const { t } = useTranslation();
const [login, setLogin] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const handleSubmit = async () => {
if (!login || !password) { setError("Введите логин и пароль"); return; }
if (!login || !password) { setError(t('auth.enterLoginPassword')); return; }
setLoading(true); setError("");
try {
@@ -61,7 +64,7 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data.message || "Неверный логин или пароль");
throw new Error(data.message || t('auth.invalidCredentials'));
}
const { token, user } = await res.json();
@@ -77,11 +80,11 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
return (
<>
<div className="f-m-group">
<label className="f-m-label">Логин</label>
<label className="f-m-label">{t('auth.loginLabel')}</label>
<input
className="f-m-field"
type="text"
placeholder="Ваш логин"
placeholder={t('auth.yourLogin')}
value={login}
onChange={e => { setLogin(e.target.value); setError(""); }}
autoComplete="off"
@@ -89,11 +92,11 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
</div>
<div className="f-m-group">
<label className="f-m-label">Пароль</label>
<label className="f-m-label">{t('auth.passwordLabel')}</label>
<input
className="f-m-field"
type="password"
placeholder="Ваш пароль"
placeholder={t('auth.yourPassword')}
value={password}
onChange={e => { setPassword(e.target.value); setError(""); }}
onKeyDown={e => e.key === "Enter" && handleSubmit()}
@@ -104,7 +107,7 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
<SubmitButton
loading={loading}
label="Войти"
label={t('auth.login')}
onClick={handleSubmit}
/>
@@ -112,7 +115,7 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
textAlign: "center", marginTop: 14,
color: "rgba(255,255,255,.45)", fontSize: ".85rem"
}}>
Нет аккаунта?{" "}
{t('auth.noAccount')}{" "}
<button
onClick={onSwitch}
style={{
@@ -121,7 +124,7 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
fontFamily: "var(--font)",
}}
>
Зарегистрироваться
{t('auth.register')}
</button>
</p>
</>
@@ -130,6 +133,7 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
// ─── Register form (inline modal) ──────────────────────────────────────────
function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
const { t } = useTranslation();
const [form, setForm] = useState({
name: "", login: "", password: "", password2: "",
});
@@ -144,11 +148,11 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
const validate = () => {
const e = {};
if (!form.name.trim()) e.name = "Введите имя";
if (!form.login.trim()) e.login = "Введите логин";
else if (form.login.length < 3) e.login = "Минимум 3 символа";
if (form.password.length < 6) e.password = "Минимум 6 символов";
if (form.password !== form.password2) e.password2 = "Пароли не совпадают";
if (!form.name.trim()) e.name = t('auth.validateNameRequired');
if (!form.login.trim()) e.login = t('auth.validateLoginRequired');
else if (form.login.length < 3) e.login = t('auth.loginMinChars');
if (form.password.length < 6) e.password = t('auth.validatePasswordMin');
if (form.password !== form.password2) e.password2 = t('auth.validatePasswordMatch');
setErrors(e);
return !Object.keys(e).length;
};
@@ -170,7 +174,7 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
});
const regData = await regRes.json();
if (!regRes.ok) throw new Error(regData.message || "Ошибка регистрации");
if (!regRes.ok) throw new Error(regData.message || t('auth.registrationError'));
// 2. Автоматический вход после регистрации
const loginRes = await fetch(`${API_URL}/api/v1/auth/login`, {
@@ -194,11 +198,11 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
return (
<>
<div className="f-m-group">
<label className="f-m-label">Имя</label>
<label className="f-m-label">{t('auth.nameLabel')}</label>
<input
className={`f-m-field${errors.name ? " err" : ""}`}
type="text"
placeholder="Как вас зовут?"
placeholder={t('auth.namePlaceholder')}
value={form.name}
onChange={e => setField("name", e.target.value)}
/>
@@ -206,11 +210,11 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
</div>
<div className="f-m-group">
<label className="f-m-label">Логин</label>
<label className="f-m-label">{t('auth.loginLabel')}</label>
<input
className={`f-m-field${errors.login ? " err" : ""}`}
type="text"
placeholder="Придумайте логин"
placeholder={t('auth.loginPlaceholder')}
value={form.login}
onChange={e => setField("login", e.target.value)}
/>
@@ -219,22 +223,22 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
<div className="f-m-row">
<div className="f-m-group">
<label className="f-m-label">Пароль</label>
<label className="f-m-label">{t('auth.passwordLabel')}</label>
<input
className={`f-m-field${errors.password ? " err" : ""}`}
type="password"
placeholder="Мин. 6 символов"
placeholder={t('auth.passwordMinShort')}
value={form.password}
onChange={e => setField("password", e.target.value)}
/>
{errors.password && <p className="f-err-msg">{errors.password}</p>}
</div>
<div className="f-m-group">
<label className="f-m-label">Повторите</label>
<label className="f-m-label">{t('auth.repeatLabel')}</label>
<input
className={`f-m-field${errors.password2 ? " err" : ""}`}
type="password"
placeholder="Ещё раз"
placeholder={t('auth.repeatPlaceholder')}
value={form.password2}
onChange={e => setField("password2", e.target.value)}
/>
@@ -246,7 +250,7 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
<SubmitButton
loading={loading}
label="Создать аккаунт"
label={t('auth.createAccountBtn').replace(' ✓', '')}
onClick={handleSubmit}
/>
@@ -254,7 +258,7 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
textAlign: "center", marginTop: 14,
color: "rgba(255,255,255,.45)", fontSize: ".85rem"
}}>
Уже есть аккаунт?{" "}
{t('auth.hasAccount')}{" "}
<button
onClick={onSwitch}
style={{
@@ -263,7 +267,7 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
fontFamily: "var(--font)",
}}
>
Войти
{t('auth.loginLink')}
</button>
</p>
</>