في هذه المقالة، أشارك سكربت بايثون طورته كعرض مفهوم (POC) لأتمتة ترجمة منشورات مدونتي، باستخدام نموذج اللغة GPT-4 من OpenAI. هذا السكربت مصمم خصيصاً لمعالجة ملفات Markdown في هيكل مدونتي Hugo، مما يسهل إدارة المقالات متعددة اللغات. وهي متاحة باللغات الإنجليزية، الإسبانية و الصينية.

بداية المشروع: دمج الذكاء الاصطناعي والأتمتة لمدونتي

هذا المشروع لأتمتة ترجمة مقالات مدونتي بدأ من شغفي المتزايد بالذكاء الاصطناعي. مستوحياً من تجاربي الأولية مع واجهات برمجة التطبيقات لGPT-4 من OpenAI وMistral AI، انجذبت إلى فكرة تحقيق هذه التقنيات في مشروع عملي، يوفر قيمة ملموسة لمدونتي. لم يكن هذا مجرد سعي للتمكن من أدوات الذكاء الاصطناعي، بل أيضاً رغبة في دمج الأتمتة والابتكار لإثراء مساحتي الرقمية.

تحول هذا المشروع إلى مغامرة حيث لم يكن الذكاء الاصطناعي مجرد موضوع للكتابة، بل شريكاً نشطاً في التطوير. كانت فكرة ترجمة مقالاتي بشكل بسيط وفعال باستخدام الذكاء الاصطناعي، مع استكشاف قدراته في الأتمتة، تفتح أمامي آفاقاً مثيرة. كانت فرصة لتجاوز الحواجز اللغوية، وجعل محتواي متاحاً لجمهور أوسع، مع التنقل في مجال الذكاء الاصطناعي الذي لا يزال في تطور مستمر.

التحدي

التحدي الرئيسي كان في إنشاء سكربت قادر على الترجمة بدقة والاحتفاظ بالشكل الأصلي للمقالات، بما في ذلك كتل الأكواد، الروابط والصور. تحدي آخر كان التأكد من قدرة السكربت على التكيف بسهولة لدعم اللغات المختلفة. كان يجب أن يكون قادراً على التعامل مع هذا الهيكل:

├── content
│   ├── about
│   │   └── a-propos-du-blog-jls42.md
│   ├── mentions
│   │   └── mentions-legales.md
│   ├── posts
│   │   ├── blog
│   │   │   └── nouveau-theme-logo.md
│   │   ├── ia
│   │   │   ├── poc-mistral-ai-mixtral.md
│   │   │   ├── poc-openai-api-gpt4.md
│   │   │   └── stable-difusion-aws-ec2.md
│   │   ├── infrastructure
│   │   │   └── infrastruture-as-code-serverless-ha-jls42-org.md
│   │   └── raspberry-pi
│   │       ├── glusterfs_distribue_replique_sur_raspberry_pi_via_ansible.md
│   │       ├── initialisation-auto-de-raspbian-sur-raspberry-pi.md
│   │       ├── installation-de-docker-sur-raspberry-pi-via-ansible.md
│   │       └── installation-de-kubernetes-sur-raspberry-pi-via-ansible.md

الحل: سكربت مبتكر

لقد صممت سكربت بايثون يعتمد على واجهة برمجة تطبيقات GPT-4 من OpenAI لترجمة النص مع المحافظة على العناصر غير النصية. بفضل مجموعة من قواعد المعالجة واستخدام الفواصل، يمكن للسكربت تحديد واستبعاد كتل الأكواد والعناصر الأخرى التي لا يمكن ترجمتها، مما يضمن بقاء المحتوى المترجم وفياً للأصل.

الميزات الرئيسية

  1. ترجمة دقيقة مع GPT-4: يستخدم السكربت نموذج GPT-4 من OpenAI لترجمة النص من الفرنسية إلى الإنجليزية، مع الحرص على الحفاظ على جودة ودقة المحتوى الأصلي.
  2. الحفاظ على التنسيق: يتم تحديد وترك كتل الأكواد، عناوين URL، ومسارات الصور كما هي أثناء الترجمة، مما يضمن الحفاظ على التنسيق الأصلي.
  3. مرونة متعددة اللغات: تم تصميم السكربت ليكون سهل التكيف مع لغات المصدر والهدف المختلفة، مما يسمح بمجموعة واسعة من التطبيقات متعددة اللغات.
  4. دعم ملفات Markdown: القدرة على ترجمة الوثائق المكتوبة بـ Markdown، مع الحفاظ على هيكلها وتنسيقها المحدد.
  5. أتمتة ترجمة الدلائل: الترجمة التلقائية لملفات Markdown الموجودة في دليل محدد والمجلدات الفرعية له، مما يسهل إدارة كميات كبيرة من المحتوى.
  6. إدراج ملاحظة ترجمة: يضيف تلقائياً ملاحظة ترجمة في نهاية الوثائق المترجمة، توضح النموذج GPT المستخدم في الترجمة.
  7. إعدادات وتخصيص سهلة: إعدادات افتراضية قابلة للتخصيص لمفتاح API، نموذج GPT، لغات المصدر والهدف، وأدلة الملفات، مما يوفر مرونة كبيرة في الاستخدام.
  8. تقرير الأداء : يوفر البرنامج تقريرًا عن الوقت اللازم لترجمة كل ملف، مما يسمح بمراقبة أدائه.

كود البرنامج

الكود متاح أيضًا هنا: مترجم Markdown المدعوم بالذكاء الاصطناعي

#!/usr/bin/env python3

import os
import argparse
import time
from openai import OpenAI
import re

# Initialisation de la configuration avec les valeurs par défaut
DEFAULT_API_KEY = 'votre-clé-api-par-défaut'
DEFAULT_MODEL = "gpt-4-1106-preview"
DEFAULT_SOURCE_LANG = 'fr'
DEFAULT_TARGET_LANG = 'en'
DEFAULT_SOURCE_DIR = 'content/posts'
DEFAULT_TARGET_DIR = 'traductions_en'

MODEL_TOKEN_LIMITS = {
    "gpt-4-1106-preview": 4096,
    "gpt-4-vision-preview": 4096,
    "gpt-4": 8192,
    "gpt-4-32k": 32768,
    "gpt-4-0613": 8192,
    "gpt-4-32k-0613": 32768
}

# Fonction de traduction
def translate_with_openai(text, client, args):
    """
    Traduit le texte donné du langage source au langage cible en utilisant l'API OpenAI.
    
    Args:
        text (str) : Le texte à traduire.
        client : L'objet client OpenAI.
        args : Les arguments contenant les informations sur le langage source, le langage cible et le modèle.
        
    Returns:
        str : Le texte traduit.
    """
    # Détecter et stocker les blocs de code
    code_blocks = re.findall(r'(^```[a-zA-Z]*\n.*?\n^```)', text, flags=re.MULTILINE | re.DOTALL)
    placeholders = [f"#CODEBLOCK{index}#" for index, _ in enumerate(code_blocks)]
    
    # Remplacer les blocs de code par des placeholders
    for placeholder, code_block in zip(placeholders, code_blocks):
        text = text.replace(code_block, placeholder)
    
    # Création du message pour l'API
    messages = [
        {"role": "system", "content": f"Translate the following text from {args.source_lang} to {args.target_lang}, ensuring that elements such as URLs, image paths, and code blocks (delimited by ```) are not translated. Leave these elements unchanged."},
        {"role": "user", "content": text}
    ]
    
    # Envoi de la demande de traduction
    response = client.chat.completions.create(
        model=args.model,
        messages=messages
    )
    
    # Obtenir le texte traduit et remplacer les placeholders par les blocs de code originaux
    translated_text = response.choices[0].message.content.strip()
    for placeholder, code_block in zip(placeholders, code_blocks):
        translated_text = translated_text.replace(placeholder, code_block)

    return translated_text

def add_translation_note(client, args):
    """
    Ajoute une note de traduction à un document.

    Args:
        client : Le client de traduction.
        args : Arguments supplémentaires.

    Returns:
        La note de traduction formatée.
    """
    # Note de traduction en français
    translation_note_fr = "Ce document a été traduit de la version française du blog par le modèle "
    # Traduire la note en langue cible
    translated_note = translate_with_openai(translation_note_fr + args.model, client, args)
    # Formatage de la note de traduction
    return f"\n\n**{translated_note}**\n\n"

# Traitement des fichiers Markdown
def translate_markdown_file(file_path, output_path, client, args):
    """
    Traduit le contenu d'un fichier markdown en utilisant l'API de traduction OpenAI et écrit le contenu traduit dans un nouveau fichier.

    Args:
        file_path (str): Chemin vers le fichier markdown d'entrée.
        output_path (str): Chemin vers le fichier de sortie où le contenu traduit sera écrit.
        client: Client de traduction OpenAI.
        args: Arguments supplémentaires pour le processus de traduction.

    Returns:
        None
    """
    print(f"Traitement du fichier : {file_path}")
    start_time = time.time()

    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()

    translated_content = translate_with_openai(content, client, args)
    
    # Ajouter la note de traduction à la fin du contenu traduit
    translation_note = add_translation_note(client, args)
    translated_content_with_note = translated_content + translation_note

    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(translated_content_with_note)

    end_time = time.time()
    print(f"Traduction terminée en {end_time - start_time:.2f} secondes.")

def translate_directory(input_dir, output_dir, client, args):
    """
    Traduit tous les fichiers markdown dans le répertoire d'entrée et ses sous-répertoires.

    Args:
        input_dir (str): Chemin vers le répertoire d'entrée.
        output_dir (str): Chemin vers le répertoire de sortie.
        client: Objet client de traduction.
        args: Arguments supplémentaires pour la traduction.

    Returns:
        None
    """
    for root, dirs, files in os.walk(input_dir, topdown=True):
        # Exclure les dossiers qui commencent par "traductions_"
        dirs[:] = [d for d in dirs if not d.startswith("traductions_")]

        for file in files:
            if file.endswith('.md'):
                file_path = os.path.join(root, file)
                base, _ = os.path.splitext(file)
                # Ajouter le nom du modèle utilisé dans le nom du fichier de sortie
                output_file = f"{base}-{args.model}-{args.target_lang}.md"
                relative_path = os.path.relpath(root, input_dir)
                output_path = os.path.join(output_dir, relative_path, output_file)

                os.makedirs(os.path.dirname(output_path), exist_ok=True)

                if not os.path.exists(output_path):
                    translate_markdown_file(file_path, output_path, client, args)
                    print(f"Fichier '{file}' traité.")


def main():
    """
    Fonction principale pour traduire les fichiers Markdown.

    Args:
        --source_dir (str): Répertoire source contenant les fichiers Markdown.
        --target_dir (str): Répertoire cible pour sauvegarder les traductions.
        --model (str): Modèle GPT à utiliser.
        --target_lang (str): Langue cible pour la traduction.
        --source_lang (str): Langue source pour la traduction.
    """
    parser = argparse.ArgumentParser(description="Traduit les fichiers Markdown.")
    parser.add_argument('--source_dir', type=str, default=DEFAULT_SOURCE_DIR, help='Répertoire source contenant les fichiers Markdown')
    parser.add_argument('--target_dir', type=str, default=DEFAULT_TARGET_DIR, help='Répertoire cible pour sauvegarder les traductions')
    parser.add_argument('--model', type=str, default=DEFAULT_MODEL, help='Modèle GPT à utiliser')
    parser.add_argument('--target_lang', type=str, default=DEFAULT_TARGET_LANG, help='Langue cible pour la traduction')
    parser.add_argument('--source_lang', type=str, default=DEFAULT_SOURCE_LANG, help='Langue source pour la traduction')

    args = parser.parse_args()

    openai_api_key = os.getenv('OPENAI_API_KEY', DEFAULT_API_KEY)
    with OpenAI(api_key=openai_api_key) as client:
        translate_directory(args.source_dir, args.target_dir, client, args)

if __name__ == "__main__":
    main()

شرح البرنامج

استيراد الوحدات

أولاً، لدينا بعض الوحدات الضرورية المستوردة، مثل os، argparse، time و re. تُستخدم هذه الوحدات لأداء عمليات على نظام الملفات، تحليل الحجج من سطر الأوامر، قياس وقت التنفيذ وإجراء عمليات البحث والاستبدال النصي.

الثوابت

بعد ذلك، لدينا بعض الثوابت المُعرّفة، مثل DEFAULT_API_KEY، DEFAULT_MODEL، DEFAULT_SOURCE_LANG، DEFAULT_TARGET_LANG، DEFAULT_SOURCE_DIR و DEFAULT_TARGET_DIR. تمثل هذه الثوابت القيم الافتراضية المستخدمة في البرنامج، ولكن يمكن تعديلها عن طريق تحديد الحجج في سطر الأوامر.

دالة translate_with_openai

بعد ذلك، لدينا دالة translate_with_openai. تأخذ هذه الدالة نصًا، كائن عميل OpenAI وبعض الحجج كمعطيات. تستخدم API الخاصة بـ OpenAI لترجمة النص من اللغة المصدر إلى اللغة المستهدفة. إليك كيفية عملها:

  1. تستخدم الدالة تعبيرًا منتظمًا لاكتشاف وتخزين كتل الكود في النص. هذه الكتل محددة بعلامات ثلاثية من الخلفيات (```). يتم تخزين كتل الكود في قائمة تسمى code_blocks.
  2. بعد ذلك، تستبدل الدالة كتل الكود بالنصوص المُثبّتة في النص. النصوص المثبتة هي سلاسل نصية على شكل #CODEBLOCK{index}#، حيث يشير index إلى مؤشر كتلة الكود المقابل في قائمة code_blocks.
  3. تنشئ الدالة رسالة لـ API الخاصة بـ OpenAI. تحتوي هذه الرسالة على جزأين: رسالة نظام تشير إلى API لترجمة النص من اللغة المصدر إلى اللغة المستهدفة مع ترك العناصر مثل URLs، مسارات الصور وكتل الكود دون تغيير، ورسالة مستخدم تحتوي على النص المراد ترجمته.
  4. ترسل الدالة طلب الترجمة إلى API باستخدام طريقة client.chat.completions.create(). تحدد النموذج المراد استخدامه والرسائل المراد ترجمتها.
  5. تحتوي استجابة API على النص المترجم. تسترجع الدالة النص المترجم وتستبدل النصوص المثبتة بكتل الكود الأصلية.
  6. أخيرًا، تعيد الدالة النص المترجم.

دالة add_translation_note

بعد ذلك، لدينا دالة add_translation_note. تضيف هذه الدالة ملاحظة ترجمة إلى مستند ما. تأخذ كائن عميل OpenAI وبعض الحجج كمعطيات. إليك كيفية عملها:

  1. تنشئ الدالة ملاحظة ترجمة باللغة الفرنسية باستخدام المتغير translation_note_fr.
  2. بعد ذلك، تستخدم الدالة translate_with_openai لترجمة ملاحظة الترجمة باستخدام API الخاصة بـ OpenAI. تشمل الحجج المُمررة إلى translate_with_openai ملاحظة الترجمة باللغة الفرنسية والحجج الأخرى.
  3. تُنسق الدالة ملاحظة الترجمة المترجمة بإضافة شخصيات التنسيق.
  4. أخيرًا، تُعيد الدالة ملاحظة الترجمة المُنسقة.

دالة translate_markdown_file

بعد ذلك، لدينا دالة translate_markdown_file. تأخذ هذه الدالة مسار ملف Markdown مدخل، مسار ملف المخرجات، كائن عميل OpenAI وبعض الحجج كمعطيات. تقوم بترجمة المحتوى من ملف Markdown باستخدام API الخاصة بالترجمة من OpenAI وتكتب المحتوى المترجم في ملف المخرجات.

لم يحسن هذا البرنامج الوصول إلى مقالات مدونتي فحسب، بل فتح أيضًا الطريق أمام إمكانيات جديدة للتنظيم الآلي في مجال إنشاء المحتوى متعدد اللغات. إنه خطوة للأمام نحو مشاركة أوسع وأكثر شمولية للمعرفة. ## تجربة الاستخدام ووقت المعالجة

أمثلة الاستخدام

# Création des répertoires cibles
jls42@Boo:~/blog/jls42$ mkdir content/traductions_en content/traductions_es

###############################################
# Demande de traduction à l'IA vers l'anglais #
###############################################
jls42@Boo:~/blog/jls42$ python3 translate.py --source_dir content/ --target_dir content/traductions_en
Traitement du fichier : content/posts/ia/stable-difusion-aws-ec2.md
Traduction terminée en 21.57 secondes.
Fichier 'stable-difusion-aws-ec2.md' traité.
Traitement du fichier : content/posts/ia/poc-openai-api-gpt4.md
Traduction terminée en 34.87 secondes.
Fichier 'poc-openai-api-gpt4.md' traité.
Traitement du fichier : content/posts/ia/poc-mistral-ai-mixtral.md
Traduction terminée en 62.47 secondes.
Fichier 'poc-mistral-ai-mixtral.md' traité.
Traitement du fichier : content/posts/raspberry-pi/installation-de-kubernetes-sur-raspberry-pi-via-ansible.md
Traduction terminée en 46.37 secondes.
Fichier 'installation-de-kubernetes-sur-raspberry-pi-via-ansible.md' traité.
Traitement du fichier : content/posts/raspberry-pi/installation-de-docker-sur-raspberry-pi-via-ansible.md
Traduction terminée en 10.08 secondes.
Fichier 'installation-de-docker-sur-raspberry-pi-via-ansible.md' traité.
Traitement du fichier : content/posts/raspberry-pi/initialisation-auto-de-raspbian-sur-raspberry-pi.md
Traduction terminée en 17.17 secondes.
Fichier 'initialisation-auto-de-raspbian-sur-raspberry-pi.md' traité.
Traitement du fichier : content/posts/blog/nouveau-theme-logo.md
Traduction terminée en 12.91 secondes.
Fichier 'nouveau-theme-logo.md' traité.
Traitement du fichier : content/posts/infrastructure/infrastruture-as-code-serverless-ha-jls42-org.md
Traduction terminée en 12.64 secondes.
Fichier 'infrastruture-as-code-serverless-ha-jls42-org.md' traité.
Traitement du fichier : content/mentions/mentions-legales.md
Traduction terminée en 11.90 secondes.
Fichier 'mentions-legales.md' traité.
Traitement du fichier : content/about/a-propos-du-blog-jls42.md
Traduction terminée en 18.72 secondes.
Fichier 'a-propos-du-blog-jls42.md' traité.

################################################
# Demande de traduction à l'IA vers l'espagnol #
################################################
jls42@Boo:~/blog/jls42$ python3 translate.py --source_dir content/ --target_dir content/traductions_es --target_lang es
Traitement du fichier : content/posts/ia/stable-difusion-aws-ec2.md
Traduction terminée en 33.19 secondes.
Fichier 'stable-difusion-aws-ec2.md' traité.
Traitement du fichier : content/posts/ia/poc-openai-api-gpt4.md
Traduction terminée en 25.24 secondes.
Fichier 'poc-openai-api-gpt4.md' traité.
Traitement du fichier : content/posts/ia/poc-mistral-ai-mixtral.md
Traduction terminée en 58.78 secondes.
Fichier 'poc-mistral-ai-mixtral.md' traité.
Traitement du fichier : content/posts/raspberry-pi/installation-de-kubernetes-sur-raspberry-pi-via-ansible.md
Traduction terminée en 17.64 secondes.
Fichier 'installation-de-kubernetes-sur-raspberry-pi-via-ansible.md' traité.
Traitement du fichier : content/posts/raspberry-pi/installation-de-docker-sur-raspberry-pi-via-ansible.md
Traduction terminée en 19.60 secondes.
Fichier 'installation-de-docker-sur-raspberry-pi-via-ansible.md' traité.
Traitement du fichier : content/posts/raspberry-pi/initialisation-auto-de-raspbian-sur-raspberry-pi.md
Traduction terminée en 37.12 secondes.
Fichier 'initialisation-auto-de-raspbian-sur-raspberry-pi.md' traité.
Traitement du fichier : content/posts/blog/nouveau-theme-logo.md
Traduction terminée en 18.91 secondes.
Fichier 'nouveau-theme-logo.md' traité.
Traitement du fichier : content/posts/infrastructure/infrastruture-as-code-serverless-ha-jls42-org.md
Traduction terminée en 30.73 secondes.
Fichier 'infrastruture-as-code-serverless-ha-jls42-org.md' traité.
Traitement du fichier : content/mentions/mentions-legales.md
Traduction terminée en 13.14 secondes.
Fichier 'mentions-legales.md' traité.
Traitement du fichier : content/about/a-propos-du-blog-jls42.md
Traduction terminée en 11.24 secondes.
Fichier 'a-propos-du-blog-jls42.md' traité.

وقت المعالجة

  • الإنجليزية : حوالي 4 دقائق (248.70 ثانية)
  • الإسبانية : حوالي 4.7 دقائق (284.05 ثانية)
  • الإجمالي التراكمي : حوالي 8.7 دقائق (532.75 ثانية) تُظهر هذه الأوقات فعالية وسرعة السكريبت.

النتائج

يمكنك الآن الوصول إلى نتائج هذه الترجمة التلقائية من هذه الروابط:

هذه التدوينة هي خلاصة تجربتي في أتمتة الترجمة بالذكاء الاصطناعي. إنه دليل على أنه عندما يتم الجمع بين البرمجة والذكاء الاصطناعي، فإن الاحتمالات شبه غير محدودة، مما يفتح آفاقًا جديدة ومثيرة في مجال مشاركة المعرفة وإتاحة المحتوى.

تمت ترجمة هذا المستند من النسخة fr إلى اللغة ar باستخدام النموذج gpt-4o. لمزيد من المعلومات حول عملية الترجمة، يرجى الاطلاع على https://gitlab.com/jls42/ai-powered-markdown-translator