|
"""
|
|
وحدة الترجمة - نظام تحليل المناقصات
|
|
"""
|
|
|
|
import streamlit as st
|
|
import pandas as pd
|
|
import numpy as np
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
import re
|
|
import datetime
|
|
|
|
|
|
sys.path.append(str(Path(__file__).parent.parent))
|
|
|
|
|
|
from styling.enhanced_ui import UIEnhancer
|
|
|
|
class TranslationApp:
|
|
"""تطبيق الترجمة"""
|
|
|
|
def __init__(self):
|
|
"""تهيئة تطبيق الترجمة"""
|
|
self.ui = UIEnhancer(page_title="الترجمة - نظام تحليل المناقصات", page_icon="🌐")
|
|
self.ui.apply_theme_colors()
|
|
|
|
|
|
self.supported_languages = {
|
|
"ar": "العربية",
|
|
"en": "الإنجليزية",
|
|
"fr": "الفرنسية",
|
|
"de": "الألمانية",
|
|
"es": "الإسبانية",
|
|
"it": "الإيطالية",
|
|
"zh": "الصينية",
|
|
"ja": "اليابانية",
|
|
"ru": "الروسية",
|
|
"tr": "التركية"
|
|
}
|
|
|
|
|
|
self.technical_terms = [
|
|
{"ar": "كراسة الشروط", "en": "Terms and Conditions Document", "category": "مستندات"},
|
|
{"ar": "جدول الكميات", "en": "Bill of Quantities (BOQ)", "category": "مستندات"},
|
|
{"ar": "المواصفات الفنية", "en": "Technical Specifications", "category": "مستندات"},
|
|
{"ar": "ضمان ابتدائي", "en": "Bid Bond", "category": "ضمانات"},
|
|
{"ar": "ضمان حسن التنفيذ", "en": "Performance Bond", "category": "ضمانات"},
|
|
{"ar": "ضمان دفعة مقدمة", "en": "Advance Payment Guarantee", "category": "ضمانات"},
|
|
{"ar": "ضمان صيانة", "en": "Maintenance Bond", "category": "ضمانات"},
|
|
{"ar": "مناقصة عامة", "en": "Public Tender", "category": "أنواع المناقصات"},
|
|
{"ar": "مناقصة محدودة", "en": "Limited Tender", "category": "أنواع المناقصات"},
|
|
{"ar": "منافسة", "en": "Competition", "category": "أنواع المناقصات"},
|
|
{"ar": "أمر شراء", "en": "Purchase Order", "category": "عقود"},
|
|
{"ar": "عقد إطاري", "en": "Framework Agreement", "category": "عقود"},
|
|
{"ar": "عقد زمني", "en": "Time-based Contract", "category": "عقود"},
|
|
{"ar": "عقد تسليم مفتاح", "en": "Turnkey Contract", "category": "عقود"},
|
|
{"ar": "مقاول من الباطن", "en": "Subcontractor", "category": "أطراف"},
|
|
{"ar": "استشاري", "en": "Consultant", "category": "أطراف"},
|
|
{"ar": "مالك المشروع", "en": "Project Owner", "category": "أطراف"},
|
|
{"ar": "مدير المشروع", "en": "Project Manager", "category": "أطراف"},
|
|
{"ar": "مهندس الموقع", "en": "Site Engineer", "category": "أطراف"},
|
|
{"ar": "مراقب الجودة", "en": "Quality Control", "category": "أطراف"},
|
|
{"ar": "أعمال مدنية", "en": "Civil Works", "category": "أعمال"},
|
|
{"ar": "أعمال كهربائية", "en": "Electrical Works", "category": "أعمال"},
|
|
{"ar": "أعمال ميكانيكية", "en": "Mechanical Works", "category": "أعمال"},
|
|
{"ar": "أعمال معمارية", "en": "Architectural Works", "category": "أعمال"},
|
|
{"ar": "أعمال تشطيبات", "en": "Finishing Works", "category": "أعمال"},
|
|
{"ar": "غرامة تأخير", "en": "Delay Penalty", "category": "شروط"},
|
|
{"ar": "مدة التنفيذ", "en": "Execution Period", "category": "شروط"},
|
|
{"ar": "فترة الضمان", "en": "Warranty Period", "category": "شروط"},
|
|
{"ar": "شروط الدفع", "en": "Payment Terms", "category": "شروط"},
|
|
{"ar": "تسوية النزاعات", "en": "Dispute Resolution", "category": "شروط"}
|
|
]
|
|
|
|
|
|
self.translated_documents = [
|
|
{
|
|
"id": "TD001",
|
|
"name": "كراسة الشروط - مناقصة إنشاء مبنى إداري",
|
|
"source_language": "ar",
|
|
"target_language": "en",
|
|
"original_file": "specs_v2.0_ar.pdf",
|
|
"translated_file": "specs_v2.0_en.pdf",
|
|
"translation_date": "2025-03-15",
|
|
"translated_by": "أحمد محمد",
|
|
"status": "مكتمل",
|
|
"pages": 52,
|
|
"related_entity": "T-2025-001"
|
|
},
|
|
{
|
|
"id": "TD002",
|
|
"name": "جدول الكميات - مناقصة إنشاء مبنى إداري",
|
|
"source_language": "ar",
|
|
"target_language": "en",
|
|
"original_file": "boq_v1.1_ar.xlsx",
|
|
"translated_file": "boq_v1.1_en.xlsx",
|
|
"translation_date": "2025-02-25",
|
|
"translated_by": "سارة عبدالله",
|
|
"status": "مكتمل",
|
|
"pages": 22,
|
|
"related_entity": "T-2025-001"
|
|
},
|
|
{
|
|
"id": "TD003",
|
|
"name": "المخططات - مناقصة إنشاء مبنى إداري",
|
|
"source_language": "ar",
|
|
"target_language": "en",
|
|
"original_file": "drawings_v2.0_ar.pdf",
|
|
"translated_file": "drawings_v2.0_en.pdf",
|
|
"translation_date": "2025-03-20",
|
|
"translated_by": "محمد علي",
|
|
"status": "مكتمل",
|
|
"pages": 35,
|
|
"related_entity": "T-2025-001"
|
|
},
|
|
{
|
|
"id": "TD004",
|
|
"name": "كراسة الشروط - مناقصة صيانة طرق",
|
|
"source_language": "ar",
|
|
"target_language": "en",
|
|
"original_file": "specs_v1.1_ar.pdf",
|
|
"translated_file": "specs_v1.1_en.pdf",
|
|
"translation_date": "2025-03-25",
|
|
"translated_by": "فاطمة أحمد",
|
|
"status": "مكتمل",
|
|
"pages": 34,
|
|
"related_entity": "T-2025-002"
|
|
},
|
|
{
|
|
"id": "TD005",
|
|
"name": "جدول الكميات - مناقصة صيانة طرق",
|
|
"source_language": "ar",
|
|
"target_language": "en",
|
|
"original_file": "boq_v1.0_ar.xlsx",
|
|
"translated_file": "boq_v1.0_en.xlsx",
|
|
"translation_date": "2025-03-10",
|
|
"translated_by": "خالد عمر",
|
|
"status": "مكتمل",
|
|
"pages": 15,
|
|
"related_entity": "T-2025-002"
|
|
},
|
|
{
|
|
"id": "TD006",
|
|
"name": "كراسة الشروط - مناقصة توريد معدات",
|
|
"source_language": "en",
|
|
"target_language": "ar",
|
|
"original_file": "specs_v1.0_en.pdf",
|
|
"translated_file": "specs_v1.0_ar.pdf",
|
|
"translation_date": "2025-02-15",
|
|
"translated_by": "أحمد محمد",
|
|
"status": "مكتمل",
|
|
"pages": 28,
|
|
"related_entity": "T-2025-003"
|
|
},
|
|
{
|
|
"id": "TD007",
|
|
"name": "عقد توريد - مناقصة توريد معدات",
|
|
"source_language": "en",
|
|
"target_language": "ar",
|
|
"original_file": "contract_v1.0_en.pdf",
|
|
"translated_file": "contract_v1.0_ar.pdf",
|
|
"translation_date": "2025-03-05",
|
|
"translated_by": "سارة عبدالله",
|
|
"status": "مكتمل",
|
|
"pages": 20,
|
|
"related_entity": "T-2025-003"
|
|
},
|
|
{
|
|
"id": "TD008",
|
|
"name": "كراسة الشروط - مناقصة تجهيز مختبرات",
|
|
"source_language": "ar",
|
|
"target_language": "en",
|
|
"original_file": "specs_v1.0_ar.pdf",
|
|
"translated_file": "specs_v1.0_en.pdf",
|
|
"translation_date": "2025-03-28",
|
|
"translated_by": "محمد علي",
|
|
"status": "قيد التنفيذ",
|
|
"pages": 30,
|
|
"related_entity": "T-2025-004"
|
|
}
|
|
]
|
|
|
|
|
|
self.sample_translations = {
|
|
"text1": {
|
|
"ar": """
|
|
# كراسة الشروط والمواصفات
|
|
## مناقصة إنشاء مبنى إداري
|
|
|
|
### 1. مقدمة
|
|
تدعو شركة شبه الجزيرة للمقاولات الشركات المتخصصة للتقدم بعروضها لتنفيذ مشروع إنشاء مبنى إداري في مدينة الرياض.
|
|
|
|
### 2. نطاق العمل
|
|
يشمل نطاق العمل تصميم وتنفيذ مبنى إداري مكون من 6 طوابق بمساحة إجمالية 6000 متر مربع، ويشمل ذلك:
|
|
- أعمال الهيكل الإنشائي
|
|
- أعمال التشطيبات الداخلية والخارجية
|
|
- أعمال الكهرباء والميكانيكا
|
|
- أعمال تنسيق الموقع
|
|
- أعمال أنظمة الأمن والسلامة
|
|
- أعمال أنظمة المباني الذكية
|
|
""",
|
|
|
|
"en": """
|
|
# Terms and Conditions Document
|
|
## Administrative Building Construction Tender
|
|
|
|
### 1. Introduction
|
|
Peninsula Contracting Company invites specialized companies to submit their offers for the implementation of an administrative building construction project in Riyadh.
|
|
|
|
### 2. Scope of Work
|
|
The scope of work includes the design and implementation of a 6-floor administrative building with a total area of 6000 square meters, including:
|
|
- Structural works
|
|
- Interior and exterior finishing works
|
|
- Electrical and mechanical works
|
|
- Site coordination works
|
|
- Security and safety systems works
|
|
- Smart building systems works
|
|
"""
|
|
},
|
|
|
|
"text2": {
|
|
"ar": """
|
|
### 3. المواصفات الفنية
|
|
#### 3.1 أعمال الخرسانة
|
|
- يجب أن تكون الخرسانة المسلحة بقوة لا تقل عن 40 نيوتن/مم²
|
|
- يجب استخدام حديد تسليح مطابق للمواصفات السعودية
|
|
- يجب استخدام إضافات للخرسانة لزيادة مقاومتها للعوامل الجوية
|
|
|
|
#### 3.2 أعمال التشطيبات
|
|
- يجب استخدام مواد عالية الجودة للتشطيبات الداخلية
|
|
- يجب أن تكون الواجهات الخارجية مقاومة للعوامل الجوية
|
|
- يجب استخدام زجاج عاكس للحرارة للواجهات
|
|
- يجب استخدام مواد صديقة للبيئة
|
|
""",
|
|
|
|
"en": """
|
|
### 3. Technical Specifications
|
|
#### 3.1 Concrete Works
|
|
- Reinforced concrete must have a strength of not less than 40 Newton/mm²
|
|
- Reinforcement steel must comply with Saudi specifications
|
|
- Concrete additives must be used to increase its resistance to weather conditions
|
|
|
|
#### 3.2 Finishing Works
|
|
- High-quality materials must be used for interior finishes
|
|
- Exterior facades must be weather-resistant
|
|
- Heat-reflective glass must be used for facades
|
|
- Environmentally friendly materials must be used
|
|
"""
|
|
}
|
|
}
|
|
|
|
def run(self):
|
|
"""تشغيل تطبيق الترجمة"""
|
|
|
|
menu_items = [
|
|
{"name": "لوحة المعلومات", "icon": "house"},
|
|
{"name": "المناقصات والعقود", "icon": "file-text"},
|
|
{"name": "تحليل المستندات", "icon": "file-earmark-text"},
|
|
{"name": "نظام التسعير", "icon": "calculator"},
|
|
{"name": "حاسبة تكاليف البناء", "icon": "building"},
|
|
{"name": "الموارد والتكاليف", "icon": "people"},
|
|
{"name": "تحليل المخاطر", "icon": "exclamation-triangle"},
|
|
{"name": "إدارة المشاريع", "icon": "kanban"},
|
|
{"name": "الخرائط والمواقع", "icon": "geo-alt"},
|
|
{"name": "الجدول الزمني", "icon": "calendar3"},
|
|
{"name": "الإشعارات", "icon": "bell"},
|
|
{"name": "مقارنة المستندات", "icon": "files"},
|
|
{"name": "الترجمة", "icon": "translate"},
|
|
{"name": "المساعد الذكي", "icon": "robot"},
|
|
{"name": "التقارير", "icon": "bar-chart"},
|
|
{"name": "الإعدادات", "icon": "gear"}
|
|
]
|
|
|
|
|
|
selected = self.ui.create_sidebar(menu_items)
|
|
|
|
|
|
self.ui.create_header("الترجمة", "أدوات ترجمة المستندات والنصوص")
|
|
|
|
|
|
tabs = st.tabs(["ترجمة النصوص", "ترجمة المستندات", "قاموس المصطلحات", "المستندات المترجمة"])
|
|
|
|
|
|
with tabs[0]:
|
|
self.translate_text()
|
|
|
|
|
|
with tabs[1]:
|
|
self.translate_documents()
|
|
|
|
|
|
with tabs[2]:
|
|
self.technical_terms_dictionary()
|
|
|
|
|
|
with tabs[3]:
|
|
self.show_translated_documents()
|
|
|
|
def translate_text(self):
|
|
"""ترجمة النصوص"""
|
|
st.markdown("### ترجمة النصوص")
|
|
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
source_language = st.selectbox(
|
|
"لغة المصدر",
|
|
options=list(self.supported_languages.keys()),
|
|
format_func=lambda x: self.supported_languages[x],
|
|
index=0
|
|
)
|
|
|
|
with col2:
|
|
|
|
target_languages = {k: v for k, v in self.supported_languages.items() if k != source_language}
|
|
target_language = st.selectbox(
|
|
"لغة الهدف",
|
|
options=list(target_languages.keys()),
|
|
format_func=lambda x: self.supported_languages[x],
|
|
index=0
|
|
)
|
|
|
|
|
|
st.markdown("#### خيارات الترجمة")
|
|
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
translation_engine = st.radio(
|
|
"محرك الترجمة",
|
|
options=["OpenAI", "Google Translate", "Microsoft Translator", "محلي"]
|
|
)
|
|
|
|
with col2:
|
|
use_technical_terms = st.checkbox("استخدام قاموس المصطلحات الفنية", value=True)
|
|
|
|
with col3:
|
|
preserve_formatting = st.checkbox("الحفاظ على التنسيق", value=True)
|
|
|
|
|
|
st.markdown("#### النص المراد ترجمته")
|
|
|
|
|
|
examples = st.expander("أمثلة نصية")
|
|
with examples:
|
|
if st.button("مثال 1: مقدمة كراسة الشروط"):
|
|
source_text = self.sample_translations["text1"][source_language] if source_language in self.sample_translations["text1"] else self.sample_translations["text1"]["ar"]
|
|
elif st.button("مثال 2: المواصفات الفنية"):
|
|
source_text = self.sample_translations["text2"][source_language] if source_language in self.sample_translations["text2"] else self.sample_translations["text2"]["ar"]
|
|
else:
|
|
source_text = ""
|
|
|
|
if "source_text" not in locals():
|
|
source_text = ""
|
|
|
|
source_text = st.text_area(
|
|
"أدخل النص المراد ترجمته",
|
|
value=source_text,
|
|
height=200
|
|
)
|
|
|
|
|
|
if st.button("ترجمة النص", use_container_width=True):
|
|
if not source_text:
|
|
st.error("يرجى إدخال النص المراد ترجمته")
|
|
else:
|
|
|
|
|
|
|
|
with st.spinner("جاري الترجمة..."):
|
|
|
|
import time
|
|
time.sleep(1)
|
|
|
|
|
|
if source_language == "ar" and target_language == "en" and source_text.strip() in [self.sample_translations["text1"]["ar"].strip(), self.sample_translations["text2"]["ar"].strip()]:
|
|
if source_text.strip() == self.sample_translations["text1"]["ar"].strip():
|
|
translated_text = self.sample_translations["text1"]["en"]
|
|
else:
|
|
translated_text = self.sample_translations["text2"]["en"]
|
|
elif source_language == "en" and target_language == "ar" and source_text.strip() in [self.sample_translations["text1"]["en"].strip(), self.sample_translations["text2"]["en"].strip()]:
|
|
if source_text.strip() == self.sample_translations["text1"]["en"].strip():
|
|
translated_text = self.sample_translations["text1"]["ar"]
|
|
else:
|
|
translated_text = self.sample_translations["text2"]["ar"]
|
|
else:
|
|
|
|
translated_text = f"[هذا نص مترجم نموذجي من {self.supported_languages[source_language]} إلى {self.supported_languages[target_language]}]\n\n{source_text}"
|
|
|
|
|
|
st.markdown("#### النص المترجم")
|
|
st.text_area(
|
|
"النص المترجم",
|
|
value=translated_text,
|
|
height=200
|
|
)
|
|
|
|
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
if st.button("نسخ النص المترجم", use_container_width=True):
|
|
st.success("تم نسخ النص المترجم إلى الحافظة")
|
|
|
|
with col2:
|
|
if st.button("حفظ الترجمة", use_container_width=True):
|
|
st.success("تم حفظ الترجمة بنجاح")
|
|
|
|
with col3:
|
|
if st.button("تصدير كملف", use_container_width=True):
|
|
st.success("تم تصدير الترجمة كملف بنجاح")
|
|
|
|
|
|
st.markdown("#### إحصائيات الترجمة")
|
|
|
|
col1, col2, col3, col4 = st.columns(4)
|
|
|
|
with col1:
|
|
self.ui.create_metric_card(
|
|
"عدد الكلمات",
|
|
str(len(source_text.split())),
|
|
None,
|
|
self.ui.COLORS['primary']
|
|
)
|
|
|
|
with col2:
|
|
self.ui.create_metric_card(
|
|
"عدد الأحرف",
|
|
str(len(source_text)),
|
|
None,
|
|
self.ui.COLORS['secondary']
|
|
)
|
|
|
|
with col3:
|
|
self.ui.create_metric_card(
|
|
"وقت الترجمة",
|
|
"1.2 ثانية",
|
|
None,
|
|
self.ui.COLORS['success']
|
|
)
|
|
|
|
with col4:
|
|
self.ui.create_metric_card(
|
|
"المصطلحات الفنية",
|
|
"5",
|
|
None,
|
|
self.ui.COLORS['accent']
|
|
)
|
|
|
|
def translate_documents(self):
|
|
"""ترجمة المستندات"""
|
|
st.markdown("### ترجمة المستندات")
|
|
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
source_language = st.selectbox(
|
|
"لغة المصدر",
|
|
options=list(self.supported_languages.keys()),
|
|
format_func=lambda x: self.supported_languages[x],
|
|
index=0,
|
|
key="doc_source_lang"
|
|
)
|
|
|
|
with col2:
|
|
|
|
target_languages = {k: v for k, v in self.supported_languages.items() if k != source_language}
|
|
target_language = st.selectbox(
|
|
"لغة الهدف",
|
|
options=list(target_languages.keys()),
|
|
format_func=lambda x: self.supported_languages[x],
|
|
index=0,
|
|
key="doc_target_lang"
|
|
)
|
|
|
|
|
|
st.markdown("#### تحميل المستند")
|
|
|
|
uploaded_file = st.file_uploader("اختر المستند المراد ترجمته", type=["pdf", "docx", "xlsx", "txt"])
|
|
|
|
if uploaded_file is not None:
|
|
st.success(f"تم تحميل الملف: {uploaded_file.name}")
|
|
|
|
|
|
file_details = {
|
|
"اسم الملف": uploaded_file.name,
|
|
"نوع الملف": uploaded_file.type,
|
|
"حجم الملف": f"{uploaded_file.size / 1024:.1f} كيلوبايت"
|
|
}
|
|
|
|
st.json(file_details)
|
|
|
|
|
|
st.markdown("#### خيارات الترجمة")
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
translation_engine = st.radio(
|
|
"محرك الترجمة",
|
|
options=["OpenAI", "Google Translate", "Microsoft Translator", "محلي"],
|
|
key="doc_engine"
|
|
)
|
|
|
|
use_technical_terms = st.checkbox("استخدام قاموس المصطلحات الفنية", value=True, key="doc_terms")
|
|
|
|
with col2:
|
|
preserve_formatting = st.checkbox("الحفاظ على التنسيق", value=True, key="doc_format")
|
|
|
|
translate_images = st.checkbox("ترجمة النصوص في الصور", value=False)
|
|
|
|
maintain_layout = st.checkbox("الحفاظ على تخطيط المستند", value=True)
|
|
|
|
|
|
st.markdown("#### معلومات إضافية")
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
document_name = st.text_input("اسم المستند")
|
|
|
|
with col2:
|
|
related_entity = st.text_input("الكيان المرتبط (مثل: رقم المناقصة أو المشروع)")
|
|
|
|
|
|
if st.button("بدء ترجمة المستند", use_container_width=True):
|
|
if uploaded_file is None:
|
|
st.error("يرجى تحميل المستند المراد ترجمته")
|
|
else:
|
|
|
|
|
|
|
|
progress_bar = st.progress(0)
|
|
status_text = st.empty()
|
|
|
|
|
|
import time
|
|
for i in range(101):
|
|
progress_bar.progress(i)
|
|
|
|
if i < 10:
|
|
status_text.text("جاري تحليل المستند...")
|
|
elif i < 30:
|
|
status_text.text("جاري استخراج النصوص...")
|
|
elif i < 70:
|
|
status_text.text("جاري ترجمة المحتوى...")
|
|
elif i < 90:
|
|
status_text.text("جاري إعادة بناء المستند...")
|
|
else:
|
|
status_text.text("جاري إنهاء الترجمة...")
|
|
|
|
time.sleep(0.05)
|
|
|
|
|
|
st.success("تمت ترجمة المستند بنجاح!")
|
|
|
|
|
|
file_name_parts = uploaded_file.name.split('.')
|
|
translated_file_name = f"{'.'.join(file_name_parts[:-1])}_{target_language}.{file_name_parts[-1]}"
|
|
|
|
|
|
st.markdown("#### معلومات الملف المترجم")
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
st.markdown(f"**اسم الملف:** {translated_file_name}")
|
|
st.markdown(f"**لغة المصدر:** {self.supported_languages[source_language]}")
|
|
st.markdown(f"**لغة الهدف:** {self.supported_languages[target_language]}")
|
|
|
|
with col2:
|
|
st.markdown(f"**محرك الترجمة:** {translation_engine}")
|
|
st.markdown(f"**تاريخ الترجمة:** {datetime.datetime.now().strftime('%Y-%m-%d')}")
|
|
st.markdown(f"**حالة الترجمة:** مكتمل")
|
|
|
|
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
if st.button("تنزيل الملف المترجم", use_container_width=True):
|
|
st.success("تم بدء تنزيل الملف المترجم")
|
|
|
|
with col2:
|
|
if st.button("حفظ في المستندات المترجمة", use_container_width=True):
|
|
st.success("تم حفظ الملف في المستندات المترجمة")
|
|
|
|
with col3:
|
|
if st.button("مشاركة الملف", use_container_width=True):
|
|
st.success("تم نسخ رابط مشاركة الملف")
|
|
|
|
|
|
st.markdown("#### إحصائيات الترجمة")
|
|
|
|
col1, col2, col3, col4 = st.columns(4)
|
|
|
|
with col1:
|
|
self.ui.create_metric_card(
|
|
"عدد الصفحات",
|
|
"12",
|
|
None,
|
|
self.ui.COLORS['primary']
|
|
)
|
|
|
|
with col2:
|
|
self.ui.create_metric_card(
|
|
"عدد الكلمات",
|
|
"2,450",
|
|
None,
|
|
self.ui.COLORS['secondary']
|
|
)
|
|
|
|
with col3:
|
|
self.ui.create_metric_card(
|
|
"وقت الترجمة",
|
|
"45 ثانية",
|
|
None,
|
|
self.ui.COLORS['success']
|
|
)
|
|
|
|
with col4:
|
|
self.ui.create_metric_card(
|
|
"المصطلحات الفنية",
|
|
"28",
|
|
None,
|
|
self.ui.COLORS['accent']
|
|
)
|
|
|
|
def technical_terms_dictionary(self):
|
|
"""قاموس المصطلحات الفنية"""
|
|
st.markdown("### قاموس المصطلحات الفنية")
|
|
|
|
|
|
with st.expander("إضافة مصطلح جديد"):
|
|
with st.form("add_term_form"):
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
term_ar = st.text_input("المصطلح بالعربية")
|
|
|
|
with col2:
|
|
term_en = st.text_input("المصطلح بالإنجليزية")
|
|
|
|
with col3:
|
|
term_category = st.selectbox(
|
|
"الفئة",
|
|
options=["مستندات", "ضمانات", "أنواع المناقصات", "عقود", "أطراف", "أعمال", "شروط", "أخرى"]
|
|
)
|
|
|
|
|
|
submit_button = st.form_submit_button("إضافة المصطلح")
|
|
|
|
if submit_button:
|
|
if not term_ar or not term_en:
|
|
st.error("يرجى ملء جميع الحقول المطلوبة")
|
|
else:
|
|
|
|
st.success("تمت إضافة المصطلح بنجاح")
|
|
|
|
|
|
st.markdown("#### البحث في المصطلحات")
|
|
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
search_term = st.text_input("البحث عن مصطلح")
|
|
|
|
with col2:
|
|
search_language = st.radio(
|
|
"لغة البحث",
|
|
options=["الكل", "العربية", "الإنجليزية"],
|
|
horizontal=True
|
|
)
|
|
|
|
with col3:
|
|
category_filter = st.selectbox(
|
|
"تصفية حسب الفئة",
|
|
options=["الكل", "مستندات", "ضمانات", "أنواع المناقصات", "عقود", "أطراف", "أعمال", "شروط", "أخرى"]
|
|
)
|
|
|
|
|
|
filtered_terms = self.technical_terms
|
|
|
|
if search_term:
|
|
if search_language == "العربية":
|
|
filtered_terms = [term for term in filtered_terms if search_term.lower() in term["ar"].lower()]
|
|
elif search_language == "الإنجليزية":
|
|
filtered_terms = [term for term in filtered_terms if search_term.lower() in term["en"].lower()]
|
|
else:
|
|
filtered_terms = [term for term in filtered_terms if search_term.lower() in term["ar"].lower() or search_term.lower() in term["en"].lower()]
|
|
|
|
if category_filter != "الكل":
|
|
filtered_terms = [term for term in filtered_terms if term["category"] == category_filter]
|
|
|
|
|
|
st.markdown("#### المصطلحات الفنية")
|
|
|
|
if not filtered_terms:
|
|
st.info("لا توجد مصطلحات تطابق معايير البحث")
|
|
else:
|
|
|
|
terms_df = pd.DataFrame(filtered_terms)
|
|
|
|
|
|
terms_df = terms_df.rename(columns={
|
|
"ar": "المصطلح بالعربية",
|
|
"en": "المصطلح بالإنجليزية",
|
|
"category": "الفئة"
|
|
})
|
|
|
|
|
|
st.dataframe(
|
|
terms_df,
|
|
use_container_width=True,
|
|
hide_index=True
|
|
)
|
|
|
|
|
|
col1, col2 = st.columns([1, 5])
|
|
|
|
with col1:
|
|
if st.button("تصدير القاموس", use_container_width=True):
|
|
st.success("تم تصدير القاموس بنجاح")
|
|
|
|
|
|
st.markdown("#### إحصائيات القاموس")
|
|
|
|
|
|
category_counts = {}
|
|
for term in self.technical_terms:
|
|
if term["category"] not in category_counts:
|
|
category_counts[term["category"]] = 0
|
|
category_counts[term["category"]] += 1
|
|
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
st.markdown("##### عدد المصطلحات حسب الفئة")
|
|
|
|
|
|
category_df = pd.DataFrame({
|
|
"الفئة": list(category_counts.keys()),
|
|
"العدد": list(category_counts.values())
|
|
})
|
|
|
|
|
|
st.bar_chart(category_df.set_index("الفئة"))
|
|
|
|
with col2:
|
|
st.markdown("##### إحصائيات عامة")
|
|
|
|
total_terms = len(self.technical_terms)
|
|
categories_count = len(category_counts)
|
|
|
|
st.markdown(f"**إجمالي المصطلحات:** {total_terms}")
|
|
st.markdown(f"**عدد الفئات:** {categories_count}")
|
|
st.markdown(f"**متوسط المصطلحات لكل فئة:** {total_terms / categories_count:.1f}")
|
|
st.markdown(f"**آخر تحديث للقاموس:** {datetime.datetime.now().strftime('%Y-%m-%d')}")
|
|
|
|
def show_translated_documents(self):
|
|
"""عرض المستندات المترجمة"""
|
|
st.markdown("### المستندات المترجمة")
|
|
|
|
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
entity_filter = st.selectbox(
|
|
"تصفية حسب الكيان",
|
|
options=["الكل"] + list(set([doc["related_entity"] for doc in self.translated_documents]))
|
|
)
|
|
|
|
with col2:
|
|
language_pair_filter = st.selectbox(
|
|
"تصفية حسب زوج اللغات",
|
|
options=["الكل"] + list(set([f"{doc['source_language']} -> {doc['target_language']}" for doc in self.translated_documents]))
|
|
)
|
|
|
|
with col3:
|
|
status_filter = st.selectbox(
|
|
"تصفية حسب الحالة",
|
|
options=["الكل", "مكتمل", "قيد التنفيذ"]
|
|
)
|
|
|
|
|
|
filtered_docs = self.translated_documents
|
|
|
|
if entity_filter != "الكل":
|
|
filtered_docs = [doc for doc in filtered_docs if doc["related_entity"] == entity_filter]
|
|
|
|
if language_pair_filter != "الكل":
|
|
source_lang, target_lang = language_pair_filter.split(" -> ")
|
|
filtered_docs = [doc for doc in filtered_docs if doc["source_language"] == source_lang and doc["target_language"] == target_lang]
|
|
|
|
if status_filter != "الكل":
|
|
filtered_docs = [doc for doc in filtered_docs if doc["status"] == status_filter]
|
|
|
|
|
|
if not filtered_docs:
|
|
st.info("لا توجد مستندات مترجمة تطابق معايير التصفية")
|
|
else:
|
|
|
|
docs_df = pd.DataFrame(filtered_docs)
|
|
|
|
|
|
docs_df["source_language"] = docs_df["source_language"].map(self.supported_languages)
|
|
docs_df["target_language"] = docs_df["target_language"].map(self.supported_languages)
|
|
|
|
|
|
display_df = docs_df[[
|
|
"id", "name", "source_language", "target_language", "translation_date", "status", "pages", "related_entity"
|
|
]].rename(columns={
|
|
"id": "الرقم",
|
|
"name": "اسم المستند",
|
|
"source_language": "لغة المصدر",
|
|
"target_language": "لغة الهدف",
|
|
"translation_date": "تاريخ الترجمة",
|
|
"status": "الحالة",
|
|
"pages": "عدد الصفحات",
|
|
"related_entity": "الكيان المرتبط"
|
|
})
|
|
|
|
|
|
st.dataframe(
|
|
display_df,
|
|
use_container_width=True,
|
|
hide_index=True
|
|
)
|
|
|
|
|
|
st.markdown("#### تفاصيل المستند المترجم")
|
|
|
|
selected_doc_id = st.selectbox(
|
|
"اختر مستنداً لعرض التفاصيل",
|
|
options=[doc["id"] for doc in filtered_docs],
|
|
format_func=lambda x: next((f"{doc['id']} - {doc['name']}" for doc in filtered_docs if doc["id"] == x), "")
|
|
)
|
|
|
|
|
|
selected_doc = next((doc for doc in filtered_docs if doc["id"] == selected_doc_id), None)
|
|
|
|
if selected_doc:
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
st.markdown(f"**اسم المستند:** {selected_doc['name']}")
|
|
st.markdown(f"**لغة المصدر:** {self.supported_languages[selected_doc['source_language']]}")
|
|
st.markdown(f"**لغة الهدف:** {self.supported_languages[selected_doc['target_language']]}")
|
|
st.markdown(f"**تاريخ الترجمة:** {selected_doc['translation_date']}")
|
|
|
|
with col2:
|
|
st.markdown(f"**الملف الأصلي:** {selected_doc['original_file']}")
|
|
st.markdown(f"**الملف المترجم:** {selected_doc['translated_file']}")
|
|
st.markdown(f"**المترجم:** {selected_doc['translated_by']}")
|
|
st.markdown(f"**الحالة:** {selected_doc['status']}")
|
|
|
|
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
if st.button("تنزيل الملف الأصلي", use_container_width=True):
|
|
st.success("تم بدء تنزيل الملف الأصلي")
|
|
|
|
with col2:
|
|
if st.button("تنزيل الملف المترجم", use_container_width=True):
|
|
st.success("تم بدء تنزيل الملف المترجم")
|
|
|
|
with col3:
|
|
if st.button("مشاركة الملف المترجم", use_container_width=True):
|
|
st.success("تم نسخ رابط مشاركة الملف المترجم")
|
|
|
|
|
|
st.markdown("#### إحصائيات الترجمة")
|
|
|
|
col1, col2, col3 = st.columns(3)
|
|
|
|
with col1:
|
|
|
|
language_pairs = {}
|
|
for doc in self.translated_documents:
|
|
pair = f"{self.supported_languages[doc['source_language']]} -> {self.supported_languages[doc['target_language']]}"
|
|
if pair not in language_pairs:
|
|
language_pairs[pair] = 0
|
|
language_pairs[pair] += 1
|
|
|
|
st.markdown("##### المستندات حسب زوج اللغات")
|
|
|
|
|
|
language_df = pd.DataFrame({
|
|
"زوج اللغات": list(language_pairs.keys()),
|
|
"العدد": list(language_pairs.values())
|
|
})
|
|
|
|
|
|
st.bar_chart(language_df.set_index("زوج اللغات"))
|
|
|
|
with col2:
|
|
|
|
entity_counts = {}
|
|
for doc in self.translated_documents:
|
|
if doc["related_entity"] not in entity_counts:
|
|
entity_counts[doc["related_entity"]] = 0
|
|
entity_counts[doc["related_entity"]] += 1
|
|
|
|
st.markdown("##### المستندات حسب الكيان المرتبط")
|
|
|
|
|
|
entity_df = pd.DataFrame({
|
|
"الكيان المرتبط": list(entity_counts.keys()),
|
|
"العدد": list(entity_counts.values())
|
|
})
|
|
|
|
|
|
st.bar_chart(entity_df.set_index("الكيان المرتبط"))
|
|
|
|
with col3:
|
|
|
|
total_docs = len(self.translated_documents)
|
|
completed_docs = len([doc for doc in self.translated_documents if doc["status"] == "مكتمل"])
|
|
in_progress_docs = len([doc for doc in self.translated_documents if doc["status"] == "قيد التنفيذ"])
|
|
total_pages = sum([doc["pages"] for doc in self.translated_documents])
|
|
|
|
st.markdown("##### إحصائيات عامة")
|
|
st.markdown(f"**إجمالي المستندات المترجمة:** {total_docs}")
|
|
st.markdown(f"**المستندات المكتملة:** {completed_docs}")
|
|
st.markdown(f"**المستندات قيد التنفيذ:** {in_progress_docs}")
|
|
st.markdown(f"**إجمالي الصفحات المترجمة:** {total_pages}")
|
|
st.markdown(f"**متوسط الصفحات لكل مستند:** {total_pages / total_docs:.1f}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
translation_app = TranslationApp()
|
|
translation_app.run()
|
|
|