تفشل ترجمة كتاب من 300 صفحة داخل نافذة محادثة بطريقة يمكن التنبؤ بها. عندما تطلب ترجمة، يلخّص النموذج. وتتبدّل المصطلحات من فصل إلى آخر، فيظهر المفهوم الواحد بثلاثة مصطلحات عربية مختلفة. كما تُترجم ترويسات الصفحات وأرقامها وكأنها نص. وأي جلسة تنتهي بعطل تُهدر ساعة عمل كاملة.
أردت أن أعرف ما الذي يلزم للقيام بذلك على الوجه الصحيح، فبنيت Kaman AI Translator — تطبيق سطح مكتب لويندوز يترجم كتب PDF وEPUB صفحةً صفحة إلى الفارسية الأكاديمية والعكس، مع معالجة الإخفاقات المتكررة بشفرة محلية قابلة للقراءة.
المستودع ملفا بايثون ومجموعة اختبارات. الجزء المثير ليس الواجهة، بل خط الأنابيب تحتها.
القيد الذي شكّل كل شيء
"الذكاء الاصطناعي" هنا ليس خدمة مستضافة داخل التطبيق. إنه طبقة محرك رفيعة قابلة للاستبدال — حزمة Gemini الرسمية من Google، أو أي نقطة نهاية متوافقة مع OpenAI (Ollama، LM Studio، llama.cpp، أو مزوّد سحابي).
الأمر مهم لأن استراتيجية الترجمة — التقسيم، والسياق المتحرك، وبنية الكتاب، وضبط الجودة — شفرة محلية حتمية. النموذج أداة تستدعيها هذه الاستراتيجية؛ ليس هو من يقرّر أبدًا. ولهذا السبب يمكن اختبار خط الأنابيب بالكامل دون خادم، بمحرّك وهمي ودون مفتاح API.
تقسيم لا يقطع الجملة في نصفها
المدخل الطويل هو أول ما ينكسر. الحد الافتراضي هو 6000 حرف لكل طلب، ويعمل المُقسِّم على ثلاث مستويات: حدود الفقرات أولاً، ثم حدود الجمل، ثم الكلمات — لا في منتصف الجملة أبدًا.
ثم تُعاد تغليف الوحدات ضمن الحد، فلا يُهدر طلب على فقرة واحدة ضخمة، ولا تصل قطعة مليئة بالشظايا إلى النموذج.
ويصل ذيل سياق متحرك مع كل قطعة: آخر 700 حرف من الترجمة السابقة تُضاف إلى الأمر، فيبقى المصطلح والأسلوب متسقين عبر الفصل بدل أن يبدأ كل صفحة من الصفر. يمكن تصفير هذا الإعداد وتعطيل الميزة كليًا، لكن الوضع الافتراضي مفعّل لأنه أرخص تحسين جودة في التطبيق كله.
قالب بنية خاص بكل كتاب
أكثر أجزاء الشفرة المحلية إثارةً يجيب على سؤال محدد: كيف نمنع تكرار التسمية نفسها في الكتاب الواحد خمس صيغ مختلفة؟
يكتشف التطبيق، لكل كتاب، محليًا ومجانًا، أمرين:
- العناوين — عنوان الفصل ينبغي أن يُقرأ كعنوان فصل لا كنص عادي.
- التسميات المتكررة — مثل "هل تعلم؟" أو "الأنشطة"، التي تتكرر عشرات المرات ويجب أن تُترجَم متطابقة في كل مرة.
يُدرَج الاثنان في كل أمر خاص بذلك الكتاب، فتُترجَم التسمية دائمًا بالطريقة نفسها. والاثنان قابلان للتعديل يدويًا، ويمرّ تمرير اختياري بالذكاء الاصطناعي يقرأ فهرس المحتويات ويستخرج بنية أعمق مقابل طلب API إضافي واحد فقط.
الكشف رخيص: الأسطر التي تتكرر في ربع الصفحات المُعاينة على الأقل، قرب الحافة العليا أو السفلى للصفحة، تُعد ترويسات متكررة وتُحذف قبل أن يراها النموذج أصلًا. وتُجمع الكلمات المكسورة بشرطة — exam- مع ple تصير example — بينما تُحمى أرقام القوائم والصيغ داخل النص.
أوامر ملزمة، وجولة ثانية تتحقق من العمل
الأوامر صريحة: يجب ألّا تحذف الترجمة، وألّا تلخّص، وأن تحفظ بنية الفقرات والأرقام والمعادلات وعلامات الإحالة مثل [12]، وأن تستخدم مصطلحًا واحدًا متسقًا لكل مفهوم.
ثم هناك حلقة ضبط الجودة الاختيارية: جولة ثانية تقارن الترجمة بنصها المصدر، وتُعلّم الانزياح المعنوي والمحذوفات، وتعيد نسخة مصحّحة. هذه نداء ثانٍ حقيقي، والواجهة تقول ذلك — تكلفة هذه الجولة تكاد تكون الضعف.
وهناك أيضًا طبقة أمان بصيغة JSON. ردود النماذج تصل أحيانًا بصيغة JSON خام، أو بكائن JSON ملفوف بنص. تُفكّ هذه الردود أو تُصلَح أو تُقبل كنص عادي، فلا يرى المستخدم مخرجات JSON مشوّهة في صندوق الإخراج.
الصفحات الممسوحة ضوئيًا لا تُترك
الكتاب الممسوح لا طبقة نصية له. والترجمة الساذجة تتعامل مع الصفحة الفارغة كصفحة فارغة، فيصبح كتاب من 300 صفحة مسحًا هو 300 صفحة مفقودة.
هنا، النص المستخرج الفارغ تمامًا يُطلق مسارًا آخر: تُرسم الصفحة PNG بتكبير 2× (نحو 144 DPI) وتُرسَل إلى نموذج قادر على الرؤية يقرأها يترجمها في خطوة واحدة.
ومحدودان يجب ذكرهما بصراحة: يجب أن يقبل المزوّد الصور — النموذج المحلي النصي فقط لا يستطيع ذلك، ولهذا السبب يتّجه مسار الصفحة المسوحة عمليًا نحو Gemini. ولأن هذه الصفحة بلا نص مصدر مستخرج، تُخطّى حلقة ضبط الجودة فيها: لا يوجد مرجع مستقل للتحقق من الترجمة. وهذه الصفحات تستحق علامة "تمت المراجعة" البشرية أكثر من الصفحات النصية.
لا يُفقد شيء، أبدًا
أهم ما اهتممت به كان أن العمل المنجز لا يضيع أبدًا — لا بعطل، ولا بتوقف الحصة، ولا بإعادة تشغيل.
لكل كتاب ملف حالة JSON، اسمه مشتق من تجزئة مساره المطلق، ويُكتب ذرّيًا بعد كل صفحة: تُكتب البيانات إلى ملف مؤقت ثم تستبدل به os.replace، فلا يُرى الملف نصف مكتوب حتى لو قُتلت العملية أثناء الحفظ. ويحرس threading.Lock العملية كلها.
وحين تفتح كتابًا بصفحات محفوظة، يسأل التطبيق سؤالًا صريحًا — هل تكمل من أول صفحة غير مكتملة، أم تبدأ من جديد — و«متابعة» هو الافتراضي. وحتى عند البدء من جديد، يبقى قالب البنية سليمًا، فالكتاب لم يتغيّر وإعادة اكتشافه ليس سوى إهدار للوقت.
أقصى خسارة لتشغيل واحد تساوي على الأكثر الصفحة التي كانت قيد التنفيذ.
المفتاح مشفَّر، والتطبيق لا يتحدث إلى غير المزوّد
يُدخَل مفتاح API مرة واحدة في نافذة الإعدادات، ويُقنَّع في الواجهة، ويُخزَّن مشفَّرًا عبر Windows DPAPI — واجهة حماية بيانات ويندوز، مربوطة بحسابك وبجهازك. ولا يستطيع مستخدم آخر على الجهاز نفسه، ولا المستخدم نفسه على جهاز آخر، فك تشفيره.
وهناك مسار بديل للمنصّات التي لا تدعم DPAPI، والتطبيق لا يخفيه: يعود المفتاح إلى تخزين base64 موسوم بوضوح، ويُكتب السبب في encrypt_fallback.log — طابع زمني ورسالة الخطأ، ولا أي جزء من السر. وbase64 ترميز لا تشفير، والمشروع يصرّح بذلك بدل أن يدّعي غير ذلك.
سطح الشبكة بالكامل سطر واحد: المزوّد الذي اضبطته أنت. لا تتبّع، ولا فحص تحديثات، ولا تقارير أعطال، ولا خدمة طرف ثالث، ولا تحليلات. تعرض نافذة الإعدادات مسار مجلد البيانات بالضبط وزرًّا يفتحه بنقرة.
وإن أردت ألّا يغادر شيء الجهاز، فوجّه مزوّد OpenAI المتوافق إلى Ollama على localhost — لهذا بالضبط يوجد العنوان الافتراضي (http://localhost:11434/v1).
الواجهة فارسية، وهذا يعني RTL من الرأس إلى القدم
هذه ليست تخطيطًا إنجليزيًا حشوته نصوص فارسية. التطبيق كله يعمل من اليمين إلى اليسار، وبالأرقام الفارسية في كل مكان (۰۱۲۳۴۵۶۷۸۹).
ولهذا نتائج حقيقية، والمجموعة الاختبارية تُثبّتها بدل تركها للصدفة:
- «الصفحة التالية» تشير يسارًا و«السابقة» تشير يمينًا، لأن اتجاه السير في RTL كذلك.
- تُضاف لوحة المصدر أولًا في التخطيط، فتستقر على اليمين — حيث يتوقع القارئ الفارسي النص الأصلي.
- نسبة التقدم محاذاة إلى اليمين، ونص شريط التقدم الداخلي مخفي لأن Qt ترسمه على اليسار.
- قيم حقول الأرقام محاذاة إلى اليمين عبر مرشّح مخصّص، يُعاد تطبيقه عند تغيّر السمة، لأن Qt تعيد بناء تخطيط حقول الأرقام وتفقد المحاذاة لولا ذلك.
- زر النسخ أزرق عندما تكون للصفحة الحالية ترجمة ورمادي عندما لا تكون، فتُقرأ حالته دون تلميح.
أثناء التشغيل الطويل يتبع التطبيق الصفحة قيد الترجمة مباشرةً — لكن التنقل بين الصفحات يبقى حرًا تمامًا. بمجرد أن تتصفّح بنفسك، يتوقف التطبيق عن تحريك العرض. المتابعة تستمر؛ فقط التتبع التلقائي يتراجع.
ثلاثة تصديرات
عند انتهاء الكتاب، يقدّم قائمة الحفظ ثلاثة صيغ:
- صفحة واحدة بصيغة TXT، بترويسة تذكر اسم الكتاب ورقم الصفحة، ومكتوبة بـ
utf-8-sigلتُعرض الفارسية صحيحة في Notepad بدل أن تظهر مشوّهة. - الكتاب كاملًا بصيغة Markdown، قسم
##لكل صفحة بترتيب رقمي — مناسب للتتبّع والإصدار. - الكتاب كاملًا كنسخة HTML مستقلة تعمل من اليمين لليسار، بصورة الصفحة بجوار كل ترجمة، وCSS مضمّن، وقواعد طباعة تمنع انقسام الصفحة بين ورقتين. افتحها في متصفح واطبع «حفظ كـ PDF» لتحصل على PDF فارسي مرتّب مع صور الصفحات الأصلية.
مسار HTML اختيار مقصود: فإخراج PDF صحيح من اليمين لليسار يستلزم تضمين خط فارسي والتعامل الصحيح مع تخطيط ثنائي الاتجاه، والمتصفح يؤدي ذلك جيدًا أصلًا. وإخراج PDF مباشر مع رندر خط RTL في خارطة طريق المشروع، لكن تصدير HTML جيد اليوم، ويستخدم الخطوط التي يملكها المستخدم بالفعل.
ما سأبنيه تاليًا
الخطوة التالية الصادقة ليست نموذجًا أفضل، بل بيانات أفضل: مجموعة تقييم حقيقية من الكتب مع الترجمات التي ينتجها مترجم بشري جيد، لتصبح جودة خط الأنابيب قابلة للقياس لا مجرد جدل. وأول ميزة سأضيفها هي تصدير واستيراد مسرد — قائمة مصطلحات واحدة مشتركة بين عدة كتب — لأن الاتساق عبر سلسلة من الكتب هو ما لا تحله القوالب الخاصة بكل كتاب.
الأرقام، ومن أين أتت
| الحقيقة | القيمة |
|---|---|
| إصدار التطبيق | 3.0.0 |
| النواة | kaman_core.py، نحو 1400 سطر، بلا Qt |
| الواجهة | translator_app_v3.py، نحو 1800 سطر |
| مجموعة الاختبارات | 302 فحصًا، كلها ناجحة، بلا اتصال تمامًا |
| اعتماديات وقت التشغيل | 4 — PyQt6، PyMuPDF، Pillow، google-genai |
| حد القطعة الافتراضي | 6000 حرف |
| ذيل السياق المتحرك الافتراضي | 700 حرف |
| رسم الصفحة المسوحة | مصفوفة 2× (نحو 144 DPI) |
| حماية المفتاح | Windows DPAPI، مع بديل base64 يُسجَّل |
| الإصدارات | v3.0.0 (ملف تنفيذي لويندوز + أرشيف مصدر)، v2.0.0 |
تعمل مجموعة الاختبارات بلا شبكة وبلا مفتاح API وبلا حساب مزوّد: محرّكات وهمية تحل محل الحقيقية، وملف المستخدم الحقيقي لا يُمَسّ أبدًا لأن APPDATA يُعاد توجيهه إلى مجلد مؤقت قبل استيراد وحدات التطبيق. وتطبع الجولة الواحدة 302 فحصًا ناجحًا وصفر إخفاق — هذا رقم مقيس، لا مقرَّب.
والتوثيق التقني الكامل منشور في ويكي المستودع: البنية، وخط أنابيب الترجمة، والمحرّكات، وقوالب البنية، ومسار الصفحة المسوحة، ومخزن العمل، والتصديرات، والواجهة، والإعدادات، ونموذج الأمان، ومجموعة الاختبارات، وعملية الإصدار.
ذو صلة: يوثّق هذا الموقع أيضًا أداة استشارة فارسية وبوت مراسلة مبنيين على الأفكار نفسها — محرّك محلي قابل للتدقيق، مع نموذج مستضاف حيث يستحق فقط.