Import source: app/src/pages/NlpNormalizerPage

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:25:28 +03:30
parent 2fc1a1bfcb
commit f088f68971
+130
View File
@@ -0,0 +1,130 @@
import { useState } from 'react';
import { useNav, useToast } from '../../teknav-ui.jsx';
import { iconNode } from '../../src/lib/icon.jsx';
import clipboardIcon from '../../src/assets/icons/clipboard-copy.svg';
import { LegalPage, LegalSection } from './shared.jsx';
export function NlpNormalizerPage() {
const { navigate } = useNav();
const toast = useToast();
const [text, setText] = useState('');
const [normalized, setNormalized] = useState('');
const [options, setOptions] = useState({
stripHtml: true,
normalizeLetters: true,
normalizeZwnj: true,
cleanWhitespace: true,
});
const toggle = (k) => setOptions((o) => ({ ...o, [k]: !o[k] }));
const handleNormalize = () => {
let t = text || '';
if (options.stripHtml) {
t = t.replace(/<[^>]*>/g, ' ');
}
if (options.normalizeLetters) {
t = t.replace(/ي/g, 'ی').replace(/ك/g, 'ک').replace(/ة/g, 'ه');
}
if (options.normalizeZwnj) {
t = t.replace(/\s+(می|نمی)\s+/g, ' $1‌');
t = t.replace(/\s+(تر|ترین|ها|های|ام|ات|اش|مان|تان|شان)\b/g, '‌$1');
t = t.replace(/\b(می|نمی)\b\s+/g, '$1‌');
}
if (options.cleanWhitespace) {
t = t.replace(/\s+/g, ' ');
t = t.replace(/\n\s*\n+/g, '\n\n');
}
setNormalized(t.trim());
toast('متن با موفقیت نرمال‌سازی شد', 'success');
};
const copyToClipboard = (val) => {
if (!val) return;
navigator.clipboard.writeText(val);
toast('کپی شد!', 'success');
};
const textareaStyle = {
width: '100%',
minHeight: 180,
background: '#FAF7F0',
border: '1px solid #D8D0C4',
borderRadius: 8,
padding: '12px 16px',
fontSize: 14,
fontFamily: 'Vazirmatn, sans-serif',
color: '#263238',
outline: 'none',
lineHeight: 1.7,
resize: 'vertical',
boxSizing: 'border-box',
};
return (
<LegalPage title="نرمال‌ساز متن فارسی (NLP)" subtitle="اصلاح نویسه‌ها، تنظیم فاصله‌ها و بهینه‌سازی متون فارسی برای ابزارهای پردازش زبان طبیعی و مدل‌های زبانی.">
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
<div>
<label style={{ fontSize: 13, fontWeight: 700, color: '#0F2A2E', display: 'block', marginBottom: 8 }}>متن ورودی:</label>
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="متن فارسی خود را برای نرمال‌سازی اینجا وارد کنید..."
style={textareaStyle}
/>
</div>
<div style={{ background: '#FAF7F0', border: '1px solid #E4DDD2', borderRadius: 10, padding: 18 }}>
<h3 style={{ fontSize: 14, fontWeight: 800, color: '#0F2A2E', marginBottom: 12 }}>تنظیمات نرمال‌سازی</h3>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 12 }}>
{[
['stripHtml', 'حذف تگ‌های HTML'],
['normalizeLetters', 'تبدیل ی و ک عربی به فارسی'],
['normalizeZwnj', 'تنظیم نیم‌فاصله‌های پیشوند/پسوند'],
['cleanWhitespace', 'اصلاح فاصله‌ها و تب‌های مکرر'],
].map(([k, label]) => (
<label key={k} style={{ display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer', fontSize: 13, color: '#374549' }}>
<input type="checkbox" checked={options[k]} onChange={() => toggle(k)} style={{ accentColor: '#0F6B73' }} />
<span>{label}</span>
</label>
))}
</div>
</div>
<div style={{ display: 'flex', gap: 12 }}>
<button onClick={handleNormalize} style={{ background: '#0F6B73', color: '#fff', border: 'none', borderRadius: 8, padding: '12px 28px', fontSize: 14, fontWeight: 600, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
نرمال‌سازی متن
</button>
<button onClick={() => navigate('/tools')} style={{ background: 'transparent', color: '#5F6B6D', border: '1px solid #D8D0C4', borderRadius: 8, padding: '12px 20px', fontSize: 14, fontWeight: 600, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif' }}>
بازگشت
</button>
</div>
{normalized && (
<div style={{ marginTop: 10 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
<label style={{ fontSize: 13, fontWeight: 700, color: '#0F2A2E' }}>متن خروجی:</label>
<button onClick={() => copyToClipboard(normalized)} style={{ background: '#FAF7F0', border: '1px solid #D8D0C4', borderRadius: 6, padding: '6px 12px', fontSize: 12, cursor: 'pointer', fontFamily: 'Vazirmatn,sans-serif', display: 'flex', alignItems: 'center', gap: 6 }}>
<span className="tek-icon" style={{ ['--i']: `url("${clipboardIcon}")` }} aria-hidden="true" /> کپی خروجی
</button>
</div>
<textarea
readOnly
value={normalized}
style={{ ...textareaStyle, background: '#fff' }}
/>
</div>
)}
<div style={{ borderTop: '1px solid #E4DDD2', paddingTop: 20, marginTop: 10 }}>
<h3 style={{ fontSize: 14, fontWeight: 800, color: '#0F2A2E', marginBottom: 8 }}>ارجاع علمی و اشتراک‌گذاری</h3>
<p style={{ color: '#5F6B6D', fontSize: 12, lineHeight: 1.7, marginBottom: 12 }}>برای استفاده از خروجی‌های این ابزار در تحقیقات یا کارهای متن‌باز خود، می‌توانید از فرمت ارجاع زیر استفاده کنید:</p>
<div style={{ background: '#FAF7F0', border: '1px solid #E4DDD2', borderRadius: 8, padding: 12, fontSize: 11, fontFamily: 'monospace', direction: 'ltr', textAlign: 'left', overflowX: 'auto', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<code style={{ color: '#0F2A2E' }}>[Teknav Persian NLP Tool] (https://www.teknav.ir/tools/nlp-normalizer)</code>
<button onClick={() => copyToClipboard('[Teknav Persian NLP Tool] (https://www.teknav.ir/tools/nlp-normalizer)')} style={{ background: '#fff', border: '1px solid #D8D0C4', borderRadius: 4, padding: '2px 8px', fontSize: 10, cursor: 'pointer', fontFamily: 'sans-serif' }}>Copy</button>
</div>
</div>
</div>
</LegalPage>
);
}