diff --git a/frontend/app/[locale]/contacts/page.tsx b/frontend/app/[locale]/contacts/page.tsx
index 995129f7..d40e55e0 100644
--- a/frontend/app/[locale]/contacts/page.tsx
+++ b/frontend/app/[locale]/contacts/page.tsx
@@ -1,17 +1,29 @@
-export const metadata = {
- title: "Contacts | DevLovers",
- description:
- "Get in touch with the DevLovers team for questions, feedback, or collaboration.",
-};
+import { getTranslations } from "next-intl/server";
+
+export async function generateMetadata({
+ params,
+}: {
+ params: Promise<{ locale: string }>;
+}) {
+ const { locale } = await params;
+ const t = await getTranslations({ locale, namespace: "contacts" });
+
+ return {
+ title: t("metaTitle"),
+ description: t("metaDescription"),
+ };
+}
+
+export default async function ContactsPage() {
+ const t = await getTranslations("contacts");
-export default function ContactsPage() {
return (
- Contacts
- We’d love to hear from you! 💬
+ {t("title")}
+ {t("subtitle")} 💬
-
- 📧 Email:{" "}
+ 📧 {t("email")}{" "}
-
- 💼 LinkedIn:{" "}
+ 💼 {t("linkedin")}{" "}
-
- 🧑💻 GitHub:{" "}
+ 🧑💻 {t("github")}{" "}
);
-}
\ No newline at end of file
+}
diff --git a/frontend/app/[locale]/dashboard/page.tsx b/frontend/app/[locale]/dashboard/page.tsx
index f40c3e60..5a993a3d 100644
--- a/frontend/app/[locale]/dashboard/page.tsx
+++ b/frontend/app/[locale]/dashboard/page.tsx
@@ -1,5 +1,6 @@
import { redirect } from '@/i18n/routing';
import { Link } from '@/i18n/routing'
+import { getTranslations } from 'next-intl/server';
import { getCurrentUser } from '@/lib/auth';
import { getUserProfile } from '@/db/queries/users';
import { getUserQuizStats } from '@/db/queries/quiz';
@@ -9,10 +10,19 @@ import { StatsCard } from '@/components/dashboard/StatsCard';
import { QuizSavedBanner } from '@/components/dashboard/QuizSavedBanner';
import { PostAuthQuizSync } from "@/components/auth/PostAuthQuizSync";
-export const metadata = {
- title: 'Dashboard | DevLovers',
- description: 'Track your progress and quiz performance.',
-};
+export async function generateMetadata({
+ params,
+}: {
+ params: Promise<{ locale: string }>;
+}) {
+ const { locale } = await params;
+ const t = await getTranslations({ locale, namespace: 'dashboard' });
+
+ return {
+ title: t('metaTitle'),
+ description: t('metaDescription'),
+ };
+}
export default async function DashboardPage({ params }: { params: Promise<{ locale: string }> }) {
const session = await getCurrentUser();
@@ -22,6 +32,8 @@ export default async function DashboardPage({ params }: { params: Promise<{ loca
const user = await getUserProfile(session.id);
if (!user) { redirect({ href: '/login', locale }); return; }
+ const t = await getTranslations('dashboard');
+
const attempts = await getUserQuizStats(session.id);
const totalAttempts = attempts.length;
@@ -36,7 +48,7 @@ export default async function DashboardPage({ params }: { params: Promise<{ loca
const lastActiveDate =
totalAttempts > 0
- ? new Date(attempts[0].completedAt).toLocaleDateString('uk-UA')
+ ? new Date(attempts[0].completedAt).toLocaleDateString(locale)
: null;
const userForDisplay = {
@@ -74,21 +86,21 @@ export default async function DashboardPage({ params }: { params: Promise<{ loca
- Dashboard
+ {t('title')}
- Welcome back to your training ground.
+ {t('subtitle')}
- Support & Feedback
+ {t('supportLink')}
diff --git a/frontend/components/auth/AuthProvidersBlock.tsx b/frontend/components/auth/AuthProvidersBlock.tsx
index e54e3f64..21d0f053 100644
--- a/frontend/components/auth/AuthProvidersBlock.tsx
+++ b/frontend/components/auth/AuthProvidersBlock.tsx
@@ -1,6 +1,11 @@
+"use client";
+
+import { useTranslations } from "next-intl";
import { OAuthButtons } from "@/components/auth/OAuthButtons";
export function AuthProvidersBlock() {
+ const t = useTranslations("auth");
+
return (
<>
@@ -8,7 +13,7 @@ export function AuthProvidersBlock() {
diff --git a/frontend/components/auth/ForgotPasswordForm.tsx b/frontend/components/auth/ForgotPasswordForm.tsx
index 1bf6206e..870ed55c 100644
--- a/frontend/components/auth/ForgotPasswordForm.tsx
+++ b/frontend/components/auth/ForgotPasswordForm.tsx
@@ -1,6 +1,7 @@
"use client";
import { useState } from "react";
+import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { AuthShell } from "@/components/auth/AuthShell";
import { AuthErrorBanner } from "@/components/auth/AuthErrorBanner";
@@ -8,6 +9,7 @@ import { AuthSuccessBanner } from "@/components/auth/AuthSuccessBanner";
import { EmailField } from "@/components/auth/fields/EmailField";
export function ForgotPasswordForm() {
+ const t = useTranslations("auth.forgotPassword");
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [emailSent, setEmailSent] = useState(false);
@@ -37,34 +39,30 @@ export function ForgotPasswordForm() {
);
if (!res.ok) {
- setError(
- "Failed to send reset email. Please try again."
- );
+ setError(t("errors.sendFailed"));
return;
}
setEmailSent(true);
} catch {
- setError(
- "Network error. Please check your connection."
- );
+ setError(t("errors.networkError"));
} finally {
setLoading(false);
}
}
return (
-
+
{emailSent ? (
- We’ve sent a password reset link to{" "}
+ {t("emailSent")}{" "}
{email}.
- Please check your inbox.
+ {t("checkInbox")}
>
}
@@ -82,9 +80,7 @@ export function ForgotPasswordForm() {
disabled={loading}
className="w-full"
>
- {loading
- ? "Sending..."
- : "Send reset link"}
+ {loading ? t("submitting") : t("submit")}
)}
diff --git a/frontend/components/auth/LoginForm.tsx b/frontend/components/auth/LoginForm.tsx
index d6946a4f..baec41b6 100644
--- a/frontend/components/auth/LoginForm.tsx
+++ b/frontend/components/auth/LoginForm.tsx
@@ -1,6 +1,7 @@
"use client";
import { useState } from "react";
+import { useTranslations } from "next-intl";
import { Link } from "@/i18n/routing";
import { Button } from "@/components/ui/button";
import { AuthShell } from "@/components/auth/AuthShell";
@@ -20,6 +21,7 @@ export function LoginForm({
locale,
returnTo,
}: LoginFormProps) {
+ const t = useTranslations("auth.login");
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] =
useState(null);
@@ -58,11 +60,9 @@ export function LoginForm({
setErrorCode(data?.code ?? null);
if (data?.code === "EMAIL_NOT_VERIFIED") {
- setErrorMessage(
- "Your email address is not verified. Please check your inbox."
- );
+ setErrorMessage(t("errors.emailNotVerified"));
} else {
- setErrorMessage("Invalid email or password");
+ setErrorMessage(t("errors.invalidCredentials"));
}
return;
}
@@ -71,9 +71,7 @@ export function LoginForm({
returnTo || `/${locale}/dashboard`;
} catch (err) {
console.error("Login request failed:", err);
- setErrorMessage(
- "Network error. Please check your connection and try again."
- );
+ setErrorMessage(t("errors.networkError"));
setErrorCode(null);
} finally {
setLoading(false);
@@ -94,10 +92,7 @@ export function LoginForm({
if (!res.ok) {
setErrorCode(data?.code ?? "RESEND_FAILED");
- setErrorMessage(
- data?.error ??
- "Failed to resend verification email. Please try again."
- );
+ setErrorMessage(data?.error ?? t("errors.resendFailed"));
return;
}
@@ -107,18 +102,16 @@ export function LoginForm({
} catch (err) {
console.error("Resend verification failed:", err);
setErrorCode("NETWORK_ERROR");
- setErrorMessage(
- "Network error. Please check your connection and try again."
- );
+ setErrorMessage(t("errors.networkError"));
}
}
return (
- Don’t have an account?{" "}
+ {t("noAccount")}{" "}
- Sign up
+ {t("signupLink")}
}
@@ -152,7 +145,7 @@ export function LoginForm({
}
className="text-sm underline text-gray-600"
>
- Forgot password?
+ {t("forgotPassword")}
@@ -161,7 +154,7 @@ export function LoginForm({
message={errorMessage}
actionLabel={
errorCode === "EMAIL_NOT_VERIFIED"
- ? "Resend verification email"
+ ? t("resendVerification")
: undefined
}
onAction={
@@ -176,7 +169,7 @@ export function LoginForm({
- Verification successfully sent to{" "}
+ {t("verificationSent")}{" "}
{email}
>
}
@@ -188,7 +181,7 @@ export function LoginForm({
disabled={loading}
className="w-full"
>
- {loading ? "Logging in..." : "Log in"}
+ {loading ? t("submitting") : t("submit")}
diff --git a/frontend/components/auth/ResetPasswordForm.tsx b/frontend/components/auth/ResetPasswordForm.tsx
index 17dcd505..e940a79e 100644
--- a/frontend/components/auth/ResetPasswordForm.tsx
+++ b/frontend/components/auth/ResetPasswordForm.tsx
@@ -1,6 +1,7 @@
"use client";
import { useState } from "react";
+import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { AuthShell } from "@/components/auth/AuthShell";
import { AuthErrorBanner } from "@/components/auth/AuthErrorBanner";
@@ -14,6 +15,7 @@ type ResetPasswordFormProps = {
export function ResetPasswordForm({
token,
}: ResetPasswordFormProps) {
+ const t = useTranslations("auth.resetPassword");
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(false);
@@ -43,28 +45,22 @@ export function ResetPasswordForm({
);
if (!res.ok) {
- setError(
- "Failed to reset password. The link may be invalid or expired."
- );
+ setError(t("errors.resetFailed"));
return;
}
setSuccess(true);
} catch {
- setError(
- "Network error. Please try again."
- );
+ setError(t("errors.networkError"));
} finally {
setLoading(false);
}
}
return (
-
+
{success ? (
-
+
) : (
)}
diff --git a/frontend/components/auth/SignupForm.tsx b/frontend/components/auth/SignupForm.tsx
index 96dc4d46..d798a042 100644
--- a/frontend/components/auth/SignupForm.tsx
+++ b/frontend/components/auth/SignupForm.tsx
@@ -1,6 +1,7 @@
"use client";
import { useState } from "react";
+import { useTranslations } from "next-intl";
import { Link } from "@/i18n/routing";
import { Button } from "@/components/ui/button";
import { AuthShell } from "@/components/auth/AuthShell";
@@ -20,6 +21,7 @@ export function SignupForm({
locale,
returnTo,
}: SignupFormProps) {
+ const t = useTranslations("auth.signup");
const [loading, setLoading] = useState(false);
const [error, setError] =
useState(null);
@@ -52,10 +54,7 @@ export function SignupForm({
const data = await res.json().catch(() => null);
if (!res.ok) {
- setError(
- data?.error ??
- "Failed to sign up. Please try again."
- );
+ setError(data?.error ?? t("errors.signupFailed"));
return;
}
@@ -67,9 +66,7 @@ export function SignupForm({
window.location.href =
returnTo || `/${locale}/dashboard`;
} catch {
- setError(
- "Network error. Please check your connection and try again."
- );
+ setError(t("errors.networkError"));
} finally {
setLoading(false);
}
@@ -77,11 +74,11 @@ export function SignupForm({
return (
- Already have an account?{" "}
+ {t("hasAccount")}{" "}
- Log in
+ {t("loginLink")}
)
@@ -107,13 +104,12 @@ export function SignupForm({
message={
<>
- We’ve sent a verification email to{" "}
+ {t("verificationSent")}{" "}
{email}.
- Please check your inbox and click the
- verification link to activate your account.
+ {t("checkInbox")}
>
}
@@ -128,7 +124,7 @@ export function SignupForm({
}
className="inline-block underline"
>
- Go to login
+ {t("goToLogin")}
}
/>
@@ -149,7 +145,7 @@ export function SignupForm({
disabled={loading}
className="w-full"
>
- {loading ? "Signing up..." : "Sign up"}
+ {loading ? t("submitting") : t("submit")}
)}
diff --git a/frontend/components/auth/fields/EmailField.tsx b/frontend/components/auth/fields/EmailField.tsx
index 86fe4ae0..d0613826 100644
--- a/frontend/components/auth/fields/EmailField.tsx
+++ b/frontend/components/auth/fields/EmailField.tsx
@@ -1,3 +1,7 @@
+"use client";
+
+import { useTranslations } from "next-intl";
+
type EmailFieldProps = {
onChange?: (value: string) => void;
};
@@ -5,11 +9,13 @@ type EmailFieldProps = {
export function EmailField({
onChange,
}: EmailFieldProps) {
+ const t = useTranslations("auth.fields");
+
return (
diff --git a/frontend/components/auth/fields/PasswordField.tsx b/frontend/components/auth/fields/PasswordField.tsx
index 42a5743f..b4c101d2 100644
--- a/frontend/components/auth/fields/PasswordField.tsx
+++ b/frontend/components/auth/fields/PasswordField.tsx
@@ -1,18 +1,18 @@
"use client";
import { useState } from "react";
+import { useTranslations } from "next-intl";
type PasswordFieldProps = {
name?: string;
- placeholder?: string;
minLength?: number;
};
export function PasswordField({
name = "password",
- placeholder = "Password",
minLength,
}: PasswordFieldProps) {
+ const t = useTranslations("auth.fields");
const [visible, setVisible] = useState(false);
return (
@@ -20,7 +20,7 @@ export function PasswordField({
setVisible(v => !v)}
className="absolute inset-y-0 right-2 flex items-center text-sm text-gray-500"
>
- {visible ? "Hide" : "Show"}
+ {visible ? t("hide") : t("show")}
);
diff --git a/frontend/components/dashboard/ProfileCard.tsx b/frontend/components/dashboard/ProfileCard.tsx
index 06b494fe..1eb2b638 100644
--- a/frontend/components/dashboard/ProfileCard.tsx
+++ b/frontend/components/dashboard/ProfileCard.tsx
@@ -1,3 +1,7 @@
+"use client";
+
+import { useTranslations } from "next-intl";
+
interface ProfileCardProps {
user: {
name: string | null;
@@ -6,9 +10,12 @@ interface ProfileCardProps {
points: number;
createdAt: Date | null;
};
+ locale: string;
}
-export function ProfileCard({ user }: ProfileCardProps) {
+export function ProfileCard({ user, locale }: ProfileCardProps) {
+ const t = useTranslations("dashboard.profile");
+
const cardStyles = `
relative overflow-hidden rounded-[2rem]
border border-slate-200/70 dark:border-slate-700/80
@@ -35,14 +42,14 @@ export function ProfileCard({ user }: ProfileCardProps) {
id="profile-heading"
className="text-2xl font-bold text-slate-800 dark:text-slate-100"
>
- {user.name || 'Developer'}
+ {user.name || t("defaultName")}
{user.email}
- {user.role || 'user'}
+ {user.role || t("defaultRole")}
@@ -50,7 +57,7 @@ export function ProfileCard({ user }: ProfileCardProps) {
-
- Total Points
+ {t("totalPoints")}
-
@@ -59,11 +66,11 @@ export function ProfileCard({ user }: ProfileCardProps) {
-
- Joined
+ {t("joined")}
-
{user.createdAt
- ? new Date(user.createdAt).toLocaleDateString('uk-UA')
+ ? new Date(user.createdAt).toLocaleDateString(locale)
: '-'}
diff --git a/frontend/components/dashboard/QuizSavedBanner.tsx b/frontend/components/dashboard/QuizSavedBanner.tsx
index faa391ee..20c6e4eb 100644
--- a/frontend/components/dashboard/QuizSavedBanner.tsx
+++ b/frontend/components/dashboard/QuizSavedBanner.tsx
@@ -1,6 +1,7 @@
'use client';
import { useEffect, useState } from 'react';
+import { useTranslations } from 'next-intl';
import { Link } from '@/i18n/routing'
interface SavedQuizInfo {
@@ -12,6 +13,7 @@ interface SavedQuizInfo {
}
export function QuizSavedBanner() {
+ const t = useTranslations('dashboard.quizSaved');
const [info, setInfo] = useState(null);
useEffect(() => {
@@ -33,32 +35,32 @@ export function QuizSavedBanner() {
- 🎉 Результат квізу збережено!
+ 🎉 {t('title')}
- Ви набрали {info.score}/{info.total} ({info.percentage.toFixed(0)}%)
+ {t('scored')} {info.score}/{info.total} ({info.percentage.toFixed(0)}%)
{info.pointsAwarded > 0 && (
- <> • +{info.pointsAwarded} балів додано до рейтингу>
+ <> • {t('pointsAwarded', { points: info.pointsAwarded })}>
)}
{info.pointsAwarded === 0 && (
- <> • Бали не нараховано (результат не покращено)>
+ <> • {t('noPoints')}>
)}
-
- Переглянути рейтинг
+ {t('viewLeaderboard')}
-
- Пройти ще раз
+ {t('tryAgain')}
);
-}
\ No newline at end of file
+}
diff --git a/frontend/components/dashboard/StatsCard.tsx b/frontend/components/dashboard/StatsCard.tsx
index 985c5c05..7cfb19d3 100644
--- a/frontend/components/dashboard/StatsCard.tsx
+++ b/frontend/components/dashboard/StatsCard.tsx
@@ -1,3 +1,6 @@
+"use client";
+
+import { useTranslations } from "next-intl";
import { Link } from '@/i18n/routing';
import { TrendingUp, History } from 'lucide-react';
@@ -10,6 +13,7 @@ interface StatsCardProps {
}
export function StatsCard({ stats }: StatsCardProps) {
+ const t = useTranslations("dashboard.stats");
const hasActivity = stats && stats.totalAttempts > 0;
const cardStyles = `
@@ -23,11 +27,11 @@ export function StatsCard({ stats }: StatsCardProps) {
`;
const primaryBtnStyles = `
- group relative inline-flex items-center justify-center rounded-full
- px-8 py-3 text-sm font-semibold tracking-widest uppercase text-white
- bg-gradient-to-r from-sky-500 via-indigo-500 to-pink-500
- shadow-[0_4px_14px_rgba(56,189,248,0.4)]
- dark:shadow-[0_4px_20px_rgba(129,140,248,0.4)]
+ group relative inline-flex items-center justify-center rounded-full
+ px-8 py-3 text-sm font-semibold tracking-widest uppercase text-white
+ bg-gradient-to-r from-sky-500 via-indigo-500 to-pink-500
+ shadow-[0_4px_14px_rgba(56,189,248,0.4)]
+ dark:shadow-[0_4px_20px_rgba(129,140,248,0.4)]
transition-all hover:scale-105 hover:shadow-lg
`;
@@ -44,16 +48,16 @@ export function StatsCard({ stats }: StatsCardProps) {
id="stats-heading"
className="text-xl font-bold text-slate-800 dark:text-slate-100 mb-2"
>
- Quiz Statistics
+ {t("title")}
{!hasActivity ? (
<>
- Ready to level up? Challenge yourself with a new React quiz.
+ {t("noActivity")}
- Start a Quiz
+ {t("startQuiz")}
-
- Attempts
+ {t("attempts")}
-
{stats?.totalAttempts}
@@ -73,7 +77,7 @@ export function StatsCard({ stats }: StatsCardProps) {
-
- Avg Score
+ {t("avgScore")}
-
{stats?.averageScore}%
@@ -82,7 +86,7 @@ export function StatsCard({ stats }: StatsCardProps) {
- Continue Learning
+ {t("continueLearning")}
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 8cb0ecc3..42e68f22 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -280,5 +280,106 @@
"policyLink": "Privacy Policy",
"accept": "Accept Cookies",
"decline": "Decline"
+ },
+ "auth": {
+ "login": {
+ "title": "Log in",
+ "submit": "Log in",
+ "submitting": "Logging in...",
+ "noAccount": "Don't have an account?",
+ "signupLink": "Sign up",
+ "forgotPassword": "Forgot password?",
+ "errors": {
+ "invalidCredentials": "Invalid email or password",
+ "emailNotVerified": "Your email address is not verified. Please check your inbox.",
+ "networkError": "Network error. Please check your connection and try again.",
+ "resendFailed": "Failed to resend verification email. Please try again."
+ },
+ "resendVerification": "Resend verification email",
+ "verificationSent": "Verification successfully sent to"
+ },
+ "signup": {
+ "title": "Sign up",
+ "submit": "Sign up",
+ "submitting": "Signing up...",
+ "hasAccount": "Already have an account?",
+ "loginLink": "Log in",
+ "errors": {
+ "signupFailed": "Failed to sign up. Please try again.",
+ "networkError": "Network error. Please check your connection and try again."
+ },
+ "verificationSent": "We've sent a verification email to",
+ "checkInbox": "Please check your inbox and click the verification link to activate your account.",
+ "goToLogin": "Go to login"
+ },
+ "forgotPassword": {
+ "title": "Reset password",
+ "submit": "Send reset link",
+ "submitting": "Sending...",
+ "errors": {
+ "sendFailed": "Failed to send reset email. Please try again.",
+ "networkError": "Network error. Please check your connection."
+ },
+ "emailSent": "We've sent a password reset link to",
+ "checkInbox": "Please check your inbox."
+ },
+ "resetPassword": {
+ "title": "Set new password",
+ "submit": "Update password",
+ "submitting": "Updating...",
+ "errors": {
+ "resetFailed": "Failed to reset password. The link may be invalid or expired.",
+ "networkError": "Network error. Please try again."
+ },
+ "success": "Your password has been updated successfully."
+ },
+ "fields": {
+ "email": "Email",
+ "password": "Password",
+ "name": "Name",
+ "showPassword": "Show password",
+ "hidePassword": "Hide password",
+ "show": "Show",
+ "hide": "Hide"
+ },
+ "divider": "or"
+ },
+ "contacts": {
+ "metaTitle": "Contacts | DevLovers",
+ "metaDescription": "Get in touch with the DevLovers team for questions, feedback, or collaboration.",
+ "title": "Contacts",
+ "subtitle": "We'd love to hear from you!",
+ "email": "Email:",
+ "linkedin": "LinkedIn:",
+ "github": "GitHub:"
+ },
+ "dashboard": {
+ "metaTitle": "Dashboard | DevLovers",
+ "metaDescription": "Track your progress and quiz performance.",
+ "title": "Dashboard",
+ "subtitle": "Welcome back to your training ground.",
+ "supportLink": "Support & Feedback",
+ "profile": {
+ "defaultName": "Developer",
+ "defaultRole": "user",
+ "totalPoints": "Total Points",
+ "joined": "Joined"
+ },
+ "stats": {
+ "title": "Quiz Statistics",
+ "noActivity": "Ready to level up? Challenge yourself with a new quiz.",
+ "startQuiz": "Start a Quiz",
+ "attempts": "Attempts",
+ "avgScore": "Avg Score",
+ "continueLearning": "Continue Learning"
+ },
+ "quizSaved": {
+ "title": "Quiz result saved!",
+ "scored": "You scored",
+ "pointsAwarded": "+{points} points added to rating",
+ "noPoints": "No points awarded (result not improved)",
+ "viewLeaderboard": "View leaderboard",
+ "tryAgain": "Try again"
+ }
}
}
diff --git a/frontend/messages/pl.json b/frontend/messages/pl.json
index 7f26bab3..821df3d7 100644
--- a/frontend/messages/pl.json
+++ b/frontend/messages/pl.json
@@ -153,9 +153,110 @@
},
"CookieBanner": {
"title": "🍪 Dbamy o Twoją prywatność",
- "description": "Używamy plików cookie, aby poprawić jakość przeglądania, wyświetlać spersonalizowane treści i analizować nasz ruch. Klikając „Zaakceptuj”, wyrażasz zgodę na używanie plików cookie. Przeczytaj naszą",
+ "description": "Używamy plików cookie, aby poprawić jakość przeglądania, wyświetlać spersonalizowane treści i analizować nasz ruch. Klikając „Zaakceptuj\", wyrażasz zgodę na używanie plików cookie. Przeczytaj naszą",
"policyLink": "Politykę Prywatności",
"accept": "Zaakceptuj",
"decline": "Odrzuć"
+ },
+ "auth": {
+ "login": {
+ "title": "Zaloguj się",
+ "submit": "Zaloguj się",
+ "submitting": "Logowanie...",
+ "noAccount": "Nie masz konta?",
+ "signupLink": "Zarejestruj się",
+ "forgotPassword": "Zapomniałeś hasła?",
+ "errors": {
+ "invalidCredentials": "Nieprawidłowy email lub hasło",
+ "emailNotVerified": "Twój adres email nie został zweryfikowany. Sprawdź swoją skrzynkę.",
+ "networkError": "Błąd sieci. Sprawdź połączenie i spróbuj ponownie.",
+ "resendFailed": "Nie udało się ponownie wysłać emaila weryfikacyjnego. Spróbuj ponownie."
+ },
+ "resendVerification": "Wyślij ponownie email weryfikacyjny",
+ "verificationSent": "Email weryfikacyjny został wysłany na"
+ },
+ "signup": {
+ "title": "Rejestracja",
+ "submit": "Zarejestruj się",
+ "submitting": "Rejestrowanie...",
+ "hasAccount": "Masz już konto?",
+ "loginLink": "Zaloguj się",
+ "errors": {
+ "signupFailed": "Nie udało się zarejestrować. Spróbuj ponownie.",
+ "networkError": "Błąd sieci. Sprawdź połączenie i spróbuj ponownie."
+ },
+ "verificationSent": "Wysłaliśmy email weryfikacyjny na",
+ "checkInbox": "Sprawdź swoją skrzynkę i kliknij link weryfikacyjny, aby aktywować konto.",
+ "goToLogin": "Przejdź do logowania"
+ },
+ "forgotPassword": {
+ "title": "Resetowanie hasła",
+ "submit": "Wyślij link resetujący",
+ "submitting": "Wysyłanie...",
+ "errors": {
+ "sendFailed": "Nie udało się wysłać emaila resetującego. Spróbuj ponownie.",
+ "networkError": "Błąd sieci. Sprawdź połączenie."
+ },
+ "emailSent": "Wysłaliśmy link do resetowania hasła na",
+ "checkInbox": "Sprawdź swoją skrzynkę."
+ },
+ "resetPassword": {
+ "title": "Ustaw nowe hasło",
+ "submit": "Zaktualizuj hasło",
+ "submitting": "Aktualizowanie...",
+ "errors": {
+ "resetFailed": "Nie udało się zresetować hasła. Link może być nieprawidłowy lub wygasły.",
+ "networkError": "Błąd sieci. Spróbuj ponownie."
+ },
+ "success": "Twoje hasło zostało pomyślnie zaktualizowane."
+ },
+ "fields": {
+ "email": "Email",
+ "password": "Hasło",
+ "name": "Imię",
+ "showPassword": "Pokaż hasło",
+ "hidePassword": "Ukryj hasło",
+ "show": "Pokaż",
+ "hide": "Ukryj"
+ },
+ "divider": "lub"
+ },
+ "contacts": {
+ "metaTitle": "Kontakt | DevLovers",
+ "metaDescription": "Skontaktuj się z zespołem DevLovers w sprawie pytań, opinii lub współpracy.",
+ "title": "Kontakt",
+ "subtitle": "Chętnie usłyszymy od Ciebie!",
+ "email": "Email:",
+ "linkedin": "LinkedIn:",
+ "github": "GitHub:"
+ },
+ "dashboard": {
+ "metaTitle": "Panel | DevLovers",
+ "metaDescription": "Śledź swój postęp i wyniki quizów.",
+ "title": "Panel",
+ "subtitle": "Witaj z powrotem na swoim placu treningowym.",
+ "supportLink": "Wsparcie i opinie",
+ "profile": {
+ "defaultName": "Programista",
+ "defaultRole": "użytkownik",
+ "totalPoints": "Łączna liczba punktów",
+ "joined": "Dołączył"
+ },
+ "stats": {
+ "title": "Statystyki quizów",
+ "noActivity": "Gotowy na rozwój? Sprawdź się w nowym quizie.",
+ "startQuiz": "Rozpocznij quiz",
+ "attempts": "Próby",
+ "avgScore": "Średni wynik",
+ "continueLearning": "Kontynuuj naukę"
+ },
+ "quizSaved": {
+ "title": "Wynik quizu zapisany!",
+ "scored": "Zdobyłeś",
+ "pointsAwarded": "+{points} punktów dodano do rankingu",
+ "noPoints": "Nie przyznano punktów (wynik nie uległ poprawie)",
+ "viewLeaderboard": "Zobacz ranking",
+ "tryAgain": "Spróbuj ponownie"
+ }
}
}
diff --git a/frontend/messages/uk.json b/frontend/messages/uk.json
index e2f8ed7a..f60e4eff 100644
--- a/frontend/messages/uk.json
+++ b/frontend/messages/uk.json
@@ -280,5 +280,106 @@
"policyLink": "Політику конфіденційності",
"accept": "Прийняти",
"decline": "Відхилити"
+ },
+ "auth": {
+ "login": {
+ "title": "Увійти",
+ "submit": "Увійти",
+ "submitting": "Вхід...",
+ "noAccount": "Немає акаунта?",
+ "signupLink": "Зареєструватися",
+ "forgotPassword": "Забули пароль?",
+ "errors": {
+ "invalidCredentials": "Невірний email або пароль",
+ "emailNotVerified": "Ваша електронна адреса не підтверджена. Перевірте вашу поштову скриньку.",
+ "networkError": "Помилка мережі. Перевірте з'єднання та спробуйте ще раз.",
+ "resendFailed": "Не вдалося повторно надіслати лист підтвердження. Спробуйте ще раз."
+ },
+ "resendVerification": "Надіслати лист підтвердження повторно",
+ "verificationSent": "Лист підтвердження успішно надіслано на"
+ },
+ "signup": {
+ "title": "Реєстрація",
+ "submit": "Зареєструватися",
+ "submitting": "Реєстрація...",
+ "hasAccount": "Вже є акаунт?",
+ "loginLink": "Увійти",
+ "errors": {
+ "signupFailed": "Не вдалося зареєструватися. Спробуйте ще раз.",
+ "networkError": "Помилка мережі. Перевірте з'єднання та спробуйте ще раз."
+ },
+ "verificationSent": "Ми надіслали лист підтвердження на",
+ "checkInbox": "Перевірте вашу поштову скриньку та натисніть на посилання для активації акаунта.",
+ "goToLogin": "Перейти до входу"
+ },
+ "forgotPassword": {
+ "title": "Скидання пароля",
+ "submit": "Надіслати посилання",
+ "submitting": "Надсилання...",
+ "errors": {
+ "sendFailed": "Не вдалося надіслати лист для скидання. Спробуйте ще раз.",
+ "networkError": "Помилка мережі. Перевірте з'єднання."
+ },
+ "emailSent": "Ми надіслали посилання для скидання пароля на",
+ "checkInbox": "Перевірте вашу поштову скриньку."
+ },
+ "resetPassword": {
+ "title": "Встановити новий пароль",
+ "submit": "Оновити пароль",
+ "submitting": "Оновлення...",
+ "errors": {
+ "resetFailed": "Не вдалося скинути пароль. Посилання може бути недійсним або застарілим.",
+ "networkError": "Помилка мережі. Спробуйте ще раз."
+ },
+ "success": "Ваш пароль успішно оновлено."
+ },
+ "fields": {
+ "email": "Email",
+ "password": "Пароль",
+ "name": "Ім'я",
+ "showPassword": "Показати пароль",
+ "hidePassword": "Сховати пароль",
+ "show": "Показати",
+ "hide": "Сховати"
+ },
+ "divider": "або"
+ },
+ "contacts": {
+ "metaTitle": "Контакти | DevLovers",
+ "metaDescription": "Зв'яжіться з командою DevLovers для запитань, відгуків або співпраці.",
+ "title": "Контакти",
+ "subtitle": "Будемо раді почути від вас!",
+ "email": "Email:",
+ "linkedin": "LinkedIn:",
+ "github": "GitHub:"
+ },
+ "dashboard": {
+ "metaTitle": "Панель | DevLovers",
+ "metaDescription": "Відстежуйте свій прогрес та результати квізів.",
+ "title": "Панель",
+ "subtitle": "З поверненням на вашу навчальну площадку.",
+ "supportLink": "Підтримка та відгуки",
+ "profile": {
+ "defaultName": "Розробник",
+ "defaultRole": "користувач",
+ "totalPoints": "Загальна кількість балів",
+ "joined": "Приєднався"
+ },
+ "stats": {
+ "title": "Статистика квізів",
+ "noActivity": "Готові прокачатися? Випробуйте себе в новому квізі.",
+ "startQuiz": "Почати квіз",
+ "attempts": "Спроби",
+ "avgScore": "Середній бал",
+ "continueLearning": "Продовжити навчання"
+ },
+ "quizSaved": {
+ "title": "Результат квізу збережено!",
+ "scored": "Ви набрали",
+ "pointsAwarded": "+{points} балів додано до рейтингу",
+ "noPoints": "Бали не нараховано (результат не покращено)",
+ "viewLeaderboard": "Переглянути рейтинг",
+ "tryAgain": "Пройти ще раз"
+ }
}
}