この記事では、OpenAIのGPT-4言語モデルを使用して、私のブログ投稿の翻訳を自動化するために概念実証(POC)として開発したPythonスクリプトを共有します。このスクリプトは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解決策:革新的なスクリプト
私は、APIとしてOpenAI GPT-4を利用してテキストを翻訳し、非テキスト要素を保持するPythonスクリプトを設計しました。一連の処理ルールとプレースホルダの使用により、スクリプトはコードブロックやその他翻訳すべきでない要素を識別して除外でき、翻訳されたコンテンツが元の内容に忠実であることを保証します。
主な機能
- GPT-4による高精度な翻訳 : スクリプトはOpenAIのGPT-4モデルを使用してフランス語から英語へテキストを翻訳し、元のコンテンツの品質とニュアンスを保持します。
- フォーマットの保持 : コードブロック、URL、画像パスは翻訳中に識別され、そのまま保持されるため、元のフォーマットが保たれます。
- 多言語の柔軟性 : スクリプトはソース言語とターゲット言語を簡単に変更できるよう設計されており、多様な多言語適用が可能です。
- Markdownファイル対応 : Markdownで書かれたドキュメントを翻訳し、その構造と書式を保持します。
- ディレクトリの翻訳自動化 : 指定したディレクトリとそのサブディレクトリ内のMarkdownファイルを自動で翻訳し、大量のコンテンツ管理を容易にします。
- 翻訳ノートの挿入 : 翻訳されたドキュメントの末尾に、使用したGPTモデルを示す翻訳ノートを自動で追加します。
- 設定とカスタマイズの容易さ : APIキー、GPTモデル、ソース/ターゲット言語、ファイルディレクトリなどのデフォルト設定をカスタマイズでき、柔軟に利用できます。
- パフォーマンスレポート : 各ファイルの翻訳に要した時間を出力し、処理性能を監視できます。
スクリプトのコード
Le code est disponible aussi ici : AI搭載Markdown翻訳ツール
#!/usr/bin/env python3
import osimport argparseimport timefrom openai import OpenAIimport re
# Initialisation de la configuration avec les valeurs par défautDEFAULT_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 traductiondef 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 Markdowndef 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 APIを使用します。動作は以下の通りです:
- 正規表現を使用してテキスト内のコードブロックを検出して保存します。これらのコードブロックはトリプルバックティックで区切られており、(
). Les blocs de code sont stockés dans une liste appeléecode_blocks`。 - 次に、コードブロックをテキスト内のプレースホルダに置き換えます。プレースホルダは
#CODEBLOCK{index}#の形式で、indexが対応するコードブロックのインデックスです(code_blocksのリスト内の位置)。 - 関数はOpenAIに送るメッセージを作成します。このメッセージは2つの部分からなります:システムメッセージ(URL、画像パス、コードブロックなどの要素は翻訳せずにそのままにするよう指示)と、翻訳すべきテキストを含むユーザーメッセージです。
- 関数は
client.chat.completions.create()メソッドを使用して翻訳リクエストをAPIに送ります。モデルとメッセージを指定します。 - APIの応答から翻訳されたテキストを取得し、プレースホルダを元のコードブロックに置き換えます。
- 最後に、翻訳済みのテキストを返します。
関数 add_translation_note
次に、関数 add_translation_note があります。この関数はドキュメントに翻訳ノートを追加します。OpenAIクライアントオブジェクトと引数を受け取り、以下の手順で動作します:
- まず、
translation_note_fr変数を使用してフランス語の翻訳ノートを作成します。 - 次に、
translate_with_openai関数を使用してこの翻訳ノートをOpenAI APIでターゲット言語に翻訳します。渡される引数にはフランス語のノートと追加の引数が含まれます(translate_with_openai)。 - 翻訳されたノートをフォーマットして装飾を追加します。
- 最後に、フォーマット済みの翻訳ノートを返します。
関数 translate_markdown_file
次に、関数 translate_markdown_file があります。この関数は入力のMarkdownファイルパス、出力ファイルパス、OpenAIクライアント、引数を受け取り、OpenAI翻訳APIを使用してMarkdownファイルの内容を翻訳し、出力ファイルに書き込みます。
このスクリプトは私のブログ記事のアクセシビリティを向上させただけでなく、多言語コンテンツ作成の自動化に新たな可能性を開きました。プログラミングと人工知能を組み合わせることで、知識共有とコンテンツのアクセスの面で新しくエキサイティングな地平が開けることを示す一歩です。
利用体験と処理時間
使用例
# Création des répertoires ciblesjls42@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_enTraitement du fichier : content/posts/ia/stable-difusion-aws-ec2.mdTraduction terminée en 21.57 secondes.Fichier 'stable-difusion-aws-ec2.md' traité.Traitement du fichier : content/posts/ia/poc-openai-api-gpt4.mdTraduction terminée en 34.87 secondes.Fichier 'poc-openai-api-gpt4.md' traité.Traitement du fichier : content/posts/ia/poc-mistral-ai-mixtral.mdTraduction 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.mdTraduction 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.mdTraduction 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.mdTraduction 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.mdTraduction 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.mdTraduction terminée en 12.64 secondes.Fichier 'infrastruture-as-code-serverless-ha-jls42-org.md' traité.Traitement du fichier : content/mentions/mentions-legales.mdTraduction terminée en 11.90 secondes.Fichier 'mentions-legales.md' traité.Traitement du fichier : content/about/a-propos-du-blog-jls42.mdTraduction 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 esTraitement du fichier : content/posts/ia/stable-difusion-aws-ec2.mdTraduction terminée en 33.19 secondes.Fichier 'stable-difusion-aws-ec2.md' traité.Traitement du fichier : content/posts/ia/poc-openai-api-gpt4.mdTraduction terminée en 25.24 secondes.Fichier 'poc-openai-api-gpt4.md' traité.Traitement du fichier : content/posts/ia/poc-mistral-ai-mixtral.mdTraduction 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.mdTraduction 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.mdTraduction 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.mdTraduction 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.mdTraduction 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.mdTraduction terminée en 30.73 secondes.Fichier 'infrastruture-as-code-serverless-ha-jls42-org.md' traité.Traitement du fichier : content/mentions/mentions-legales.mdTraduction terminée en 13.14 secondes.Fichier 'mentions-legales.md' traité.Traitement du fichier : content/about/a-propos-du-blog-jls42.mdTraduction 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へ移行し、新しい多言語アーキテクチャになっています。翻訳は現在、組み込みの言語セレクタから利用可能です。
このブログ投稿は、AIを使った翻訳自動化における私の経験の凝縮です。プログラミングと人工知能を組み合わせると、可能性はほとんど無限であり、知識共有とコンテンツのアクセシビリティにおいて新しく刺激的な展望を切り開くことを示しています。
このドキュメントは gpt-5-mini モデルを使用して fr バージョンから ja 言語に翻訳されました。翻訳プロセスの詳細については、https://gitlab.com/jls42/ai-powered-markdown-translator をご覧ください。