在这篇文章中,我分享了一个作为概念证明(POC)开发的Python脚本,用于自动化翻译我的博客文章,使用OpenAI的GPT-4语言模型。这个脚本专门设计用于处理Hugo博客的Markdown文件结构,便于管理多语言文章。它们可以在英文西班牙文中文中获得。

项目介绍:将AI与自动化融合到我的博客中

这个自动化博客文章翻译的项目是由我对人工智能的不断增长的兴趣推动的。受OpenAI GPT-4和Mistral AI API的初步经验的启发,我被实现这些技术在实际项目中的想法吸引。这不仅仅是掌握AI工具的旅程,而且是一个将自动化和创新融合到数字空间中以增加价值的愿望。

这个项目成为了一个使AI不仅仅是一个写作主题,而是一个积极的合作伙伴的探险之旅。简单高效地使用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 API的Python脚本,能够翻译文本并保留非文本元素。通过一系列处理规则和使用占位符,脚本可以识别并排除代码块和其他不可翻译元素,从而确保翻译后的内容保持原来的样子。

主要特点

  1. 精确翻译与GPT-4:脚本使用OpenAI的GPT-4模型将文本从法语翻译成英语,确保保留原始内容的质量和味道。
  2. 保留格式:代码块、URL和图像路径被识别并在翻译过程中保留不变,确保原始格式得到保留。
  3. 多语言灵活性:脚本设计为可以轻松地适应不同的源语言和目标语言,支持多种多语言应用。
  4. 支持Markdown文件:能够翻译用Markdown编写的文档,并保留其特定的结构和格式。
  5. 自动化目录翻译:自动翻译给定目录及其子目录中找到的Markdown文件,便于管理大量内容。
  6. 翻译注释的集成:自动在翻译后的文档末尾添加翻译注释,指示使用了哪个GPT模型进行翻译。
  7. 轻松的配置和个性化:可以自定义API密钥、GPT模型、源语言和目标语言以及文件夹的默认参数,以提供更大的灵活性。 性能报告:脚本提供有关翻译每个文件所需时间的反馈,以监控其性能。

脚本代码

脚本代码也可在此处获取:基于AI的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()

脚本细节

模块导入

首先,我们有一些必要的模块导入,例如osargparsetimere。这些模块用于执行文件系统操作、分析命令行参数、测量执行时间和执行文本搜索和替换操作。

常量

然后,我们有一些定义的常量,例如DEFAULT_API_KEYDEFAULT_MODELDEFAULT_SOURCE_LANGDEFAULT_TARGET_LANGDEFAULT_SOURCE_DIRDEFAULT_TARGET_DIR。这些常量代表脚本中使用的默认值,但可以通过指定命令行参数来修改。

translate_with_openai函数

接下来,我们有translate_with_openai函数。该函数接受文本、OpenAI客户端对象和参数。它使用OpenAI API来将文本从源语言翻译为目标语言。以下是其工作原理:

  1. 函数使用正则表达式检测并存储文本中的代码块。这些代码块由三个反单引号(```)分隔。代码块存储在名为code_blocks的列表中。
  2. 然后,函数用占位符替换文本中的代码块。占位符是形如#CODEBLOCK{index}#的字符串,其中index是相应代码块在code_blocks列表中的索引。
  3. 函数创建一条API OpenAI消息。该消息由两部分组成:一条系统消息,指示API将文本从源语言翻译为目标语言,同时保留像URL、图像路径和代码块这样的元素不变;以及一条用户消息,包含要翻译的文本。
  4. 函数使用client.chat.completions.create()方法将翻译请求发送到API。它指定要使用的模型和要翻译的消息。
  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客户端对象和参数。它使用OpenAI翻译API翻译Markdown文件内容,并将翻译后的内容写入输出文件中。

这个脚本不仅提高了我博客文章的可访问性,而且还为多语种内容创作领域开辟了新的自动化可能性。这是共享知识的更广泛和包容性的一步。 ## 使用体验和处理时间

使用示例

# 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秒) 这些时间证明脚本的效率和快捷。

结果

您现在可以通过以下链接访问这些翻译内容生成的结果:

这篇博客帖子是我使用IA进行翻译自动化的经验总结。这是一个证明,当将编程与人工智能相结合时,可能性几乎是无限的,在知识共享和内容可访问性方面开启了新的和令人激动的视角。

**这份文件已由模型迈斯特拉中型翻译自法文版本。

Note: This is a direct translation of the provided sentence into Chinese. It states that the document has been translated from the French version by the Mistral-medium model.**