|
<!DOCTYPE html> |
|
<html lang="ar" dir="rtl"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>نظام المقارنة والتحليل المتقدم - شركة موندو لينجوا</title> |
|
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet"> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> |
|
<style> |
|
|
|
|
|
|
|
@keyframes gradient { |
|
0% { background-position: 0% 50%; } |
|
50% { background-position: 100% 50%; } |
|
100% { background-position: 0% 50%; } |
|
} |
|
.animate-gradient { |
|
background-size: 200% 200%; |
|
animation: gradient 15s ease infinite; |
|
} |
|
.transition-all { transition: all 0.3s ease-in-out; } |
|
.animate-scale { transition: transform 0.2s ease-in-out; } |
|
.animate-scale:hover { transform: scale(1.02); } |
|
.pulse-animation { animation: pulse 2s infinite; } |
|
@keyframes pulse { |
|
0% { box-shadow: 0 0 0 0 rgba(0,0,0,0.2); } |
|
70% { box-shadow: 0 0 0 10px rgba(0,0,0,0); } |
|
100% { box-shadow: 0 0 0 0 rgba(0,0,0,0); } |
|
} |
|
|
|
|
|
|
|
|
|
.text-comparison { line-height: 1.8; white-space: pre-wrap; } |
|
.highlight-missing { |
|
background-color: #f87171; |
|
color: #fff; |
|
padding: 0 4px; |
|
border-radius: 3px; |
|
font-weight: bold; |
|
} |
|
.highlight-number { |
|
background-color: #facc15; |
|
color: #000; |
|
padding: 0 4px; |
|
border-radius: 3px; |
|
font-weight: bold; |
|
} |
|
.highlight-meaning { |
|
background-color: #60a5fa; |
|
color: #fff; |
|
padding: 0 4px; |
|
border-radius: 3px; |
|
font-weight: bold; |
|
} |
|
|
|
|
|
|
|
|
|
.line-item { |
|
margin-bottom: 1rem; |
|
padding: 0.5rem; |
|
border-bottom: 1px dashed #ccc; |
|
} |
|
.line-number { |
|
font-weight: bold; |
|
color: #4B5563; |
|
margin-left: 0.5rem; |
|
} |
|
.line-text { display: inline-block; } |
|
|
|
|
|
|
|
|
|
.card { |
|
background-color: #fff; |
|
border-radius: 1rem; |
|
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.1); |
|
padding: 2rem; |
|
border: 1px solid #f3f4f6; |
|
} |
|
.card-hover:hover { |
|
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.2); |
|
transform: translateY(-3px); |
|
} |
|
.icon { margin-right: 0.5rem; } |
|
</style> |
|
</head> |
|
<body class="bg-gradient-to-br from-gray-100 via-blue-100 to-indigo-100 min-h-screen"> |
|
<div class="min-h-screen pb-12"> |
|
|
|
<header class="bg-gradient-to-r from-indigo-700 via-purple-700 to-pink-700 animate-gradient text-white py-10 mb-10 shadow-xl"> |
|
<div class="max-w-6xl mx-auto px-4 text-center"> |
|
<h1 class="text-5xl font-bold mb-4 animate-scale"> |
|
<i class="fas fa-chart-line icon"></i> نظام المقارنة والتحليل المتقدم |
|
</h1> |
|
<p class="text-xl opacity-90"> |
|
<i class="fas fa-info-circle icon"></i> تحليل دقيق لاكتشاف النصوص المفقودة، الأرقام/التواريخ واختلاف المعنى |
|
</p> |
|
</div> |
|
</header> |
|
|
|
|
|
<main class="max-w-6xl mx-auto px-4 space-y-8"> |
|
|
|
<div class="card card-hover"> |
|
<h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6"> |
|
<i class="fas fa-file-upload icon text-indigo-600"></i> تحميل الملفات |
|
</h2> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
|
|
<div class="group border-2 border-dashed border-indigo-300 rounded-xl p-8 text-center bg-indigo-50 hover:bg-indigo-100 transition-colors duration-300"> |
|
<label class="cursor-pointer block"> |
|
<input type="file" id="sourceFile" accept=".docx,.pdf" class="hidden"> |
|
<i class="fas fa-upload text-5xl text-indigo-500 mb-4"></i> |
|
<span class="text-lg text-indigo-600 group-hover:text-indigo-700">ملف المصدر</span> |
|
</label> |
|
</div> |
|
|
|
<div class="group border-2 border-dashed border-pink-300 rounded-xl p-8 text-center bg-pink-50 hover:bg-pink-100 transition-colors duration-300"> |
|
<label class="cursor-pointer block"> |
|
<input type="file" id="targetFile" accept=".docx,.pdf" class="hidden"> |
|
<i class="fas fa-download text-5xl text-pink-500 mb-4"></i> |
|
<span class="text-lg text-pink-600 group-hover:text-pink-700">ملف الهدف</span> |
|
</label> |
|
</div> |
|
</div> |
|
<div id="processStatus" class="hidden mt-4"> |
|
<div class="flex items-center justify-center space-x-3 bg-indigo-100 rounded-xl p-4"> |
|
<div class="animate-spin h-8 w-8 border-4 border-indigo-600 rounded-full border-t-transparent"></div> |
|
<span class="text-lg text-indigo-700">جارٍ معالجة الملف...</span> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="card card-hover"> |
|
<div class="space-y-6"> |
|
|
|
<div class="group"> |
|
<label class="block text-lg font-bold text-gray-700 mb-3"> |
|
<i class="fas fa-language icon text-indigo-600"></i> النص المصدر |
|
</label> |
|
<textarea id="sourceText" dir="rtl" class="w-full px-6 py-4 border-2 border-gray-200 rounded-xl focus:ring-indigo-200 focus:border-indigo-400 transition-all resize-none text-lg" rows="6" placeholder="اكتب النص المصدر هنا..."></textarea> |
|
</div> |
|
|
|
<div class="group"> |
|
<label class="block text-lg font-bold text-gray-700 mb-3"> |
|
<i class="fas fa-language icon text-pink-600"></i> النص الهدف |
|
</label> |
|
<textarea id="targetText" dir="ltr" class="w-full px-6 py-4 border-2 border-gray-200 rounded-xl focus:ring-pink-200 focus:border-pink-400 transition-all resize-none text-lg" rows="6" placeholder="اكتب النص الهدف هنا..."></textarea> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="card card-hover"> |
|
<h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6"> |
|
<i class="fas fa-book-open icon text-green-600"></i> مصادر إضافية |
|
</h2> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
|
|
<div class="group border-2 border-dashed border-green-300 rounded-xl p-8 text-center bg-green-50 hover:bg-green-100 transition-colors duration-300"> |
|
<label class="cursor-pointer block"> |
|
<input type="file" id="sourceExtraFile" accept=".docx,.pdf" class="hidden"> |
|
<i class="fas fa-upload text-5xl text-green-500 mb-4"></i> |
|
<span class="text-lg text-green-600 group-hover:text-green-700">تحميل ملف المصدر</span> |
|
</label> |
|
</div> |
|
|
|
<div class="group"> |
|
<label class="block text-lg font-bold text-gray-700 mb-3"> |
|
<i class="fas fa-edit icon text-green-600"></i> إدخال المصادر يدويًا |
|
</label> |
|
<textarea id="sourceExtraText" dir="rtl" class="w-full px-6 py-4 border-2 border-green-200 rounded-xl focus:ring-green-200 focus:border-green-400 transition-all resize-none text-lg" rows="6" placeholder="اكتب المصادر هنا..."></textarea> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<button id="submitBtn" class="w-full bg-gradient-to-r from-indigo-600 to-pink-600 hover:from-indigo-700 hover:to-pink-700 text-white font-bold py-5 px-8 rounded-xl transition-all transform hover:scale-105 focus:ring-indigo-200 text-xl shadow-lg hover:shadow-xl pulse-animation"> |
|
<div class="flex items-center justify-center"> |
|
<i class="fas fa-sync-alt icon ml-2"></i> تحليل ومراجعة النصوص |
|
</div> |
|
</button> |
|
|
|
|
|
<div id="resultSection" class="card hidden"> |
|
<h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6"> |
|
<i class="fas fa-search icon text-green-600"></i> نتائج التحليل والمقارنة |
|
</h2> |
|
<div id="errorsList" class="space-y-3 mb-6"></div> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
<div> |
|
<h4 class="text-lg font-bold text-gray-700 mb-3"> |
|
<i class="fas fa-file-alt icon text-indigo-600"></i> النص المصدر (مع التعليم) |
|
</h4> |
|
<div id="sourceTextReview" class="bg-indigo-50 rounded-xl p-6 min-h-[200px] border-2 border-indigo-100 text-comparison"></div> |
|
</div> |
|
<div> |
|
<h4 class="text-lg font-bold text-gray-700 mb-3"> |
|
<i class="fas fa-file-alt icon text-pink-600"></i> النص الهدف (مع التعليم) |
|
</h4> |
|
<div id="targetTextReview" class="bg-gray-50 rounded-xl p-6 min-h-[200px] border-2 border-gray-200 text-comparison"></div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="explanationBox" class="card hidden"> |
|
<h2 class="text-2xl font-bold text-gray-800 border-b pb-3 mb-6"> |
|
<i class="fas fa-info-circle icon text-green-600"></i> شرح الاختلافات |
|
</h2> |
|
<div id="explanationText" class="text-lg text-gray-700"></div> |
|
</div> |
|
</main> |
|
</div> |
|
|
|
|
|
|
|
|
|
<script> |
|
(function() { |
|
"use strict"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const API_URL = 'https://api.deepseek.com/chat/completions'; |
|
const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01'; |
|
const ANALYSIS_PROMPT = `أنت خبير لغوي وتقني متخصص في مراجعة الترجمة التقنية وتحليل النصوص بدقة. |
|
مهمتك مقارنة النص المصدر والنص الهدف واستخراج الاختلافات التالية: |
|
1. **النصوص المفقودة:** الكلمات أو العبارات التي لم تُترجم من النص المصدر (لا يوجد لها مقابل في النص الهدف). |
|
2. **الأرقام والتواريخ:** التي لا تتطابق بين النص المصدر والنص الهدف. |
|
3. **اختلاف المعنى:** في حال وجود اختلاف واضح في معنى النص. |
|
يُرجى تمييز: |
|
- النصوص المفقودة بوضعها بين علامتي __ والكلمة__. |
|
- الأرقام والتواريخ بوضعها بين علامتي < والرقم/التاريخ>. |
|
- اختلاف المعنى بوضعها بين [MEANING] و [/MEANING]. |
|
قدم الناتج في شكل قائمة تفصيلية مع شرح مختصر لكل اختلاف مع ذكر رقم السطر إن أمكن. |
|
|
|
النص المصدر: |
|
{source} |
|
|
|
النص الهدف: |
|
{target}`; |
|
|
|
|
|
|
|
|
|
const countWords = text => |
|
text.trim().split(/\s+/).filter(word => word !== "").length; |
|
|
|
const escapeRegExp = string => |
|
string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
|
|
|
|
|
const splitIntoLines = text => |
|
text.split(/\n+/).map((line, i) => { |
|
line = line.trim(); |
|
if(line && !line.endsWith('.')) { line += '.'; } |
|
return `<div class="line-item"><span class="line-number">${i+1}:</span><span class="line-text">${line}</span></div>`; |
|
}).join(''); |
|
|
|
|
|
const getLineNumber = (text, substring) => { |
|
const index = text.indexOf(substring); |
|
if (index === -1) return "غير محدد"; |
|
return text.substring(0, index).split("\n").length; |
|
}; |
|
|
|
|
|
|
|
|
|
const applyHighlights = (originalText, analysisOutput) => { |
|
let highlightedText = originalText; |
|
let match; |
|
|
|
const missingRegex = /__(.*?)__/g; |
|
while ((match = missingRegex.exec(analysisOutput)) !== null) { |
|
const phrase = match[1].trim(); |
|
if (phrase) { |
|
const replacement = `<span class="highlight-missing">${phrase}</span>`; |
|
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g'); |
|
highlightedText = highlightedText.replace(phraseRegex, replacement); |
|
} |
|
} |
|
|
|
const numberRegex = /<([^<>]+)>/g; |
|
while ((match = numberRegex.exec(analysisOutput)) !== null) { |
|
const phrase = match[1].trim(); |
|
if (phrase) { |
|
const replacement = `<span class="highlight-number">${phrase}</span>`; |
|
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g'); |
|
highlightedText = highlightedText.replace(phraseRegex, replacement); |
|
} |
|
} |
|
|
|
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g; |
|
while ((match = meaningRegex.exec(analysisOutput)) !== null) { |
|
const phrase = match[1].trim(); |
|
if (phrase) { |
|
const replacement = `<span class="highlight-meaning">${phrase}</span>`; |
|
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g'); |
|
highlightedText = highlightedText.replace(phraseRegex, replacement); |
|
} |
|
} |
|
return highlightedText; |
|
}; |
|
|
|
|
|
const generateExplanation = (sourceText, analysisOutput) => { |
|
let steps = []; |
|
let match; |
|
const iconMissing = `<i class="fas fa-exclamation-triangle mr-1"></i>`; |
|
const iconNumber = `<i class="fas fa-hashtag mr-1"></i>`; |
|
const iconMeaning = `<i class="fas fa-info-circle mr-1"></i>`; |
|
|
|
const missingRegex = /__(.*?)__/g; |
|
while ((match = missingRegex.exec(analysisOutput)) !== null) { |
|
const phrase = match[1].trim(); |
|
if (phrase) { |
|
const lineNum = getLineNumber(sourceText, phrase); |
|
steps.push(`<li>${iconMissing} في السطر ${lineNum}، النص المفقود: <span class="highlight-missing">${phrase}</span></li>`); |
|
} |
|
} |
|
|
|
const numberRegex = /<([^<>]+)>/g; |
|
while ((match = numberRegex.exec(analysisOutput)) !== null) { |
|
const phrase = match[1].trim(); |
|
if (phrase) { |
|
const lineNum = getLineNumber(sourceText, phrase); |
|
steps.push(`<li>${iconNumber} في السطر ${lineNum}، الرقم/التاريخ: <span class="highlight-number">${phrase}</span></li>`); |
|
} |
|
} |
|
|
|
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g; |
|
while ((match = meaningRegex.exec(analysisOutput)) !== null) { |
|
const phrase = match[1].trim(); |
|
if (phrase) { |
|
const lineNum = getLineNumber(sourceText, phrase); |
|
steps.push(`<li>${iconMeaning} في السطر ${lineNum}، اختلاف المعنى: <span class="highlight-meaning">${phrase}</span></li>`); |
|
} |
|
} |
|
if (steps.length === 0) { |
|
return `<p><i class="fas fa-check-circle mr-2"></i> لا توجد اختلافات ملحوظة بين النصين.</p>`; |
|
} |
|
return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`; |
|
}; |
|
|
|
|
|
|
|
|
|
const processFile = async file => { |
|
let text = ""; |
|
try { |
|
if (file.type === 'application/pdf') { |
|
const form = new FormData(); |
|
form.append('image', file); |
|
const response = await fetch('https://demo.api4ai.cloud/ocr/v1/results', { |
|
method: 'POST', |
|
body: form, |
|
headers: { 'A4A-CLIENT-APP-ID': 'sample' } |
|
}); |
|
const data = await response.json(); |
|
text = data.results?.[0]?.entities?.[0]?.objects?.[0]?.entities?.[0]?.text || ""; |
|
} else if (file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { |
|
const arrayBuffer = await file.arrayBuffer(); |
|
const result = await mammoth.extractRawText({ arrayBuffer }); |
|
text = result.value; |
|
} else { |
|
throw new Error('نوع الملف غير مدعوم'); |
|
} |
|
} catch (error) { |
|
console.error('Error processing file:', error); |
|
throw error; |
|
} |
|
return text; |
|
}; |
|
|
|
|
|
|
|
|
|
const processFileInput = (inputId, targetTextAreaId, errorMsg) => { |
|
document.getElementById(inputId)?.addEventListener('change', async (event) => { |
|
const file = event.target.files[0]; |
|
if (!file) return; |
|
document.getElementById('processStatus').classList.remove('hidden'); |
|
try { |
|
const text = await processFile(file); |
|
document.getElementById(targetTextAreaId).value = text; |
|
} catch (error) { |
|
addError(errorMsg); |
|
} finally { |
|
document.getElementById('processStatus').classList.add('hidden'); |
|
} |
|
}); |
|
}; |
|
|
|
|
|
processFileInput('sourceFile', 'sourceText', 'خطأ في معالجة ملف المصدر'); |
|
processFileInput('targetFile', 'targetText', 'خطأ في معالجة ملف الهدف'); |
|
processFileInput('sourceExtraFile', 'sourceExtraText', 'خطأ في معالجة ملف المصدر الإضافي'); |
|
|
|
|
|
|
|
|
|
const addError = (message, type = 'error') => { |
|
const errorsList = document.getElementById('errorsList'); |
|
if (!errorsList) return; |
|
const errorDiv = document.createElement('div'); |
|
errorDiv.className = `p-4 rounded-xl ${type === 'error' ? 'bg-red-50 text-red-700' : 'bg-yellow-50 text-yellow-700'}`; |
|
errorDiv.innerHTML = `<div class="flex items-center"> |
|
<i class="fas fa-${type === 'error' ? 'exclamation-circle' : 'info-circle'} mr-2"></i> |
|
<span>${message}</span> |
|
</div>`; |
|
errorsList.appendChild(errorDiv); |
|
}; |
|
|
|
|
|
|
|
|
|
document.getElementById('submitBtn').addEventListener('click', async () => { |
|
const sourceText = document.getElementById('sourceText').value; |
|
const targetText = document.getElementById('targetText').value; |
|
document.getElementById('errorsList').innerHTML = ''; |
|
document.getElementById('resultSection').classList.remove('hidden'); |
|
document.getElementById('explanationBox').classList.remove('hidden'); |
|
|
|
if (!sourceText || !targetText) { |
|
addError('يرجى إدخال كلا النصين المصدر والهدف'); |
|
return; |
|
} |
|
|
|
const sourceWordCount = countWords(sourceText); |
|
const targetWordCount = countWords(targetText); |
|
if (sourceWordCount !== targetWordCount) { |
|
addError(`عدد كلمات النص المصدر (${sourceWordCount}) يختلف عن النص الهدف (${targetWordCount})`, 'warning'); |
|
} |
|
|
|
try { |
|
const progressDiv = document.createElement('div'); |
|
progressDiv.className = "bg-indigo-100 p-4 rounded-xl mb-4"; |
|
progressDiv.innerHTML = `<div class="flex items-center"> |
|
<div class="animate-spin h-6 w-6 border-4 border-indigo-600 rounded-full border-t-transparent mr-3"></div> |
|
<span>جارٍ التحليل...</span> |
|
</div>`; |
|
document.getElementById('errorsList').appendChild(progressDiv); |
|
|
|
const prompt = ANALYSIS_PROMPT |
|
.replace("{source}", sourceText) |
|
.replace("{target}", targetText); |
|
|
|
const payload = { |
|
model: "deepseek-chat", |
|
messages: [ |
|
{ role: "system", content: "أنت خبير في تحليل ومراجعة النصوص بدقة عالية." }, |
|
{ role: "user", content: prompt } |
|
], |
|
temperature: 0.3, |
|
max_tokens: 2048, |
|
stream: false |
|
}; |
|
|
|
const response = await fetch(API_URL, { |
|
method: 'POST', |
|
headers: { |
|
'Authorization': 'Bearer ' + API_KEY, |
|
'Content-Type': 'application/json' |
|
}, |
|
body: JSON.stringify(payload) |
|
}); |
|
|
|
if (!response.ok) { |
|
throw new Error('حدث خطأ بالشبكة: ' + response.statusText); |
|
} |
|
const data = await response.json(); |
|
const analysisOutput = data.choices[0].message.content.trim(); |
|
progressDiv.remove(); |
|
|
|
|
|
if (analysisOutput.includes('[MATCH]')) { |
|
const checkDiv = document.createElement('div'); |
|
checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center"; |
|
checkDiv.innerHTML = `<i class="fas fa-check-circle mr-2"></i> <span>النصوص متطابقة تماماً</span>`; |
|
document.getElementById('errorsList').appendChild(checkDiv); |
|
document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceText); |
|
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText); |
|
document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا يوجد اختلاف يجب الإشارة إليه.</p>`; |
|
} else { |
|
const sourceHighlighted = applyHighlights(sourceText, analysisOutput); |
|
const targetHighlighted = applyHighlights(targetText, analysisOutput); |
|
document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted); |
|
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted); |
|
const explanationHTML = generateExplanation(sourceText, analysisOutput); |
|
document.getElementById('explanationText').innerHTML = explanationHTML; |
|
} |
|
} catch (error) { |
|
document.getElementById('errorsList').innerHTML = ''; |
|
addError(`خطأ في التحليل: ${error.message}`); |
|
} |
|
}); |
|
})(); |
|
</script> |
|
</body> |
|
</html> |
|
|