Update index.html
Browse files- index.html +136 -166
index.html
CHANGED
@@ -36,20 +36,34 @@
|
|
36 |
================================= */
|
37 |
.text-comparison { line-height: 1.8; white-space: pre-wrap; }
|
38 |
.highlight-number {
|
39 |
-
background-color: #FDE68A;
|
40 |
padding: 0 4px;
|
41 |
border-radius: 3px;
|
42 |
font-weight: bold;
|
43 |
}
|
44 |
.highlight-missing {
|
45 |
-
background-color: #
|
|
|
46 |
padding: 0 4px;
|
47 |
border-radius: 3px;
|
48 |
font-style: italic;
|
49 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
.highlight-meaning {
|
51 |
-
background-color: #
|
52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
padding: 0 4px;
|
54 |
border-radius: 3px;
|
55 |
font-weight: bold;
|
@@ -178,7 +192,7 @@
|
|
178 |
<!-- زر التحليل -->
|
179 |
<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 mb-8 pulse-animation">
|
180 |
<div class="flex items-center justify-center">
|
181 |
-
<i class="fas fa-sync-alt ml-2"></i> تحليل النصوص
|
182 |
</div>
|
183 |
</button>
|
184 |
|
@@ -213,30 +227,6 @@
|
|
213 |
</h2>
|
214 |
<div id="explanationText" class="text-lg text-gray-700"></div>
|
215 |
</div>
|
216 |
-
|
217 |
-
<!-- زر إعادة المراجعة -->
|
218 |
-
<button id="reviewAgainBtn" class="w-full bg-gray-600 hover:bg-gray-700 text-white font-bold py-3 px-6 rounded-xl transition-all transform hover:scale-105 focus:ring-gray-200 text-xl shadow-lg hover:shadow-xl mb-8">
|
219 |
-
<div class="flex items-center justify-center">
|
220 |
-
<i class="fas fa-redo ml-2"></i> إعادة المراجعة
|
221 |
-
</div>
|
222 |
-
</button>
|
223 |
-
|
224 |
-
<!-- قسم إعادة المراجعة: قائمة منسدلة لتحديد نوع المراجعة -->
|
225 |
-
<div id="reviewAgainSection" class="hidden bg-white rounded-2xl shadow-lg p-8 border border-gray-100 hover:shadow-xl transition-all animate-scale mb-8 card-hover">
|
226 |
-
<h2 class="text-2xl font-bold mb-6 text-gray-800 border-b pb-3 flex items-center">
|
227 |
-
<i class="fas fa-redo text-blue-600 ml-2"></i> إعادة المراجعة - اختيار النوع
|
228 |
-
</h2>
|
229 |
-
<div class="flex items-center space-x-4">
|
230 |
-
<select id="reviewType" class="px-4 py-2 border rounded-xl focus:outline-none">
|
231 |
-
<option value="all">جميع الاختلافات</option>
|
232 |
-
<option value="number">مراجعة رقمية فقط</option>
|
233 |
-
<option value="meaning">مراجعة المعنى فقط</option>
|
234 |
-
<option value="missing">مراجعة النص المفقود فقط</option>
|
235 |
-
</select>
|
236 |
-
<button id="confirmReviewBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-xl">تنفيذ إعادة المراجعة</button>
|
237 |
-
</div>
|
238 |
-
</div>
|
239 |
-
|
240 |
</main>
|
241 |
</div>
|
242 |
|
@@ -244,54 +234,76 @@
|
|
244 |
جافا سكريبت: الوظائف والمعالجة
|
245 |
================================= -->
|
246 |
<script>
|
247 |
-
// متغيرات لتخزين النصوص ونتيجة التحليل لإعادة المراجعة
|
248 |
-
let storedAnalysisOutput = "";
|
249 |
-
let storedSourceText = "";
|
250 |
-
let storedTargetText = "";
|
251 |
-
|
252 |
// إعدادات API
|
253 |
const API_URL = 'https://api.deepseek.com/chat/completions';
|
254 |
const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
|
255 |
|
256 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
257 |
لا تقم بإزالة أو تعديل العلامات التالية:
|
258 |
-
•
|
259 |
• النصوص المفقودة: تحافظ على علامات __ و __.
|
|
|
260 |
• اختلافات المعنى: تحافظ على علامات [MEANING] و [/MEANING].
|
|
|
261 |
|
262 |
اعتمد النص المصدر كأساس للمقارنة، وقم بتحديد:
|
263 |
-
1. اختلافات
|
264 |
2. النصوص المفقودة كما هي بين علامتي __.
|
265 |
-
3.
|
266 |
-
|
|
|
|
|
267 |
النص المصدر:
|
268 |
{source}
|
269 |
-
|
270 |
النص الهدف:
|
271 |
{target}`;
|
272 |
|
273 |
-
|
|
|
|
|
|
|
274 |
function countWords(text) {
|
275 |
return text.trim().split(/\s+/).filter(word => word !== "").length;
|
276 |
}
|
|
|
277 |
function escapeRegExp(string) {
|
278 |
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
279 |
}
|
|
|
280 |
function splitIntoLines(text) {
|
281 |
-
return text.split(
|
|
|
|
|
|
|
|
|
|
|
|
|
282 |
}
|
|
|
283 |
function getLineNumber(text, substring) {
|
284 |
const index = text.indexOf(substring);
|
285 |
if (index === -1) return "غير محدد";
|
286 |
return text.substring(0, index).split("\n").length;
|
287 |
}
|
288 |
|
289 |
-
|
|
|
|
|
290 |
function applyHighlights(originalText, analysisOutput) {
|
291 |
let highlightedText = originalText;
|
292 |
-
// اختلافات الأرقام
|
293 |
-
const numberRegex = /<([^<>]+)>/g;
|
294 |
let match;
|
|
|
|
|
295 |
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
296 |
const phrase = match[1].trim();
|
297 |
if (phrase) {
|
@@ -300,7 +312,7 @@
|
|
300 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
301 |
}
|
302 |
}
|
303 |
-
// النصوص المفقودة
|
304 |
const missingRegex = /__(.*?)__/g;
|
305 |
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
306 |
const phrase = match[1].trim();
|
@@ -310,7 +322,17 @@
|
|
310 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
311 |
}
|
312 |
}
|
313 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
314 |
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
315 |
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
316 |
const phrase = match[1].trim();
|
@@ -320,56 +342,28 @@
|
|
320 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
321 |
}
|
322 |
}
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
let match;
|
332 |
-
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
333 |
-
const phrase = match[1].trim();
|
334 |
-
if (phrase) {
|
335 |
-
const replacement = `<span class="highlight-meaning">${phrase}</span>`;
|
336 |
-
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
337 |
-
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
338 |
-
}
|
339 |
-
}
|
340 |
-
} else if (reviewType === 'number') {
|
341 |
-
const numberRegex = /<([^<>]+)>/g;
|
342 |
-
let match;
|
343 |
-
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
344 |
-
const phrase = match[1].trim();
|
345 |
-
if (phrase) {
|
346 |
-
const replacement = `<span class="highlight-number">${phrase}</span>`;
|
347 |
-
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
348 |
-
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
349 |
-
}
|
350 |
-
}
|
351 |
-
} else if (reviewType === 'missing') {
|
352 |
-
const missingRegex = /__(.*?)__/g;
|
353 |
-
let match;
|
354 |
-
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
355 |
-
const phrase = match[1].trim();
|
356 |
-
if (phrase) {
|
357 |
-
const replacement = `<span class="highlight-missing">__${phrase}__</span>`;
|
358 |
-
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
359 |
-
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
360 |
-
}
|
361 |
}
|
362 |
-
} else {
|
363 |
-
highlightedText = applyHighlights(originalText, analysisOutput);
|
364 |
}
|
365 |
return highlightedText;
|
366 |
}
|
367 |
|
368 |
-
|
|
|
|
|
|
|
369 |
function generateExplanation(sourceText, analysisOutput) {
|
370 |
let steps = [];
|
371 |
-
const missingRegex = /__(.*?)__/g;
|
372 |
let match;
|
|
|
|
|
373 |
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
374 |
const phrase = match[1].trim();
|
375 |
if (phrase) {
|
@@ -377,71 +371,52 @@
|
|
377 |
steps.push(`<li><strong>الخطوة 1:</strong> في السطر ${lineNum}، الجزء "<span class="highlight-missing">__${phrase}__</span>" من النص المصدر مفقود في النص الهدف. تأكد من إضافته لتحسين الدقة.</li>`);
|
378 |
}
|
379 |
}
|
|
|
380 |
const numberRegex = /<([^<>]+)>/g;
|
381 |
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
382 |
const phrase = match[1].trim();
|
383 |
if (phrase) {
|
384 |
const lineNum = getLineNumber(sourceText, phrase);
|
385 |
-
steps.push(`<li><strong>الخطوة 2:</strong> في السطر ${lineNum}، الرقم "<span class="highlight-number">${phrase}</span>" في المصدر لا يتطابق مع
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
386 |
}
|
387 |
}
|
|
|
388 |
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
389 |
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
390 |
const phrase = match[1].trim();
|
391 |
if (phrase) {
|
392 |
const lineNum = getLineNumber(sourceText, phrase);
|
393 |
-
steps.push(`<li><strong>الخطوة
|
394 |
}
|
395 |
}
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
function generateExplanationByType(sourceText, analysisOutput, reviewType) {
|
404 |
-
let steps = [];
|
405 |
-
if (reviewType === 'meaning') {
|
406 |
-
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
407 |
-
let match;
|
408 |
-
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
409 |
-
const phrase = match[1].trim();
|
410 |
-
if (phrase) {
|
411 |
-
const lineNum = getLineNumber(sourceText, phrase);
|
412 |
-
steps.push(`<li><strong>مراجعة المعنى:</strong> في السطر ${lineNum}، التعبير "<span class="highlight-meaning">${phrase}</span>" يختلف. تحقق من الدقة.</li>`);
|
413 |
-
}
|
414 |
-
}
|
415 |
-
} else if (reviewType === 'number') {
|
416 |
-
const numberRegex = /<([^<>]+)>/g;
|
417 |
-
let match;
|
418 |
-
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
419 |
-
const phrase = match[1].trim();
|
420 |
-
if (phrase) {
|
421 |
-
const lineNum = getLineNumber(sourceText, phrase);
|
422 |
-
steps.push(`<li><strong>مراجعة رقمية:</strong> في السطر ${lineNum}، الرقم "<span class="highlight-number">${phrase}</span>" لا يتطابق. يرجى المراجعة.</li>`);
|
423 |
-
}
|
424 |
-
}
|
425 |
-
} else if (reviewType === 'missing') {
|
426 |
-
const missingRegex = /__(.*?)__/g;
|
427 |
-
let match;
|
428 |
-
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
429 |
-
const phrase = match[1].trim();
|
430 |
-
if (phrase) {
|
431 |
-
const lineNum = getLineNumber(sourceText, phrase);
|
432 |
-
steps.push(`<li><strong>مراجعة النص المفقود:</strong> في السطر ${lineNum}، النص "<span class="highlight-missing">__${phrase}__</span>" مفقود أو مختلف. يرجى المراجعة.</li>`);
|
433 |
-
}
|
434 |
}
|
435 |
-
} else {
|
436 |
-
return generateExplanation(sourceText, analysisOutput);
|
437 |
}
|
|
|
438 |
if (steps.length === 0) {
|
439 |
-
return `<p
|
440 |
}
|
441 |
return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
|
442 |
}
|
443 |
|
444 |
-
|
|
|
|
|
445 |
async function processFile(file) {
|
446 |
let text = "";
|
447 |
if (file.type === 'application/pdf') {
|
@@ -464,7 +439,10 @@
|
|
464 |
return text;
|
465 |
}
|
466 |
|
467 |
-
|
|
|
|
|
|
|
468 |
document.getElementById('sourceFile')?.addEventListener('change', async (event) => {
|
469 |
const file = event.target.files[0];
|
470 |
if (!file) return;
|
@@ -479,6 +457,7 @@
|
|
479 |
document.getElementById('processStatus').classList.add('hidden');
|
480 |
}
|
481 |
});
|
|
|
482 |
document.getElementById('targetFile')?.addEventListener('change', async (event) => {
|
483 |
const file = event.target.files[0];
|
484 |
if (!file) return;
|
@@ -493,6 +472,7 @@
|
|
493 |
document.getElementById('processStatus').classList.add('hidden');
|
494 |
}
|
495 |
});
|
|
|
496 |
document.getElementById('sourceExtraFile')?.addEventListener('change', async (event) => {
|
497 |
const file = event.target.files[0];
|
498 |
if (!file) return;
|
@@ -508,7 +488,9 @@
|
|
508 |
}
|
509 |
});
|
510 |
|
511 |
-
|
|
|
|
|
512 |
function addError(message, type = 'error') {
|
513 |
const errorsList = document.getElementById('errorsList');
|
514 |
if (!errorsList) return;
|
@@ -521,10 +503,13 @@
|
|
521 |
errorsList.appendChild(errorDiv);
|
522 |
}
|
523 |
|
524 |
-
|
|
|
|
|
525 |
document.getElementById('submitBtn').addEventListener('click', async () => {
|
526 |
const sourceText = document.getElementById('sourceText').value;
|
527 |
const targetText = document.getElementById('targetText').value;
|
|
|
528 |
document.getElementById('errorsList').innerHTML = '';
|
529 |
document.getElementById('resultSection').classList.remove('hidden');
|
530 |
document.getElementById('explanationBox').classList.remove('hidden');
|
@@ -534,6 +519,7 @@
|
|
534 |
return;
|
535 |
}
|
536 |
|
|
|
537 |
const sourceWordCount = countWords(sourceText);
|
538 |
const targetWordCount = countWords(targetText);
|
539 |
if (sourceWordCount !== targetWordCount) {
|
@@ -541,6 +527,7 @@
|
|
541 |
}
|
542 |
|
543 |
try {
|
|
|
544 |
const progressDiv = document.createElement('div');
|
545 |
progressDiv.className = "bg-indigo-100 p-4 rounded-xl mb-4";
|
546 |
progressDiv.innerHTML = `<div class="flex items-center">
|
@@ -549,10 +536,12 @@
|
|
549 |
</div>`;
|
550 |
document.getElementById('errorsList').appendChild(progressDiv);
|
551 |
|
|
|
552 |
const prompt = ANALYSIS_PROMPT
|
553 |
.replace("{source}", sourceText)
|
554 |
.replace("{target}", targetText);
|
555 |
|
|
|
556 |
const payload = {
|
557 |
model: "deepseek-chat",
|
558 |
messages: [
|
@@ -578,11 +567,7 @@
|
|
578 |
const analysisOutput = data.choices[0].message.content.trim();
|
579 |
progressDiv.remove();
|
580 |
|
581 |
-
//
|
582 |
-
storedAnalysisOutput = analysisOutput;
|
583 |
-
storedSourceText = sourceText;
|
584 |
-
storedTargetText = targetText;
|
585 |
-
|
586 |
if (analysisOutput.includes('[MATCH]')) {
|
587 |
const checkDiv = document.createElement('div');
|
588 |
checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
|
@@ -592,23 +577,30 @@
|
|
592 |
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
|
593 |
document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا يوجد اختلاف يجب الإشارة إليه.</p>`;
|
594 |
} else {
|
|
|
595 |
const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
|
596 |
const targetHighlighted = applyHighlights(targetText, analysisOutput);
|
597 |
document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
|
598 |
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted);
|
|
|
599 |
const explanationHTML = generateExplanation(sourceText, analysisOutput);
|
600 |
document.getElementById('explanationText').innerHTML = explanationHTML;
|
601 |
|
|
|
602 |
const numDiffCount = (analysisOutput.match(/<([^<>]+)>/g) || []).length;
|
603 |
const missingDiffCount = (analysisOutput.match(/__(.*?)__/g) || []).length;
|
|
|
604 |
const meaningDiffCount = (analysisOutput.match(/\[MEANING\](.*?)\[\/MEANING\]/g) || []).length;
|
605 |
-
|
|
|
606 |
const summaryDiv = document.createElement('div');
|
607 |
summaryDiv.className = "p-4 rounded-xl bg-yellow-50 text-gray-800";
|
608 |
let summaryText = '<div class="font-bold mb-2">ملخص الاختلافات:</div><ul class="list-disc mr-6 space-y-1">';
|
609 |
-
if (numDiffCount > 0) summaryText += `<li>اختلاف في
|
610 |
if (missingDiffCount > 0) summaryText += `<li>النصوص المفقودة: ${missingDiffCount}</li>`;
|
|
|
611 |
if (meaningDiffCount > 0) summaryText += `<li>اختلاف في المعنى: ${meaningDiffCount}</li>`;
|
|
|
612 |
summaryText += '</ul>';
|
613 |
summaryDiv.innerHTML = summaryText;
|
614 |
document.getElementById('errorsList').appendChild(summaryDiv);
|
@@ -619,28 +611,6 @@
|
|
619 |
addError(`خطأ في التحليل: ${error.message}`);
|
620 |
}
|
621 |
});
|
622 |
-
|
623 |
-
// زر إعادة المراجعة: إخفاء النتائج القديمة وعرض قسم اختيار النوع
|
624 |
-
document.getElementById('reviewAgainBtn').addEventListener('click', () => {
|
625 |
-
document.getElementById('resultSection').classList.add('hidden');
|
626 |
-
document.getElementById('explanationBox').classList.add('hidden');
|
627 |
-
document.getElementById('errorsList').innerHTML = '';
|
628 |
-
document.getElementById('reviewAgainSection').classList.remove('hidden');
|
629 |
-
});
|
630 |
-
|
631 |
-
// تنفيذ إعادة المراجعة بناءً على النوع المحدد
|
632 |
-
document.getElementById('confirmReviewBtn').addEventListener('click', () => {
|
633 |
-
const reviewType = document.getElementById('reviewType').value;
|
634 |
-
const explanationHTML = generateExplanationByType(storedSourceText, storedAnalysisOutput, reviewType);
|
635 |
-
document.getElementById('explanationText').innerHTML = explanationHTML;
|
636 |
-
const sourceHighlighted = applyHighlightsByType(storedSourceText, storedAnalysisOutput, reviewType);
|
637 |
-
const targetHighlighted = applyHighlightsByType(storedTargetText, storedAnalysisOutput, reviewType);
|
638 |
-
document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
|
639 |
-
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted);
|
640 |
-
document.getElementById('reviewAgainSection').classList.add('hidden');
|
641 |
-
document.getElementById('resultSection').classList.remove('hidden');
|
642 |
-
document.getElementById('explanationBox').classList.remove('hidden');
|
643 |
-
});
|
644 |
</script>
|
645 |
</body>
|
646 |
-
</html>
|
|
|
36 |
================================= */
|
37 |
.text-comparison { line-height: 1.8; white-space: pre-wrap; }
|
38 |
.highlight-number {
|
39 |
+
background-color: #FDE68A; /* أصفر */
|
40 |
padding: 0 4px;
|
41 |
border-radius: 3px;
|
42 |
font-weight: bold;
|
43 |
}
|
44 |
.highlight-missing {
|
45 |
+
background-color: #FECACA; /* أحمر للنصوص الناقصة */
|
46 |
+
color: #B91C1C;
|
47 |
padding: 0 4px;
|
48 |
border-radius: 3px;
|
49 |
font-style: italic;
|
50 |
}
|
51 |
+
.highlight-extra {
|
52 |
+
background-color: #D1FAE5; /* أخضر للنصوص الزائدة */
|
53 |
+
color: #065F46;
|
54 |
+
padding: 0 4px;
|
55 |
+
border-radius: 3px;
|
56 |
+
font-weight: bold;
|
57 |
+
}
|
58 |
.highlight-meaning {
|
59 |
+
background-color: #E0E7FF;
|
60 |
+
padding: 0 4px;
|
61 |
+
border-radius: 3px;
|
62 |
+
font-weight: bold;
|
63 |
+
}
|
64 |
+
.highlight-doubt {
|
65 |
+
background-color: #DBEAFE; /* أزرق لعلامات الشك أو الأخطاء البسيطة */
|
66 |
+
color: #1E3A8A;
|
67 |
padding: 0 4px;
|
68 |
border-radius: 3px;
|
69 |
font-weight: bold;
|
|
|
192 |
<!-- زر التحليل -->
|
193 |
<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 mb-8 pulse-animation">
|
194 |
<div class="flex items-center justify-center">
|
195 |
+
<i class="fas fa-sync-alt ml-2"></i> تحليل ومراجعة النصوص
|
196 |
</div>
|
197 |
</button>
|
198 |
|
|
|
227 |
</h2>
|
228 |
<div id="explanationText" class="text-lg text-gray-700"></div>
|
229 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
</main>
|
231 |
</div>
|
232 |
|
|
|
234 |
جافا سكريبت: الوظائف والمعالجة
|
235 |
================================= -->
|
236 |
<script>
|
|
|
|
|
|
|
|
|
|
|
237 |
// إعدادات API
|
238 |
const API_URL = 'https://api.deepseek.com/chat/completions';
|
239 |
const API_KEY = 'sk-15606736ed9e4aea8b7cc11a195d2b01';
|
240 |
|
241 |
+
/*
|
242 |
+
البرومبت الجديد:
|
243 |
+
- تحليل النص كفقرات مع التأكد من انتهاء كل فقرة بنقطة.
|
244 |
+
- التحقق من النصوص الناقصة، النصوص الزائدة، اختلافات المعنى،
|
245 |
+
اختلافات الأرقام/التواريخ وعلامات الشك أو الأخطاء البسيطة.
|
246 |
+
- تُحاط النصوص الناقصة بعلامتي __، النصوص الزائدة بـ <<EXTRA>> و <<\/EXTRA>>,
|
247 |
+
اختلافات المعنى بـ [MEANING] و [/MEANING]،
|
248 |
+
والأخطاء أو علامات الشك بـ [DOUBT] و [/DOUBT].
|
249 |
+
*/
|
250 |
+
const ANALYSIS_PROMPT = `أنت خبير لغوي متخصص في مراجعة الترجمة التقنية. مهمتك مقارنة النص المصدر والنص الهدف بدقة عالية وتحليل النص كفقرات منفصلة مع التأكد من أن كل فقرة تنتهي بنقطة. النصوص مليئة بالأخطاء والنواقص.
|
251 |
لا تقم بإزالة أو تعديل العلامات التالية:
|
252 |
+
• الأرقام والتواريخ: تحافظ على علامات < و >.
|
253 |
• النصوص المفقودة: تحافظ على علامات __ و __.
|
254 |
+
• النصوص الزائدة: ضعها بين <<EXTRA>> و <<\/EXTRA>>.
|
255 |
• اختلافات المعنى: تحافظ على علامات [MEANING] و [/MEANING].
|
256 |
+
• علامات الشك أو الأخطاء البسيطة: ضعها بين [DOUBT] و [/DOUBT].
|
257 |
|
258 |
اعتمد النص المصدر كأساس للمقارنة، وقم بتحديد:
|
259 |
+
1. اختلافات الأرقام/التواريخ باستخدام <الرقم/التاريخ_في_المصدر> → <الرقم/التاريخ_في_الهدف>.
|
260 |
2. النصوص المفقودة كما هي بين علامتي __.
|
261 |
+
3. النصوص الزائدة كما هي بين <<EXTRA>> و <<\/EXTRA>>.
|
262 |
+
4. اختلافات المعنى باستخدام [MEANING] مع الحفاظ على التعليم.
|
263 |
+
5. علامات الشك أو الأخطاء البسيطة باستخدام [DOUBT] مع ضرورة مراجعة دقيقة لتفادي التظليل الوهمي.
|
264 |
+
|
265 |
النص المصدر:
|
266 |
{source}
|
267 |
+
|
268 |
النص الهدف:
|
269 |
{target}`;
|
270 |
|
271 |
+
/* =====================================
|
272 |
+
دوال مساعدة للتعامل مع النصوص
|
273 |
+
===================================== */
|
274 |
+
// حساب عدد الكلمات في النص
|
275 |
function countWords(text) {
|
276 |
return text.trim().split(/\s+/).filter(word => word !== "").length;
|
277 |
}
|
278 |
+
// هروب أحرف Regex الخاصة
|
279 |
function escapeRegExp(string) {
|
280 |
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
281 |
}
|
282 |
+
// تقسيم النص إلى فقرات مع التأكد من انتهاء كل فقرة بنقطة وترقيمها
|
283 |
function splitIntoLines(text) {
|
284 |
+
return text.split(/\n+/).map((line, i) => {
|
285 |
+
line = line.trim();
|
286 |
+
if(line && !line.endsWith('.')) {
|
287 |
+
line += '.';
|
288 |
+
}
|
289 |
+
return `<div class="line-item"><span class="line-number">${i+1}:</span> <span class="line-text">${line}</span></div>`;
|
290 |
+
}).join('');
|
291 |
}
|
292 |
+
// الحصول على رقم السطر لظهور عبارة معينة
|
293 |
function getLineNumber(text, substring) {
|
294 |
const index = text.indexOf(substring);
|
295 |
if (index === -1) return "غير محدد";
|
296 |
return text.substring(0, index).split("\n").length;
|
297 |
}
|
298 |
|
299 |
+
/* =====================================
|
300 |
+
دوال التمييز (Highlighting) للنتائج
|
301 |
+
===================================== */
|
302 |
function applyHighlights(originalText, analysisOutput) {
|
303 |
let highlightedText = originalText;
|
|
|
|
|
304 |
let match;
|
305 |
+
// تمييز اختلافات الأرقام/التواريخ
|
306 |
+
const numberRegex = /<([^<>]+)>/g;
|
307 |
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
308 |
const phrase = match[1].trim();
|
309 |
if (phrase) {
|
|
|
312 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
313 |
}
|
314 |
}
|
315 |
+
// تمييز النصوص المفقودة
|
316 |
const missingRegex = /__(.*?)__/g;
|
317 |
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
318 |
const phrase = match[1].trim();
|
|
|
322 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
323 |
}
|
324 |
}
|
325 |
+
// تمييز النصوص الزائدة
|
326 |
+
const extraRegex = /<<EXTRA>>(.*?)<<\/EXTRA>>/g;
|
327 |
+
while ((match = extraRegex.exec(analysisOutput)) !== null) {
|
328 |
+
const phrase = match[1].trim();
|
329 |
+
if (phrase) {
|
330 |
+
const replacement = `<span class="highlight-extra">${phrase}</span>`;
|
331 |
+
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
332 |
+
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
333 |
+
}
|
334 |
+
}
|
335 |
+
// تمييز اختلافات المعنى
|
336 |
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
337 |
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
338 |
const phrase = match[1].trim();
|
|
|
342 |
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
343 |
}
|
344 |
}
|
345 |
+
// تمييز علامات الشك أو الأخطاء البسيطة
|
346 |
+
const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
|
347 |
+
while ((match = doubtRegex.exec(analysisOutput)) !== null) {
|
348 |
+
const phrase = match[1].trim();
|
349 |
+
if (phrase) {
|
350 |
+
const replacement = `<span class="highlight-doubt">${phrase}</span>`;
|
351 |
+
const phraseRegex = new RegExp(escapeRegExp(phrase), 'g');
|
352 |
+
highlightedText = highlightedText.replace(phraseRegex, replacement);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
353 |
}
|
|
|
|
|
354 |
}
|
355 |
return highlightedText;
|
356 |
}
|
357 |
|
358 |
+
/* =====================================
|
359 |
+
دالة توليد الشرح التفصيلي Organized Explanation
|
360 |
+
يتم تقسيم الشرح إلى خطوات منظمة
|
361 |
+
===================================== */
|
362 |
function generateExplanation(sourceText, analysisOutput) {
|
363 |
let steps = [];
|
|
|
364 |
let match;
|
365 |
+
// الخطوة 1: النصوص المفقودة
|
366 |
+
const missingRegex = /__(.*?)__/g;
|
367 |
while ((match = missingRegex.exec(analysisOutput)) !== null) {
|
368 |
const phrase = match[1].trim();
|
369 |
if (phrase) {
|
|
|
371 |
steps.push(`<li><strong>الخطوة 1:</strong> في السطر ${lineNum}، الجزء "<span class="highlight-missing">__${phrase}__</span>" من النص المصدر مفقود في النص الهدف. تأكد من إضافته لتحسين الدقة.</li>`);
|
372 |
}
|
373 |
}
|
374 |
+
// الخطوة 2: اختلافات الأرقام/التواريخ
|
375 |
const numberRegex = /<([^<>]+)>/g;
|
376 |
while ((match = numberRegex.exec(analysisOutput)) !== null) {
|
377 |
const phrase = match[1].trim();
|
378 |
if (phrase) {
|
379 |
const lineNum = getLineNumber(sourceText, phrase);
|
380 |
+
steps.push(`<li><strong>الخطوة 2:</strong> في السطر ${lineNum}، الرقم أو التاريخ "<span class="highlight-number">${phrase}</span>" في المصدر لا يتطابق مع الهدف. يرجى المراجعة.</li>`);
|
381 |
+
}
|
382 |
+
}
|
383 |
+
// الخطوة 3: النصوص الزائدة
|
384 |
+
const extraRegex = /<<EXTRA>>(.*?)<<\/EXTRA>>/g;
|
385 |
+
while ((match = extraRegex.exec(analysisOutput)) !== null) {
|
386 |
+
const phrase = match[1].trim();
|
387 |
+
if (phrase) {
|
388 |
+
const lineNum = getLineNumber(sourceText, phrase);
|
389 |
+
steps.push(`<li><strong>تنبيه إضافي:</strong> في السطر ${lineNum}، النص "<span class="highlight-extra">${phrase}</span>" موجود كإضافة غير متوقعة في النص الهدف. يرجى المراجعة.</li>`);
|
390 |
}
|
391 |
}
|
392 |
+
// الخطوة 4: اختلافات المعنى
|
393 |
const meaningRegex = /\[MEANING\](.*?)\[\/MEANING\]/g;
|
394 |
while ((match = meaningRegex.exec(analysisOutput)) !== null) {
|
395 |
const phrase = match[1].trim();
|
396 |
if (phrase) {
|
397 |
const lineNum = getLineNumber(sourceText, phrase);
|
398 |
+
steps.push(`<li><strong>الخطوة 4:</strong> في السطر ${lineNum}، تم العثور على اختلاف في المعنى مع التعبير "<span class="highlight-meaning">${phrase}</span>". تحقق من الدقة.</li>`);
|
399 |
}
|
400 |
}
|
401 |
+
// الخطوة 5: علامات الشك أو الأخطاء البسيطة
|
402 |
+
const doubtRegex = /\[DOUBT\](.*?)\[\/DOUBT\]/g;
|
403 |
+
while ((match = doubtRegex.exec(analysisOutput)) !== null) {
|
404 |
+
const phrase = match[1].trim();
|
405 |
+
if (phrase) {
|
406 |
+
const lineNum = getLineNumber(sourceText, phrase);
|
407 |
+
steps.push(`<li><strong>تنبيه:</strong> في السطر ${lineNum}، تم العثور على علامة شك أو خطأ بسيط "<span class="highlight-doubt">${phrase}</span>". يُنصح بمراجعة علامات الترقيم والأخطاء البسيطة.</li>`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
408 |
}
|
|
|
|
|
409 |
}
|
410 |
+
// إذا لم يكن هناك اختلافات
|
411 |
if (steps.length === 0) {
|
412 |
+
return `<p>النصوص متطابقة تماماً ولا توجد فروقات تحتاج للتنبيه.</p>`;
|
413 |
}
|
414 |
return `<ol class="list-decimal ml-6 space-y-2">${steps.join('')}</ol>`;
|
415 |
}
|
416 |
|
417 |
+
/* =====================================
|
418 |
+
دالة معالجة الملفات (PDF و DOCX)
|
419 |
+
===================================== */
|
420 |
async function processFile(file) {
|
421 |
let text = "";
|
422 |
if (file.type === 'application/pdf') {
|
|
|
439 |
return text;
|
440 |
}
|
441 |
|
442 |
+
/* =====================================
|
443 |
+
دوال رفع الملفات وإدخال النصوص
|
444 |
+
===================================== */
|
445 |
+
// رفع ملف السورس
|
446 |
document.getElementById('sourceFile')?.addEventListener('change', async (event) => {
|
447 |
const file = event.target.files[0];
|
448 |
if (!file) return;
|
|
|
457 |
document.getElementById('processStatus').classList.add('hidden');
|
458 |
}
|
459 |
});
|
460 |
+
// رفع ملف التارجت
|
461 |
document.getElementById('targetFile')?.addEventListener('change', async (event) => {
|
462 |
const file = event.target.files[0];
|
463 |
if (!file) return;
|
|
|
472 |
document.getElementById('processStatus').classList.add('hidden');
|
473 |
}
|
474 |
});
|
475 |
+
// رفع ملف المصادر الإضافية
|
476 |
document.getElementById('sourceExtraFile')?.addEventListener('change', async (event) => {
|
477 |
const file = event.target.files[0];
|
478 |
if (!file) return;
|
|
|
488 |
}
|
489 |
});
|
490 |
|
491 |
+
/* =====================================
|
492 |
+
دالة عرض الأخطاء والرسائل
|
493 |
+
===================================== */
|
494 |
function addError(message, type = 'error') {
|
495 |
const errorsList = document.getElementById('errorsList');
|
496 |
if (!errorsList) return;
|
|
|
503 |
errorsList.appendChild(errorDiv);
|
504 |
}
|
505 |
|
506 |
+
/* =====================================
|
507 |
+
معالجة عملية التحليل عند الضغط على الزر
|
508 |
+
===================================== */
|
509 |
document.getElementById('submitBtn').addEventListener('click', async () => {
|
510 |
const sourceText = document.getElementById('sourceText').value;
|
511 |
const targetText = document.getElementById('targetText').value;
|
512 |
+
// مسح الرسائل السابقة وإظهار النتائج
|
513 |
document.getElementById('errorsList').innerHTML = '';
|
514 |
document.getElementById('resultSection').classList.remove('hidden');
|
515 |
document.getElementById('explanationBox').classList.remove('hidden');
|
|
|
519 |
return;
|
520 |
}
|
521 |
|
522 |
+
// مقارنة عدد الكلمات وتنبيه إن وجد اختلاف
|
523 |
const sourceWordCount = countWords(sourceText);
|
524 |
const targetWordCount = countWords(targetText);
|
525 |
if (sourceWordCount !== targetWordCount) {
|
|
|
527 |
}
|
528 |
|
529 |
try {
|
530 |
+
// عرض مؤشر التقدم
|
531 |
const progressDiv = document.createElement('div');
|
532 |
progressDiv.className = "bg-indigo-100 p-4 rounded-xl mb-4";
|
533 |
progressDiv.innerHTML = `<div class="flex items-center">
|
|
|
536 |
</div>`;
|
537 |
document.getElementById('errorsList').appendChild(progressDiv);
|
538 |
|
539 |
+
// بناء الـ prompt باستخدام النصين المُدخلين
|
540 |
const prompt = ANALYSIS_PROMPT
|
541 |
.replace("{source}", sourceText)
|
542 |
.replace("{target}", targetText);
|
543 |
|
544 |
+
// استدعاء DeepSeek API
|
545 |
const payload = {
|
546 |
model: "deepseek-chat",
|
547 |
messages: [
|
|
|
567 |
const analysisOutput = data.choices[0].message.content.trim();
|
568 |
progressDiv.remove();
|
569 |
|
570 |
+
// إذا كانت النصوص متطابقة تمامًا
|
|
|
|
|
|
|
|
|
571 |
if (analysisOutput.includes('[MATCH]')) {
|
572 |
const checkDiv = document.createElement('div');
|
573 |
checkDiv.className = "p-4 rounded-xl bg-green-50 text-green-700 flex items-center";
|
|
|
577 |
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetText);
|
578 |
document.getElementById('explanationText').innerHTML = `<p>النصوص متطابقة ولا يوجد اختلاف يجب الإشارة إليه.</p>`;
|
579 |
} else {
|
580 |
+
// تطبيق التحديد على النصوص
|
581 |
const sourceHighlighted = applyHighlights(sourceText, analysisOutput);
|
582 |
const targetHighlighted = applyHighlights(targetText, analysisOutput);
|
583 |
document.getElementById('sourceTextReview').innerHTML = splitIntoLines(sourceHighlighted);
|
584 |
document.getElementById('targetTextReview').innerHTML = splitIntoLines(targetHighlighted);
|
585 |
+
// توليد شرح منظم على شكل خطوات
|
586 |
const explanationHTML = generateExplanation(sourceText, analysisOutput);
|
587 |
document.getElementById('explanationText').innerHTML = explanationHTML;
|
588 |
|
589 |
+
// عرض ملخص الاختلافات في الأسفل (اختياري)
|
590 |
const numDiffCount = (analysisOutput.match(/<([^<>]+)>/g) || []).length;
|
591 |
const missingDiffCount = (analysisOutput.match(/__(.*?)__/g) || []).length;
|
592 |
+
const extraDiffCount = (analysisOutput.match(/<<EXTRA>>(.*?)<<\/EXTRA>>/g) || []).length;
|
593 |
const meaningDiffCount = (analysisOutput.match(/\[MEANING\](.*?)\[\/MEANING\]/g) || []).length;
|
594 |
+
const doubtDiffCount = (analysisOutput.match(/\[DOUBT\](.*?)\[\/DOUBT\]/g) || []).length;
|
595 |
+
if (numDiffCount > 0 || missingDiffCount > 0 || extraDiffCount > 0 || meaningDiffCount > 0 || doubtDiffCount > 0) {
|
596 |
const summaryDiv = document.createElement('div');
|
597 |
summaryDiv.className = "p-4 rounded-xl bg-yellow-50 text-gray-800";
|
598 |
let summaryText = '<div class="font-bold mb-2">ملخص الاختلافات:</div><ul class="list-disc mr-6 space-y-1">';
|
599 |
+
if (numDiffCount > 0) summaryText += `<li>اختلاف في الأرقام/التواريخ: ${numDiffCount}</li>`;
|
600 |
if (missingDiffCount > 0) summaryText += `<li>النصوص المفقودة: ${missingDiffCount}</li>`;
|
601 |
+
if (extraDiffCount > 0) summaryText += `<li>النصوص الزائدة: ${extraDiffCount}</li>`;
|
602 |
if (meaningDiffCount > 0) summaryText += `<li>اختلاف في المعنى: ${meaningDiffCount}</li>`;
|
603 |
+
if (doubtDiffCount > 0) summaryText += `<li>علامات الشك أو الأخطاء البسيطة: ${doubtDiffCount}</li>`;
|
604 |
summaryText += '</ul>';
|
605 |
summaryDiv.innerHTML = summaryText;
|
606 |
document.getElementById('errorsList').appendChild(summaryDiv);
|
|
|
611 |
addError(`خطأ في التحليل: ${error.message}`);
|
612 |
}
|
613 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
614 |
</script>
|
615 |
</body>
|
616 |
+
</html>
|