بحث

infrastructureblogia

إحداث ثورة في ترجمة مقالات المدونة بالذكاء الاصطناعي

إحداث ثورة في ترجمة مقالات المدونة بالذكاء الاصطناعي

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

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

بدأ هذا المشروع لأتمتة ترجمة مقالات مدونتي بدافع شغفي المتزايد بالذكاء الاصطناعي. مستوحى من تجاربي الأولية مع واجهات برمجة تطبيقات OpenAI GPT-4 و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

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

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

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

  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، ومعاملات كوسائط. تستخدم واجهة برمجة تطبيقات OpenAI لترجمة النص من لغة المصدر إلى لغة الهدف. إليك كيفية عملها:

  1. تستخدم الدالة تعبيرًا نمطيًا لاكتشاف وتخزين كتل الشيفرة في النص. تُحدد هذه الكتل بواسطة ثلاث backticks (). Les blocs de code sont stockés dans une liste appelée code_blocks`.
  2. بعد ذلك، تستبدل الدالة كتل الشيفرة بنوائب (placeholders) داخل النص. تكون النويبات على شكل سلسلة من النوع #CODEBLOCK{index}#، حيث أن index هو مؤشر كتلة الشيفرة المقابلة في القائمة code_blocks.
  3. تنشئ الدالة رسالة لواجهة برمجة تطبيقات OpenAI. تحتوي هذه الرسالة على جزئين: رسالة نظام توضح للواجهة أن تترجم النص من لغة المصدر إلى لغة الهدف مع ترك عناصر مثل عناوين URL، مسارات الصور وكتل الشيفرة دون تغيير، ورسالة مستخدم تحتوي النص المراد ترجمته.
  4. ترسل الدالة طلب الترجمة إلى الواجهة باستخدام الطريقة client.chat.completions.create(). تحدد النموذج الذي سيتم استخدامه والرسائل المراد ترجمتها.
  5. تحتوي استجابة الواجهة على النص المترجم. تستخرج الدالة النص المترجم وتستبدل النويبات بكتل الشيفرة الأصلية.
  6. أخيرًا، تُرجع الدالة النص المترجم.

الدالة add_translation_note

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

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

الدالة translate_markdown_file

بعد ذلك، لدينا الدالة translate_markdown_file. تتلقى هذه الدالة مسار ملف Markdown إدخالي، مسار ملف إخراج، كائن عميل OpenAI ومعاملات كوسائط. تقوم بترجمة محتوى ملف Markdown باستخدام واجهة برمجة تطبيقات 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 ثانية) توضح هذه الأوقات كفاءة وسرعة السكربت.

النتائج

ملاحظة : يوضح هذا المثال كيفية عمل السكربت على بنية المدونة القديمة في Hugo. منذ ذلك الحين تم ترحيل المدونة إلى Astro مع بنية جديدة متعددة اللغات. أصبحت الترجمات الآن متاحة عبر محدد اللغة المدمج.

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

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