+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/src/app/login/page.tsx b/src/app/[locale]/login/page.tsx
similarity index 72%
rename from src/app/login/page.tsx
rename to src/app/[locale]/login/page.tsx
index 9252d4b..22194bf 100644
--- a/src/app/login/page.tsx
+++ b/src/app/[locale]/login/page.tsx
@@ -3,45 +3,58 @@ import styles from '@/app/styles/login.module.scss'
import LogoIcon from '@/app/ui/logo-icon';
import Link from 'next/link';
import LoginForm from '@/app/ui/login-form';
+import LanguageSwitcher from '@/app/components/LanguageSwitcher';
+import { useTranslations } from 'next-intl';
export const metadata: Metadata = {
title: 'Login',
};
export default function Page() {
+ const t = useTranslations('Login-register-form');
+
return (
+
+
+
NO COPY
-
Защита авторских прав
+
+ {t('copyright-protection')}
+
- или войти через
+
+ {t('or-log-in-via')}
+
-
Нет аккаунта? Зарегистрироваться
+
{t('no-account')}?
+ {t('register')}
+
- Защита контента скрытыми водяными знаками
+ {t('protecting-watermark')}
- Мониторинг нарушений в реальном времени
+ {t('monitoring-violations')}
- Надежное хранение и безопасность данных
+ {t('safe-storage')}
diff --git a/src/app/page.tsx b/src/app/[locale]/page.tsx
similarity index 81%
rename from src/app/page.tsx
rename to src/app/[locale]/page.tsx
index 98243a1..0ac4b69 100644
--- a/src/app/page.tsx
+++ b/src/app/[locale]/page.tsx
@@ -1,17 +1,21 @@
import styles from "@/app/styles/page.module.scss";
import { Metadata } from 'next';
import Link from 'next/link';
+import { useTranslations } from 'next-intl';
export const metadata: Metadata = {
title: 'Home page',
};
export default function Home() {
+ const t = useTranslations('HomePage');
+
return (
Home page
+ {t('title')}
...>}>
-
Статистика
+
+ {t("statistics")}
+
diff --git a/src/app/pages/emptypage/page.tsx b/src/app/[locale]/pages/emptypage/page.tsx
similarity index 100%
rename from src/app/pages/emptypage/page.tsx
rename to src/app/[locale]/pages/emptypage/page.tsx
diff --git a/src/app/pages/layout.tsx b/src/app/[locale]/pages/layout.tsx
similarity index 93%
rename from src/app/pages/layout.tsx
rename to src/app/[locale]/pages/layout.tsx
index b9830f5..795f79d 100644
--- a/src/app/pages/layout.tsx
+++ b/src/app/[locale]/pages/layout.tsx
@@ -1,6 +1,6 @@
import NavLinks from '@/app/ui/nav-links'
import styles from '@/app/styles/page.module.scss'
-import HeaderPanel from '../ui/header/headerPanel';
+import HeaderPanel from '@/app/ui/header/headerPanel';
import { getQueryClient } from '@/app/providers/getQueryClient';
import { HydrationBoundary, dehydrate } from '@tanstack/react-query';
import { getUserData } from '@/app/actions/action';
diff --git a/src/app/pages/marking-audio/page.tsx b/src/app/[locale]/pages/marking-audio/page.tsx
similarity index 100%
rename from src/app/pages/marking-audio/page.tsx
rename to src/app/[locale]/pages/marking-audio/page.tsx
diff --git a/src/app/pages/marking-photo/page.tsx b/src/app/[locale]/pages/marking-photo/page.tsx
similarity index 100%
rename from src/app/pages/marking-photo/page.tsx
rename to src/app/[locale]/pages/marking-photo/page.tsx
diff --git a/src/app/pages/marking-video/page.tsx b/src/app/[locale]/pages/marking-video/page.tsx
similarity index 100%
rename from src/app/pages/marking-video/page.tsx
rename to src/app/[locale]/pages/marking-video/page.tsx
diff --git a/src/app/pages/payment/page.tsx b/src/app/[locale]/pages/payment/page.tsx
similarity index 100%
rename from src/app/pages/payment/page.tsx
rename to src/app/[locale]/pages/payment/page.tsx
diff --git a/src/app/pages/reports/page.tsx b/src/app/[locale]/pages/reports/page.tsx
similarity index 100%
rename from src/app/pages/reports/page.tsx
rename to src/app/[locale]/pages/reports/page.tsx
diff --git a/src/app/pages/settings/page.tsx b/src/app/[locale]/pages/settings/page.tsx
similarity index 100%
rename from src/app/pages/settings/page.tsx
rename to src/app/[locale]/pages/settings/page.tsx
diff --git a/src/app/register/page.tsx b/src/app/[locale]/register/page.tsx
similarity index 54%
rename from src/app/register/page.tsx
rename to src/app/[locale]/register/page.tsx
index ad79eec..763202a 100644
--- a/src/app/register/page.tsx
+++ b/src/app/[locale]/register/page.tsx
@@ -2,26 +2,39 @@ import LogoIcon from '@/app/ui/logo-icon';
import styles from '@/app/styles/login.module.scss'
import Link from 'next/link';
import RegisterForm from '@/app/ui/register-form';
+import LanguageSwitcher from '@/app/components/LanguageSwitcher';
+import {useTranslations} from 'next-intl';
export default function Page() {
+ const t = useTranslations('Login-register-form');
+
return (
+
+
+
NO COPY
-
Создать аккаунт
+
+ {t('create-an-account')}
+
-
🎉 Бесплатный старт
-
При регистрации вы получаете подписку "ДЕМО" с возможностью маркировки до 3 файлов в месяц и 10 МБ хранилища.
+
🎉 {t('free-start')}
+
+ {t('free-start-description')}
+
- или зарегистрироваться через
+
+ {t('or-register-via')}
+
-
Уже есть аккаунт? Войти
+
{t('already-have-an-account')}? {t('login')}
diff --git a/src/app/terms-and-privacy-policy/page.tsx b/src/app/[locale]/terms-and-privacy-policy/page.tsx
similarity index 100%
rename from src/app/terms-and-privacy-policy/page.tsx
rename to src/app/[locale]/terms-and-privacy-policy/page.tsx
diff --git a/src/app/actions/auth.ts b/src/app/actions/auth.ts
index 221ae8b..347ed36 100644
--- a/src/app/actions/auth.ts
+++ b/src/app/actions/auth.ts
@@ -83,7 +83,7 @@ export async function authorization(
let parsed = await response.json();
await createSession(parsed.token, parsed.email);
} else {
- throw (`Запрос завершился с ошибкой: ${response.status}`);
+ throw ('request-ended-with-an-error');
}
} catch (error) {
@@ -146,7 +146,7 @@ export async function registration(
path: string
}) => {
if (errors[obj.path[0]]) {
- errors[obj.path[0]] = `${errors[obj.path[0]]} ${obj.message}`;
+ errors[obj.path[0]] = `${errors[obj.path[0]]}&${obj.message}`;
} else {
errors[obj.path[0]] = obj.message;
}
@@ -168,9 +168,9 @@ export async function registration(
try {
const { full_name, email, password, phone } = validatedFields.data;
- const response = await fetch(`${API_BASE_URL}/v1/api/auth/register`, {
+ const response = await fetch(`${API_BASE_URL}/v1/api/auth/register1`, {
method: 'POST',
- body: JSON.stringify({ fullName: full_name, email, password, companyName: company, phone: phone}),
+ body: JSON.stringify({ fullName: full_name, email, password, companyName: company, phone: phone }),
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
@@ -186,7 +186,7 @@ export async function registration(
} catch (error) {
if (error) {
const errors: Record = {};
- errors['server'] = `Запрос завершился с ошибкой: ${error}`
+ errors['server'] = 'request-ended-with-an-error'
return {
previousState: {
diff --git a/src/app/actions/definitions.ts b/src/app/actions/definitions.ts
index f18a5a5..ea31844 100644
--- a/src/app/actions/definitions.ts
+++ b/src/app/actions/definitions.ts
@@ -20,26 +20,26 @@ export const SignupFormSchema = z
.object({
full_name: z
.string()
- .min(3, { error: 'Имя должно содержать не менее 3 символов.' })
+ .min(3, { error: 'register-error-name' })
.trim(),
- email: z.email({ error: 'Пожалуйста, введите правильный адрес электронной почты.' }).trim(),
+ email: z.email({ error: 'register-error-valid-email' }).trim(),
phone: z
.string()
- .min(12, { error: 'Пожалуйста, введите телефонный номер.' }),
+ .min(12, { error: 'register-error-phone' }),
password: z
.string()
- .min(8, { error: 'Длина пароля должна быть не менее 8 символов.' })
- .regex(/[a-zA-Zа-яёА-ЯЁ]/, { error: 'Пароль должен содержать как минимум 1 букву.' })
- .regex(/[0-9]/, { error: 'Пароль должен содержать как минимум 1 цифру.' })
+ .min(8, { error: 'register-error-password-symbols' })
+ .regex(/[a-zA-Zа-яёА-ЯЁ]/, { error: 'register-error-password-one-letter'})
+ .regex(/[0-9]/, { error: 'register-error-password-one-digit' })
.trim(),
confirm_password: z
.string(),
agree: z
.string()
- .min(2, { error: 'Пожалуйста, ознакомьтесь с условиями использования и политикой конфиденциальности.' })
+ .min(2, { error: 'register-error-agree' })
})
.refine((data) => data.password === data.confirm_password, {
- message: 'Повторите пароль',
+ message: 'repeat-password',
path: ['confirm_password'],
});
@@ -47,11 +47,11 @@ export const loginFormSchema = z
.object({
email: z
.string()
- .min(1, { error: 'Адрес электронной почты не должен быть пустым.' })
+ .min(1, { error: 'login-error-email' })
.trim(),
password: z
.string()
- .min(1, { error: 'Пароль не должен быть пустым.' })
+ .min(1, { error: 'login-error-password' })
.trim(),
})
diff --git a/src/app/components/LanguageSwitcher.tsx b/src/app/components/LanguageSwitcher.tsx
new file mode 100644
index 0000000..3dab9e0
--- /dev/null
+++ b/src/app/components/LanguageSwitcher.tsx
@@ -0,0 +1,85 @@
+'use client'
+
+import { useLocale } from 'next-intl'
+import { usePathname, useRouter } from '@/i18n/navigation'
+import { routing } from '@/i18n/routing'
+import { useState, useRef, useEffect } from 'react'
+import { useClickOutside } from '@/app/hooks/useClickOutside';
+
+export default function LanguageSwitcher() {
+ const locale = useLocale()
+ const router = useRouter()
+ const pathname = usePathname()
+ const [isOpen, setIsOpen] = useState(false)
+ const dropdownRef = useRef(null)
+
+ const switchLanguage = (newLocale: string) => {
+ router.replace(pathname, { locale: newLocale })
+ setIsOpen(false)
+ }
+
+ useClickOutside(dropdownRef, () => {
+ setIsOpen(false);
+ });
+
+ useEffect(() => {
+ const handleEscape = (event: KeyboardEvent) => {
+ if (event.key === 'Escape') setIsOpen(false)
+ }
+
+ document.addEventListener('keydown', handleEscape)
+ return () => document.removeEventListener('keydown', handleEscape)
+ }, []);
+
+ return (
+
+
+
+ {isOpen && (
+
+
+ {routing.locales.map((lang) => (
+
+
+
+ ))}
+
+
+ )}
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
deleted file mode 100644
index 0cb2844..0000000
--- a/src/app/layout.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import Providers from './providers/getQueryServer'
-import type { Metadata } from "next";
-import "./styles/globals.css";
-import "./styles/global-styles.scss"
-
-export const metadata: Metadata = {
- title: "No copy",
- description: "No copy",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
-
-
-
- {children}
-
-
-
- );
-}
diff --git a/src/app/styles/login.module.scss b/src/app/styles/login.module.scss
index cac5e5e..e3f652f 100644
--- a/src/app/styles/login.module.scss
+++ b/src/app/styles/login.module.scss
@@ -15,6 +15,12 @@
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 500px;
+
+ &-language {
+ position: fixed;
+ right: 20px;
+ top: 20px;
+ }
}
.logo {
diff --git a/src/app/ui/header/headerPanel.tsx b/src/app/ui/header/headerPanel.tsx
index d878c51..1e75321 100644
--- a/src/app/ui/header/headerPanel.tsx
+++ b/src/app/ui/header/headerPanel.tsx
@@ -1,11 +1,13 @@
import NotificationsButton from './notificationsButton';
import UserMenuButton from './userMenuButton';
import Link from 'next/link';
+import LanguageSwitcher from '@/app/components/LanguageSwitcher';
export default function HeaderPanel() {
return (