detail page text translation added

This commit is contained in:
nabijonovdavronbek619@gmail.com
2026-04-03 14:19:03 +05:00
parent b6dfde7337
commit 9ca13fceda
6 changed files with 131 additions and 34 deletions

View File

@@ -2,6 +2,8 @@
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useTranslations } from 'next-intl';
import { useParams } from 'next/navigation';
import { links } from '@/shared/request/links';
import { apiRequest } from '@/shared/request/apiRequest';
import Sertifikat from './sertifikat';
@@ -69,8 +71,8 @@ function parseHighlightedText(text_res: string): React.ReactNode[] {
});
}
function formatDate(iso: string): string {
return new Date(iso).toLocaleString('uz-UZ', {
function formatDate(iso: string, locale: string): string {
return new Date(iso).toLocaleString(locale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
@@ -243,8 +245,9 @@ function LoadingSkeleton() {
}
// ── Error State ───────────────────────────────────────────────────────────────
type TFunction = ReturnType<typeof useTranslations>;
function ErrorState({ message }: { message?: string }) {
function ErrorState({ message, t }: { message?: string; t: TFunction }) {
return (
<div className="max-w-5xl mx-auto px-6 py-20 flex flex-col items-center gap-4 text-center">
<div className="w-14 h-14 rounded-full bg-red-50 flex items-center justify-center">
@@ -262,9 +265,11 @@ function ErrorState({ message }: { message?: string }) {
/>
</svg>
</div>
<p className="text-slate-700 font-semibold">Failed to load document</p>
<p className="text-slate-700 font-semibold">
{t('failedToLoadDocument')}
</p>
<p className="text-slate-400 text-sm">
{message ?? 'An unexpected error occurred.'}
{message ?? t('unexpectedError')}
</p>
</div>
);
@@ -273,6 +278,9 @@ function ErrorState({ message }: { message?: string }) {
// ── Main Page ─────────────────────────────────────────────────────────────────
export default function DocumentDetailPage({ id }: { id: number }) {
const t = useTranslations('DetailPage');
const { locale } = useParams() as { locale: string };
const {
data: doc,
isLoading,
@@ -305,7 +313,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
return (
<div className="min-h-screen bg-slate-50">
<header className="bg-white border-b border-slate-200 h-16.25" />
<ErrorState message={(error as Error)?.message} />
<ErrorState message={(error as Error)?.message} t={t} />
</div>
);
}
@@ -329,9 +337,9 @@ export default function DocumentDetailPage({ id }: { id: number }) {
low: 'text-emerald-600 bg-emerald-50 border-emerald-200',
};
const aiLabel: Record<string, string> = {
high: 'AI Content Detected',
medium: 'Possible AI Content',
low: 'Likely Original',
high: t('aiContentDetected'),
medium: t('possibleAiContent'),
low: t('likelyOriginal'),
};
// Partition analyze_text dynamically:
@@ -377,7 +385,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
</button>
<div>
<p className="text-[11px] uppercase tracking-widest text-slate-400 font-semibold">
Document #{doc.id}
{t('documentNumber', { id: doc.id })}
</p>
<h1 className="text-base font-bold text-slate-800 leading-tight">
{doc.title}
@@ -407,7 +415,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
Download PDF
{t('downloadPdf')}
</a>
)}
</div>
@@ -431,7 +439,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
Created: {formatDate(doc.created_at)}
{t('created')}: {formatDate(doc.created_at, locale)}
</span>
<span className="flex items-center gap-1.5">
<svg
@@ -447,7 +455,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
Updated: {formatDate(doc.updated_at)}
{t('updated')}: {formatDate(doc.updated_at, locale)}
</span>
{res?.hash && (
<span className="flex items-center gap-1.5 font-mono">
@@ -464,14 +472,14 @@ export default function DocumentDetailPage({ id }: { id: number }) {
d="M7 20l4-16m2 16l4-16M6 9h14M4 15h14"
/>
</svg>
Hash: {res.hash}
{t('hash')}: {res.hash}
</span>
)}
</div>
{/* ── Score Overview ── */}
{res && (
<Section title="Analysis Scores" accent="bg-violet-500">
<Section title={t('analysisScores')} accent="bg-violet-500">
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-8">
<div className="flex flex-wrap justify-around gap-8">
<ScoreRing
@@ -511,8 +519,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
<div>
<p className="font-bold text-sm">{aiLabel[aiRisk]}</p>
<p className="text-xs opacity-75 mt-0.5">
AI probability score: <strong>{res.ai}%</strong> content
may have been generated or assisted by an AI model.
{t('aiProbabilityText', { score: res.ai })}
</p>
</div>
</div>
@@ -522,7 +529,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
{/* ── Document Text ── */}
{(res?.text || textRes) && (
<Section title="Document Text" accent="bg-blue-500">
<Section title={t('documentText')} accent="bg-blue-500">
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
<div className="flex border-b border-slate-100">
{(['highlighted', 'plain'] as const).map((tab) => (
@@ -536,8 +543,8 @@ export default function DocumentDetailPage({ id }: { id: number }) {
}`}
>
{tab === 'highlighted'
? 'Plagiarism Highlights'
: 'Plain Text'}
? t('plagiarismHighlights')
: t('plainText')}
</button>
))}
</div>
@@ -553,7 +560,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
{activeTab === 'highlighted' && (
<div className="px-6 pb-5 flex items-center gap-2 text-xs text-slate-500">
<span className="inline-block w-3 h-3 rounded-sm bg-amber-200 border border-amber-300" />
Highlighted fragments indicate potential plagiarism matches
{t('highlightedHint')}
</div>
)}
</div>
@@ -562,7 +569,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
{/* ── Text Statistics (all numeric / non-boolean keys) ── */}
{statKeys.length > 0 && (
<Section title="Text Statistics" accent="bg-teal-500">
<Section title={t('textStatistics')} accent="bg-teal-500">
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
{statKeys.map((key) => (
<StatCard key={key} label={key} value={analyze[key]} />
@@ -573,7 +580,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
{/* ── Detection Flags (all boolean Yes/No keys) ── */}
{flagKeys.length > 0 && (
<Section title="Detection Flags" accent="bg-rose-500">
<Section title={t('detectionFlags')} accent="bg-rose-500">
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
{flagKeys.map((key) => {
const val = analyze[key];
@@ -607,7 +614,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
{/* ── Top Words (key detected by "топ"/"top" substring) ── */}
{topWordsValue && (
<Section title="Top Words" accent="bg-orange-500">
<Section title={t('topWords')} accent="bg-orange-500">
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-6">
<div className="flex flex-wrap gap-2">
{topWordsValue
@@ -639,7 +646,10 @@ export default function DocumentDetailPage({ id }: { id: number }) {
{/* ── Footer ── */}
{result && (
<footer className="text-center text-xs text-slate-400 pb-10 mt-4">
Result ID #{result.id} · Analyzed {formatDate(result.created_at)}
{t('resultFooter', {
id: result.id,
date: formatDate(result.created_at, locale),
})}
</footer>
)}
</main>

View File

@@ -86,7 +86,7 @@ export function PlagiarismCheckForm() {
{submission.status === 'success' && (
<StatusBanner
status="success"
message={`Submission successful! ID`}
message={t('submissionSuccess')}
onDismiss={resetSubmission}
dismissText={t('dismiss')}
/>
@@ -177,8 +177,7 @@ export function PlagiarismCheckForm() {
{/* Footer note */}
<p className="text-center text-xs text-stone-400 mt-5">
Your document is processed securely and not stored beyond the
analysis period.
{t('secureNote')}
</p>
</div>
</div>