diff --git a/src/shared/config/i18n/messages/en.json b/src/shared/config/i18n/messages/en.json index c1f760b..49410a8 100644 --- a/src/shared/config/i18n/messages/en.json +++ b/src/shared/config/i18n/messages/en.json @@ -1,6 +1,104 @@ { "HomePage": { - "title": "Salom dunyo!", + "title": "Hello world!", "about": "Go to the about page" + }, + "Navbar": { + "logo": "Plagat", + "aboutSite": "About Site", + "contact": "Contact", + "login": "Login", + "signup": "Sign up", + "profile": "Profile", + "logout": "Logout" + }, + "Footer": { + "product": "Product", + "overview": "Overview", + "pricing": "Pricing", + "marketplace": "Marketplace", + "features": "Features", + "company": "Company", + "about": "About", + "team": "Team", + "blog": "Blog", + "careers": "Careers", + "resources": "Resources", + "help": "Help", + "sales": "Sales", + "advertise": "Advertise", + "privacy": "Privacy", + "copyright": "© {year} Felix IT Solutions. All rights reserved.", + "terms": "Terms and Conditions" + }, + "PlagiarismCheck": { + "badge": "Originality Check", + "title": "Submit Your Document", + "description": "Upload a document to verify its originality. Results are typically ready within a few minutes.", + "documentTopic": "Document Topic", + "topicPlaceholder": "e.g. The Impact of Artificial Intelligence on Education", + "senderFullName": "Sender Full Name", + "notLoggedIn": "Not logged in", + "certificateOption": "Certificate Option", + "documentFile": "Document File", + "clickToUpload": "Click to upload document", + "fileTypes": "PDF, DOC, DOCX, TXT · Max 20 MB", + "autoFilled": "Auto-filled", + "removeFile": "Remove file", + "certificateTitle": "Return result with certificate", + "certificateDescription": "An official certificate will be attached to your originality report.", + "submitting": "Submitting…", + "submitButton": "Submit for Originality Check", + "dismiss": "Dismiss" + }, + "HistoryPage": { + "title": "Check History", + "description": "All plagiarism checks submitted by you", + "sender": "Sender", + "file": "File", + "date": "Date", + "amount": "Amount", + "result": "Result", + "actions": "", + "emptyMessage": "No plagiarism checks found.", + "tryAgain": "Try again", + "view": "View", + "viewDetails": "View details for {sender}", + "pagination": "Page {current} of {total}", + "previousPage": "Previous page", + "nextPage": "Next page", + "page": "Page {page}", + "resultClean": "Clean", + "resultPlagiarismFound": "Plagiarism Found", + "resultPending": "Pending", + "resultFailed": "Failed" + }, + "DetailPage": { + "id": "ID", + "submissionDetails": "Submission Details", + "sender": "Sender", + "fileName": "File Name", + "fileSize": "File Size", + "submitted": "Submitted", + "payment": "Payment", + "resultTitle": "Result", + "analysisInProgress": "Analysis in progress", + "resultsReadyAfterProcessing": "Results will appear once processing is complete.", + "noResultAvailable": "No result available.", + "plagiarismResult": "Plagiarism Result", + "wordsChecked": "Words Checked", + "wordsMatched": "Words Matched", + "matchedSources": "Matched Sources", + "processedAt": "Processed At", + "certificate": "Certificate", + "noCertificate": "No certificate issued for this check.", + "noCertificateHighSimilarity": "Certificates are not issued for high-similarity results.", + "issued": "Issued", + "expires": "Expires", + "issuer": "Issuer", + "certificateId": "Certificate ID", + "downloadCertificate": "Download Certificate", + "unknownError": "Unknown error", + "words": "words" } } diff --git a/src/shared/config/i18n/messages/ru.json b/src/shared/config/i18n/messages/ru.json index aa28955..13ae46a 100644 --- a/src/shared/config/i18n/messages/ru.json +++ b/src/shared/config/i18n/messages/ru.json @@ -1,6 +1,104 @@ { "HomePage": { - "title": "Hello world!", - "about": "Go to the about page" + "title": "Привет мир!", + "about": "Перейти на страницу о нас" + }, + "Navbar": { + "logo": "Plagat", + "aboutSite": "О сайте", + "contact": "Контакты", + "login": "Войти", + "signup": "Регистрация", + "profile": "Профиль", + "logout": "Выйти" + }, + "Footer": { + "product": "Продукт", + "overview": "Обзор", + "pricing": "Цены", + "marketplace": "Маркетплейс", + "features": "Функции", + "company": "Компания", + "about": "О нас", + "team": "Команда", + "blog": "Блог", + "careers": "Карьера", + "resources": "Ресурсы", + "help": "Помощь", + "sales": "Продажи", + "advertise": "Реклама", + "privacy": "Конфиденциальность", + "copyright": "© {year} Felix IT Solutions. Все права защищены.", + "terms": "Условия и положения" + }, + "PlagiarismCheck": { + "badge": "Проверка оригинальности", + "title": "Отправьте ваш документ", + "description": "Загрузите документ для проверки его оригинальности. Результаты обычно готовы в течение нескольких минут.", + "documentTopic": "Тема документа", + "topicPlaceholder": "например: Влияние искусственного интеллекта на образование", + "senderFullName": "Полное имя отправителя", + "notLoggedIn": "Не авторизован", + "certificateOption": "Опция сертификата", + "documentFile": "Файл документа", + "clickToUpload": "Нажмите, чтобы загрузить документ", + "fileTypes": "PDF, DOC, DOCX, TXT · Макс 20 МБ", + "autoFilled": "Автозаполнено", + "removeFile": "Удалить файл", + "certificateTitle": "Вернуть результат с сертификатом", + "certificateDescription": "Официальный сертификат будет прикреплен к вашему отчету об оригинальности.", + "submitting": "Отправка…", + "submitButton": "Отправить на проверку оригинальности", + "dismiss": "Закрыть" + }, + "HistoryPage": { + "title": "История проверок", + "description": "Все проверки на плагиат, отправленные вами", + "sender": "Отправитель", + "file": "Файл", + "date": "Дата", + "amount": "Сумма", + "result": "Результат", + "actions": "", + "emptyMessage": "Проверки на плагиат не найдены.", + "tryAgain": "Попробовать снова", + "view": "Просмотр", + "viewDetails": "Просмотреть детали для {sender}", + "pagination": "Страница {current} из {total}", + "previousPage": "Предыдущая страница", + "nextPage": "Следующая страница", + "page": "Страница {page}", + "resultClean": "Чисто", + "resultPlagiarismFound": "Обнаружен плагиат", + "resultPending": "В ожидании", + "resultFailed": "Не удалось" + }, + "DetailPage": { + "id": "ID", + "submissionDetails": "Детали отправки", + "sender": "Отправитель", + "fileName": "Имя файла", + "fileSize": "Размер файла", + "submitted": "Отправлено", + "payment": "Оплата", + "resultTitle": "Результат", + "analysisInProgress": "Анализ выполняется", + "resultsReadyAfterProcessing": "Результаты появятся после завершения обработки.", + "noResultAvailable": "Результат недоступен.", + "plagiarismResult": "Результат на плагиат", + "wordsChecked": "Проверено слов", + "wordsMatched": "Найдено совпадающих слов", + "matchedSources": "Найденные источники", + "processedAt": "Обработано", + "certificate": "Сертификат", + "noCertificate": "Сертификат не выдан для этой проверки.", + "noCertificateHighSimilarity": "Сертификаты не выдаются при высокой схожести.", + "issued": "Выдан", + "expires": "Действителен до", + "issuer": "Выдавший", + "certificateId": "ID сертификата", + "downloadCertificate": "Скачать сертификат", + "unknownError": "Неизвестная ошибка", + "words": "слов" } } diff --git a/src/shared/config/i18n/messages/uz.d.json.ts b/src/shared/config/i18n/messages/uz.d.json.ts index 5d538a0..7b0caa8 100644 --- a/src/shared/config/i18n/messages/uz.d.json.ts +++ b/src/shared/config/i18n/messages/uz.d.json.ts @@ -4,7 +4,105 @@ declare const messages: { HomePage: { title: 'Salom dunyo!'; - about: 'Go to the about page'; + about: "Biz haqimizda sahifasiga o'ting"; + }; + Navbar: { + logo: 'Plagat'; + aboutSite: 'Sayt haqida'; + contact: 'Aloqa'; + login: 'Kirish'; + signup: "Ro'yxatdan o'tish"; + profile: 'Profil'; + logout: 'Chiqish'; + }; + Footer: { + product: 'Mahsulot'; + overview: "Umumiy ko'rinish"; + pricing: 'Narxlar'; + marketplace: 'Bozor'; + features: 'Xususiyatlar'; + company: 'Kompaniya'; + about: 'Biz haqimizda'; + team: 'Jamoa'; + blog: 'Blog'; + careers: 'Karyera'; + resources: 'Resurslar'; + help: 'Yordam'; + sales: 'Sotuvlar'; + advertise: 'Reklama'; + privacy: 'Maxfiylik'; + copyright: '© {year} Felix IT Solutions. Barcha huquqlar himoyalangan.'; + terms: 'Foydalanish shartlari'; + }; + PlagiarismCheck: { + badge: 'Orijinallik tekshiruvi'; + title: 'Hujjatni yuboring'; + description: "Hujjatning orijinalligini tekshirish uchun yuklang. Natijalar odatda bir necha daqiqada tayyor bo'ladi."; + documentTopic: 'Hujjat mavzusi'; + topicPlaceholder: "masalan: Sun'iy intellektning ta'limga ta'siri"; + senderFullName: "Yuboruvchi to'liq ismi"; + notLoggedIn: 'Kirilmagan'; + certificateOption: 'Sertifikat varianti'; + documentFile: 'Hujjat fayli'; + clickToUpload: 'Hujjatni yuklash uchun bosing'; + fileTypes: 'PDF, DOC, DOCX, TXT · Maks 20 MB'; + autoFilled: "Avto-to'ldirilgan"; + removeFile: 'Faylni olib tashlash'; + certificateTitle: 'Natijani sertifikat bilan qaytarish'; + certificateDescription: 'Rasmiy sertifikat sizning orijinallik hisobotingizga ilova qilinadi.'; + submitting: 'Yuborilmoqda…'; + submitButton: 'Orijinallik tekshiruvi uchun yuborish'; + dismiss: 'Yopish'; + }; + HistoryPage: { + title: 'Tekshiruv tarixi'; + description: 'Siz tomonidan yuborilgan barcha plagiat tekshiruvlari'; + sender: 'Yuboruvchi'; + file: 'Fayl'; + date: 'Sana'; + amount: 'Summa'; + result: 'Natija'; + actions: ''; + emptyMessage: 'Plagiat tekshiruvlari topilmadi.'; + tryAgain: "Qayta urinib ko'ring"; + view: "Ko'rish"; + viewDetails: "{sender} uchun tafsilotlarni ko'rish"; + pagination: '{current} / {total} sahifa'; + previousPage: 'Oldingi sahifa'; + nextPage: 'Keyingi sahifa'; + page: '{page} sahifa'; + resultClean: 'Toza'; + resultPlagiarismFound: 'Plagiat topildi'; + resultPending: 'Kutilmoqda'; + resultFailed: 'Muvaffaqiyatsiz'; + }; + DetailPage: { + id: 'ID'; + submissionDetails: 'Yuborish tafsilotlari'; + sender: 'Yuboruvchi'; + fileName: 'Fayl nomi'; + fileSize: 'Fayl hajmi'; + submitted: 'Yuborilgan'; + payment: "To'lov"; + resultTitle: 'Natija'; + analysisInProgress: 'Tahlil davom etmoqda'; + resultsReadyAfterProcessing: "Natijalar qayta ishlash tugagach paydo bo'ladi."; + noResultAvailable: 'Natija mavjud emas.'; + plagiarismResult: 'Plagiat natijasi'; + wordsChecked: "Tekshirilgan so'zlar"; + wordsMatched: "Mos keladigan so'zlar"; + matchedSources: 'Topilgan manbalar'; + processedAt: 'Qayta ishlangan'; + certificate: 'Sertifikat'; + noCertificate: 'Bu tekshiruv uchun sertifikat berilmagan.'; + noCertificateHighSimilarity: "Yuoqori o'xshashlik natijasida sertifikatlar berilmaydi."; + issued: 'Berilgan'; + expires: 'Muddati tugaydi'; + issuer: 'Beruvchi'; + certificateId: 'Sertifikat ID'; + downloadCertificate: 'Sertifikatni yuklab olish'; + unknownError: "Noma'lum xato"; + words: "so'z"; }; }; export default messages; diff --git a/src/shared/config/i18n/messages/uz.json b/src/shared/config/i18n/messages/uz.json index c1f760b..c6aecb0 100644 --- a/src/shared/config/i18n/messages/uz.json +++ b/src/shared/config/i18n/messages/uz.json @@ -1,6 +1,104 @@ { "HomePage": { "title": "Salom dunyo!", - "about": "Go to the about page" + "about": "Biz haqimizda sahifasiga o'ting" + }, + "Navbar": { + "logo": "Plagat", + "aboutSite": "Sayt haqida", + "contact": "Aloqa", + "login": "Kirish", + "signup": "Ro'yxatdan o'tish", + "profile": "Profil", + "logout": "Chiqish" + }, + "Footer": { + "product": "Mahsulot", + "overview": "Umumiy ko'rinish", + "pricing": "Narxlar", + "marketplace": "Bozor", + "features": "Xususiyatlar", + "company": "Kompaniya", + "about": "Biz haqimizda", + "team": "Jamoa", + "blog": "Blog", + "careers": "Karyera", + "resources": "Resurslar", + "help": "Yordam", + "sales": "Sotuvlar", + "advertise": "Reklama", + "privacy": "Maxfiylik", + "copyright": "© {year} Felix IT Solutions. Barcha huquqlar himoyalangan.", + "terms": "Foydalanish shartlari" + }, + "PlagiarismCheck": { + "badge": "Orijinallik tekshiruvi", + "title": "Hujjatni yuboring", + "description": "Hujjatning orijinalligini tekshirish uchun yuklang. Natijalar odatda bir necha daqiqada tayyor bo'ladi.", + "documentTopic": "Hujjat mavzusi", + "topicPlaceholder": "masalan: Sun'iy intellektning ta'limga ta'siri", + "senderFullName": "Yuboruvchi to'liq ismi", + "notLoggedIn": "Kirilmagan", + "certificateOption": "Sertifikat varianti", + "documentFile": "Hujjat fayli", + "clickToUpload": "Hujjatni yuklash uchun bosing", + "fileTypes": "PDF, DOC, DOCX, TXT · Maks 20 MB", + "autoFilled": "Avto-to'ldirilgan", + "removeFile": "Faylni olib tashlash", + "certificateTitle": "Natijani sertifikat bilan qaytarish", + "certificateDescription": "Rasmiy sertifikat sizning orijinallik hisobotingizga ilova qilinadi.", + "submitting": "Yuborilmoqda…", + "submitButton": "Orijinallik tekshiruvi uchun yuborish", + "dismiss": "Yopish" + }, + "HistoryPage": { + "title": "Tekshiruv tarixi", + "description": "Siz tomonidan yuborilgan barcha plagiat tekshiruvlari", + "sender": "Yuboruvchi", + "file": "Fayl", + "date": "Sana", + "amount": "Summa", + "result": "Natija", + "actions": "", + "emptyMessage": "Plagiat tekshiruvlari topilmadi.", + "tryAgain": "Qayta urinib ko'ring", + "view": "Ko'rish", + "viewDetails": "{sender} uchun tafsilotlarni ko'rish", + "pagination": "{current} / {total} sahifa", + "previousPage": "Oldingi sahifa", + "nextPage": "Keyingi sahifa", + "page": "{page} sahifa", + "resultClean": "Toza", + "resultPlagiarismFound": "Plagiat topildi", + "resultPending": "Kutilmoqda", + "resultFailed": "Muvaffaqiyatsiz" + }, + "DetailPage": { + "id": "ID", + "submissionDetails": "Yuborish tafsilotlari", + "sender": "Yuboruvchi", + "fileName": "Fayl nomi", + "fileSize": "Fayl hajmi", + "submitted": "Yuborilgan", + "payment": "To'lov", + "resultTitle": "Natija", + "analysisInProgress": "Tahlil davom etmoqda", + "resultsReadyAfterProcessing": "Natijalar qayta ishlash tugagach paydo bo'ladi.", + "noResultAvailable": "Natija mavjud emas.", + "plagiarismResult": "Plagiat natijasi", + "wordsChecked": "Tekshirilgan so'zlar", + "wordsMatched": "Mos keladigan so'zlar", + "matchedSources": "Topilgan manbalar", + "processedAt": "Qayta ishlangan", + "certificate": "Sertifikat", + "noCertificate": "Bu tekshiruv uchun sertifikat berilmagan.", + "noCertificateHighSimilarity": "Yuoqori o'xshashlik natijasida sertifikatlar berilmaydi.", + "issued": "Berilgan", + "expires": "Muddati tugaydi", + "issuer": "Beruvchi", + "certificateId": "Sertifikat ID", + "downloadCertificate": "Sertifikatni yuklab olish", + "unknownError": "Noma'lum xato", + "words": "so'z" } } diff --git a/src/widgets/detail/ui/detailPage.tsx b/src/widgets/detail/ui/detailPage.tsx index cd29eea..4aefd02 100644 --- a/src/widgets/detail/ui/detailPage.tsx +++ b/src/widgets/detail/ui/detailPage.tsx @@ -3,6 +3,7 @@ // ───────────────────────────────────────────────────────────── 'use client'; import React from 'react'; +import { useTranslations } from 'next-intl'; import { PlagiarismCheck } from '../lib/types'; import { getFileExtension, @@ -166,81 +167,101 @@ interface CheckDetailViewProps { check: PlagiarismCheck; } -const CheckHeader: React.FC = ({ check }) => ( -
-
-
- -
-

- {check.sender.name} -

-

{check.sender.email}

-

- ID: {check.id} -

-
-
- -
-
-); +const CheckHeader: React.FC = ({ check }) => { + const t = useTranslations('DetailPage'); -const SubmissionInfoCard: React.FC = ({ check }) => ( - } accent="blue"> - } - value={ - - - {check.sender.name} - - } - /> - +
+
+ +
+

+ {check.sender.name} +

+

{check.sender.email}

+

+ {t('id')}: {check.id} +

+
+
+ +
+ + ); +}; + +const SubmissionInfoCard: React.FC = ({ check }) => { + const t = useTranslations('DetailPage'); + + return ( + } - value={ - - - - {check.fileName} + accent="blue" + > + } + value={ + + + {check.sender.name} - - } - /> - {formatFileSize(check.fileSize)} - } - /> - } - value={formatDate(check.submittedAt)} - /> - } - value={ - - {formatCurrency(check.paymentAmount, check.currency)} - - } - /> - -); + } + /> + } + value={ + + + + {check.fileName} + + + } + /> + + {formatFileSize(check.fileSize)} + + } + /> + } + value={formatDate(check.submittedAt)} + /> + } + value={ + + {formatCurrency(check.paymentAmount, check.currency)} + + } + /> +
+ ); +}; const ResultCard: React.FC = ({ check }) => { + const t = useTranslations('DetailPage'); + if (check.status === 'processing' || check.status === 'pending') { return ( - } accent="violet"> + } + accent="violet" + >
= ({ check }) => {

- Analysis in progress + {t('analysisInProgress')}

- Results will appear once processing is complete. + {t('resultsReadyAfterProcessing')}

@@ -276,8 +297,12 @@ const ResultCard: React.FC = ({ check }) => { if (!check.result) { return ( - } accent="violet"> -

No result available.

+ } + accent="violet" + > +

{t('noResultAvailable')}

); } @@ -286,7 +311,7 @@ const ResultCard: React.FC = ({ check }) => { return ( } accent={ result.similarityLevel === 'low' @@ -309,13 +334,13 @@ const ResultCard: React.FC = ({ check }) => {

{result.checkedWords.toLocaleString()}

-

Words Checked

+

{t('wordsChecked')}

{result.matchedWords.toLocaleString()}

-

Words Matched

+

{t('wordsMatched')}

@@ -323,7 +348,7 @@ const ResultCard: React.FC = ({ check }) => { {result.sources.length > 0 && (

- Matched Sources + {t('matchedSources')}

{result.sources.map((src, i) => ( @@ -352,7 +377,7 @@ const ResultCard: React.FC = ({ check }) => { {src.matchPercentage}%

- {src.matchedWords.toLocaleString()} words + {src.matchedWords.toLocaleString()} {t('words')}

@@ -362,7 +387,7 @@ const ResultCard: React.FC = ({ check }) => { )} } value={formatDate(result.processedAt)} /> @@ -372,19 +397,19 @@ const ResultCard: React.FC = ({ check }) => { }; const CertificateCard: React.FC = ({ check }) => { + const t = useTranslations('DetailPage'); + if (!check.certificate) { return ( - } accent="violet"> + } accent="violet">
-

- No certificate issued for this check. -

+

{t('noCertificate')}

{check.result?.similarityLevel === 'high' && (

- Certificates are not issued for high-similarity results. + {t('noCertificateHighSimilarity')}

)}
@@ -395,7 +420,7 @@ const CertificateCard: React.FC = ({ check }) => { const { certificate } = check; return ( - } accent="green"> + } accent="green"> {/* Certificate visual */}
@@ -414,21 +439,21 @@ const CertificateCard: React.FC = ({ check }) => { {certificate.verificationCode}

- Certificate ID: {certificate.id} + {t('certificateId')}: {certificate.id}

} value={formatDate(certificate.issuedAt)} /> } value={formatDate(certificate.expiresAt)} /> - + @@ -466,6 +491,7 @@ interface PlagiarismDetailPageProps { export const PlagiarismDetailPage: React.FC = ({ checkId, }) => { + const t = useTranslations('DetailPage'); const { check, loadingState, error, reload } = usePlagiarismDetail(checkId); return ( @@ -473,7 +499,7 @@ export const PlagiarismDetailPage: React.FC = ({
{loadingState === 'loading' && } {loadingState === 'error' && ( - + )} {loadingState === 'success' && check && ( diff --git a/src/widgets/fileUpload/ui/Plagiraismcheckform.tsx b/src/widgets/fileUpload/ui/Plagiraismcheckform.tsx index d1e2f5e..0d690f8 100644 --- a/src/widgets/fileUpload/ui/Plagiraismcheckform.tsx +++ b/src/widgets/fileUpload/ui/Plagiraismcheckform.tsx @@ -11,6 +11,7 @@ import { } from './Plagiraismui'; import { usePlagiarismForm } from '../lib/usePlagiraism'; import { PaymentModal } from '@/widgets/paymentModal/ui/Paymentmodal'; +import { useTranslations } from 'next-intl'; // ─── UserIcon (inline) ─────────────────────────────────────────────────────── @@ -35,6 +36,8 @@ function UserIcon() { // ─── Component ─────────────────────────────────────────────────────────────── export function PlagiarismCheckForm() { + const t = useTranslations('PlagiarismCheck'); + const { form, errors, @@ -59,14 +62,13 @@ export function PlagiarismCheckForm() {
- Originality Check + {t('badge')}

- Submit Your Document + {t('title')}

- Upload a document to verify its originality. Results are typically - ready within a few minutes. + {t('description')}

@@ -86,6 +88,7 @@ export function PlagiarismCheckForm() { status="success" message={`Submission successful! ID: ${submission.response.submissionId}. ${submission.response.message}`} onDismiss={resetSubmission} + dismissText={t('dismiss')} /> )} {submission.status === 'error' && submission.error && ( @@ -93,6 +96,7 @@ export function PlagiarismCheckForm() { status="error" message={submission.error} onDismiss={resetSubmission} + dismissText={t('dismiss')} /> )} @@ -100,7 +104,7 @@ export function PlagiarismCheckForm() {
{/* Topic */} setTopic(e.target.value)} hasError={!!errors.topic} @@ -118,21 +122,24 @@ export function PlagiarismCheckForm() { {/* Sender Full Name (read-only) */} - + } + autoFilledText={t('autoFilled')} /> {/* Certificate Option */}

- Certificate Option + {t('certificateOption')}

@@ -141,7 +148,7 @@ export function PlagiarismCheckForm() {
{/* File Upload */} @@ -149,6 +156,9 @@ export function PlagiarismCheckForm() { file={form.file} onFileChange={setFile} hasError={!!errors.file} + clickToUploadText={t('clickToUpload')} + fileTypesText={t('fileTypes')} + removeFileAriaLabel={t('removeFile')} /> @@ -156,7 +166,11 @@ export function PlagiarismCheckForm() {
{/* Submit */} - +
diff --git a/src/widgets/fileUpload/ui/Plagiraismui.tsx b/src/widgets/fileUpload/ui/Plagiraismui.tsx index 28edfca..8123dc3 100644 --- a/src/widgets/fileUpload/ui/Plagiraismui.tsx +++ b/src/widgets/fileUpload/ui/Plagiraismui.tsx @@ -71,15 +71,20 @@ export function TextInput({ interface ReadonlyFieldProps { value: string; icon?: React.ReactNode; + autoFilledText?: string; } -export function ReadonlyField({ value, icon }: ReadonlyFieldProps) { +export function ReadonlyField({ + value, + icon, + autoFilledText = 'Auto-filled', +}: ReadonlyFieldProps) { return (
{icon && {icon}} {value} - Auto-filled + {autoFilledText}
); @@ -92,6 +97,9 @@ interface FileUploadFieldProps { onFileChange: (file: File | null) => void; hasError?: boolean; accept?: string; + clickToUploadText?: string; + fileTypesText?: string; + removeFileAriaLabel?: string; } export function FileUploadField({ @@ -99,6 +107,9 @@ export function FileUploadField({ onFileChange, hasError, accept = '.pdf,.doc,.docx,.txt', + clickToUploadText = 'Click to upload document', + fileTypesText = 'PDF, DOC, DOCX, TXT · Max 20 MB', + removeFileAriaLabel = 'Remove file', }: FileUploadFieldProps) { const inputRef = React.useRef(null); @@ -163,11 +174,9 @@ export function FileUploadField({

- Click to upload document -

-

- PDF, DOC, DOCX, TXT · Max 20 MB + {clickToUploadText}

+

{fileTypesText}

) : ( @@ -185,7 +194,7 @@ export function FileUploadField({ type="button" onClick={handleRemove} className="w-7 h-7 rounded-lg flex items-center justify-center text-stone-400 hover:text-rose-500 hover:bg-rose-100 transition-colors shrink-0" - aria-label="Remove file" + aria-label={removeFileAriaLabel} > @@ -200,11 +209,15 @@ export function FileUploadField({ interface CertificateCheckboxProps { checked: boolean; onChange: () => void; + title?: string; + description?: string; } export function CertificateCheckbox({ checked, onChange, + title = 'Return result with certificate', + description = 'An official certificate will be attached to your originality report.', }: CertificateCheckboxProps) { return ( ); @@ -261,9 +270,15 @@ export function CertificateCheckbox({ interface SubmitButtonProps { isLoading: boolean; + submittingText?: string; + submitText?: string; } -export function SubmitButton({ isLoading }: SubmitButtonProps) { +export function SubmitButton({ + isLoading, + submittingText = 'Submitting…', + submitText = 'Submit for Originality Check', +}: SubmitButtonProps) { return ( @@ -300,12 +315,14 @@ interface StatusBannerProps { status: 'success' | 'error'; message: string; onDismiss: () => void; + dismissText?: string; } export function StatusBanner({ status, message, onDismiss, + dismissText = 'Dismiss', }: StatusBannerProps) { const isSuccess = status === 'success'; useEffect(() => { @@ -334,7 +351,7 @@ export function StatusBanner({ onClick={onDismiss} className={`text-xs font-bold uppercase ${isSuccess ? 'text-emerald-600 hover:text-emerald-800' : 'text-rose-600 hover:text-rose-800'}`} > - Dismiss + {dismissText} ); diff --git a/src/widgets/footer/lib/data.ts b/src/widgets/footer/lib/data.ts index b3ffbe9..1194362 100644 --- a/src/widgets/footer/lib/data.ts +++ b/src/widgets/footer/lib/data.ts @@ -1,31 +1,31 @@ -const sections = [ +const getSections = (t: (key: string) => string) => [ { - title: 'Product', + title: t('product'), links: [ - { name: 'Overview', href: '#' }, - { name: 'Pricing', href: '#' }, - { name: 'Marketplace', href: '#' }, - { name: 'Features', href: '#' }, + { name: t('overview'), href: '#' }, + { name: t('pricing'), href: '#' }, + { name: t('marketplace'), href: '#' }, + { name: t('features'), href: '#' }, ], }, { - title: 'Company', + title: t('company'), links: [ - { name: 'About', href: '#' }, - { name: 'Team', href: '#' }, - { name: 'Blog', href: '#' }, - { name: 'Careers', href: '#' }, + { name: t('about'), href: '#' }, + { name: t('team'), href: '#' }, + { name: t('blog'), href: '#' }, + { name: t('careers'), href: '#' }, ], }, { - title: 'Resources', + title: t('resources'), links: [ - { name: 'Help', href: '#' }, - { name: 'Sales', href: '#' }, - { name: 'Advertise', href: '#' }, - { name: 'Privacy', href: '#' }, + { name: t('help'), href: '#' }, + { name: t('sales'), href: '#' }, + { name: t('advertise'), href: '#' }, + { name: t('privacy'), href: '#' }, ], }, ]; -export { sections }; +export { getSections }; diff --git a/src/widgets/footer/ui/index.tsx b/src/widgets/footer/ui/index.tsx index 01a5988..14aaa84 100644 --- a/src/widgets/footer/ui/index.tsx +++ b/src/widgets/footer/ui/index.tsx @@ -1,4 +1,8 @@ +import { useTranslations } from 'next-intl'; + const Footer = () => { + const t = useTranslations('Footer'); + // const shortLinks = [ // { name: 'About', href: '/about' }, // { name: 'Contact', href: '/contact' }, @@ -7,13 +11,10 @@ const Footer = () => {
-

- © {new Date().getFullYear()} Felix IT Solutions. All rights - reserved. -

+

{t('copyright', { year: new Date().getFullYear() })}

diff --git a/src/widgets/history/lib/constants.ts b/src/widgets/history/lib/constants.ts index 62f9269..819e00f 100644 --- a/src/widgets/history/lib/constants.ts +++ b/src/widgets/history/lib/constants.ts @@ -3,34 +3,34 @@ import { CheckResult } from './types'; export const TABLE_COLUMNS = [ - { key: 'senderFullName', label: 'Sender' }, - { key: 'fileName', label: 'File' }, - { key: 'date', label: 'Date' }, - { key: 'paymentAmount', label: 'Amount' }, - { key: 'result', label: 'Result' }, - { key: 'actions', label: '' }, + { key: 'senderFullName', labelKey: 'sender' }, + { key: 'fileName', labelKey: 'file' }, + { key: 'date', labelKey: 'date' }, + { key: 'paymentAmount', labelKey: 'amount' }, + { key: 'result', labelKey: 'result' }, + { key: 'actions', labelKey: 'actions' }, ] as const; // ─── Result Labels & Styles ──────────────────────────────────────────────────── export const RESULT_CONFIG: Record< CheckResult, - { label: string; className: string } + { labelKey: string; className: string } > = { clean: { - label: 'Clean', + labelKey: 'resultClean', className: 'bg-emerald-50 text-emerald-700 ring-1 ring-emerald-200', }, plagiarism_found: { - label: 'Plagiarism Found', + labelKey: 'resultPlagiarismFound', className: 'bg-red-50 text-red-700 ring-1 ring-red-200', }, pending: { - label: 'Pending', + labelKey: 'resultPending', className: 'bg-amber-50 text-amber-700 ring-1 ring-amber-200', }, failed: { - label: 'Failed', + labelKey: 'resultFailed', className: 'bg-slate-100 text-slate-500 ring-1 ring-slate-200', }, }; diff --git a/src/widgets/history/ui/historyPage.tsx b/src/widgets/history/ui/historyPage.tsx index a0e1231..6423a44 100644 --- a/src/widgets/history/ui/historyPage.tsx +++ b/src/widgets/history/ui/historyPage.tsx @@ -1,21 +1,24 @@ 'use client'; import React from 'react'; +import { useTranslations } from 'next-intl'; import { useHistory } from '../lib/useHistory'; import { HistoryTable } from './historyTable'; import { Pagination } from './pagination'; // ─── Page Header ─────────────────────────────────────────────────────────────── -const PageHeader: React.FC = () => ( -
-

- Check History -

-

- All plagiarism checks submitted by you -

-
-); +const PageHeader: React.FC = () => { + const t = useTranslations('HistoryPage'); + + return ( +
+

+ {t('title')} +

+

{t('description')}

+
+ ); +}; // ─── HistoryPage ─────────────────────────────────────────────────────────────── diff --git a/src/widgets/history/ui/historyTable.tsx b/src/widgets/history/ui/historyTable.tsx index 5ebfec7..c65b7ea 100644 --- a/src/widgets/history/ui/historyTable.tsx +++ b/src/widgets/history/ui/historyTable.tsx @@ -1,5 +1,6 @@ 'use client'; import React from 'react'; +import { useTranslations } from 'next-intl'; import { TABLE_COLUMNS } from '../lib/constants'; import { EmptyState, ErrorState, SkeletonRow } from './tableStates'; import { HistoryTableRow } from './historyTableRow'; @@ -12,23 +13,27 @@ interface HistoryTableFullProps extends HistoryTableProps { // ─── Table Header ────────────────────────────────────────────────────────────── -const TableHead: React.FC = () => ( - - - {TABLE_COLUMNS.map((col) => ( - - {col.label} - - ))} - - -); +const TableHead: React.FC = () => { + const t = useTranslations('HistoryPage'); + + return ( + + + {TABLE_COLUMNS.map((col) => ( + + {col.labelKey ? t(col.labelKey) : ''} + + ))} + + + ); +}; // ─── Table Body ──────────────────────────────────────────────────────────────── @@ -48,7 +53,7 @@ const TableBody: React.FC = ({ ); } - if (false) { + if (error) { return ( {})} /> @@ -56,7 +61,7 @@ const TableBody: React.FC = ({ ); } - if (false) { + if (items.length === 0) { return ( diff --git a/src/widgets/history/ui/historyTableRow.tsx b/src/widgets/history/ui/historyTableRow.tsx index 5406b5a..3cad5a7 100644 --- a/src/widgets/history/ui/historyTableRow.tsx +++ b/src/widgets/history/ui/historyTableRow.tsx @@ -1,5 +1,6 @@ 'use client'; import React from 'react'; +import { useTranslations } from 'next-intl'; import { HistoryTableRowProps } from '../lib/types'; import { formatDate, truncateFileName } from '../lib/utils'; import { ResultBadge } from './resultBadge'; @@ -7,6 +8,8 @@ import { useRouter } from '@/shared/config/i18n/navigation'; export const HistoryTableRow: React.FC = ({ item }) => { const router = useRouter(); + const t = useTranslations('HistoryPage'); + return ( {/* Sender */} @@ -62,7 +65,7 @@ export const HistoryTableRow: React.FC = ({ item }) => {
- - - - -); + + + ); +}; diff --git a/src/widgets/navbar/lib/data.ts b/src/widgets/navbar/lib/data.ts index 4d5e68f..fb29a8e 100644 --- a/src/widgets/navbar/lib/data.ts +++ b/src/widgets/navbar/lib/data.ts @@ -1,8 +1,8 @@ import { MenuItem } from './model'; import { LanguageRoutes } from '@/shared/config/i18n/types'; -const menu: MenuItem[] = [ - { title: 'About Site', url: '/about' }, +const getMenu = (t: (key: string) => string): MenuItem[] => [ + { title: t('aboutSite'), url: '/about' }, // { // title: 'Products', // url: '#', @@ -16,7 +16,7 @@ const menu: MenuItem[] = [ // ], // }, { - title: 'Contact', + title: t('contact'), url: '/contact', }, ]; @@ -36,4 +36,4 @@ const languages: { name: string; key: LanguageRoutes }[] = [ }, ]; -export { menu, languages }; +export { getMenu, languages }; diff --git a/src/widgets/navbar/ui/authButtons.tsx b/src/widgets/navbar/ui/authButtons.tsx index d1192c7..bcb1f58 100644 --- a/src/widgets/navbar/ui/authButtons.tsx +++ b/src/widgets/navbar/ui/authButtons.tsx @@ -11,16 +11,19 @@ import { import SubMenuLink from './SubMenuLink'; import { ChangeLang } from './ChangeLang'; import { useLoginModal, useRegisterModal } from '@/shared/zustand/auth'; +import { useTranslations } from 'next-intl'; function AuthButtons() { + const t = useTranslations('Navbar'); + const auth = { - login: { title: 'Login', url: '#' }, - signup: { title: 'Sign up', url: '#' }, + login: { title: t('login'), url: '#' }, + signup: { title: t('signup'), url: '#' }, }; const userItem = [ - { title: 'Profile', url: '/profile' }, - { title: 'Logout', url: '#' }, + { title: t('profile'), url: '/profile' }, + { title: t('logout'), url: '#' }, ]; const toggleLoginModal = useLoginModal((state) => state.toggleLoginModal); diff --git a/src/widgets/navbar/ui/index.tsx b/src/widgets/navbar/ui/index.tsx index 12edd7b..6f1abb8 100644 --- a/src/widgets/navbar/ui/index.tsx +++ b/src/widgets/navbar/ui/index.tsx @@ -8,13 +8,17 @@ import { SheetTrigger, } from '@/shared/ui/sheet'; import { Menu } from 'lucide-react'; -import { menu } from '../lib/data'; +import { getMenu } from '../lib/data'; import RenderMobileMenuItem from './RenderMobileMenuItem'; import { ChangeLang } from './ChangeLang'; import Link from 'next/link'; import { AuthButtons } from './authButtons'; +import { useTranslations } from 'next-intl'; const Navbar = () => { + const t = useTranslations('Navbar'); + const menu = getMenu(t); + return (
@@ -26,7 +30,7 @@ const Navbar = () => { href={'/'} className="flex items-center gap-2 text-2xl font-bold " > - Plagat + {t('logo')} {/*
@@ -44,7 +48,7 @@ const Navbar = () => {
{/* Logo */} - Plagat + {t('logo')}
@@ -59,7 +63,7 @@ const Navbar = () => { - Plagat + {t('logo')}