|
""" |
|
وحدة الترجمة - نظام تحليل المناقصات |
|
""" |
|
|
|
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() |
|
|