OpenAIのAPIを使って日本語のワード文章をスタイルを保持しながら英訳する
実現したこと
とある会議にて。日本語で書いてある説明書を英訳しなきゃねぇみたいな話になった。人力で頑張りましょう見たいな雰囲気が漂って来たので、無敵のPythonでやる仕組みを構築したので紹介します。
このスクリプトは元のワードファイルの形式を取得し、日本語をOpenAIのAPIを使って英語にし、形式を保持しながらワードファイルを生成する感じです。
スクリプトの概要
Word文書の翻訳: 入力されたWord文書を読み込み、内容を翻訳し、新しいWord文書として保存します。
フォーマット保持: 元の文書のフォントスタイル、色、サイズ、配置などのフォーマットを保持します。
チャンク処理: 長い文書を適切なサイズのチャンクに分割して処理することで、APIの制限を回避します。
カスタマイズ可能: コマンドラインから異なるOpenAIモデルや温度設定を指定できます。
進行状況表示: tqdmライブラリを使用して、翻訳の進行状況をリアルタイムで表示します。
環境構築
OpanAI APIキー取得
OpenAIのウェブサイトでアカウントを作成し、APIキーを取得します。詳細な手順はこちらの記事を参照してください。
conda環境構築
# environment_word_translator.yml
name: word-translator
channels:
- conda-forge
- defaults
dependencies:
- python=3.12
- python-dotenv
- openai
- python-docx
- tqdm
conda env create -f environment_word_translator.yml
スクリプト
import os
import argparse
from dotenv import load_dotenv
from typing import List, Tuple, Optional
from docx import Document
from docx.shared import RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.text.paragraph import Paragraph
from openai import OpenAI
from tqdm import tqdm
# Load environment variables from .env file
load_dotenv()
# OpenAI API key configuration
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
MAX_TOKENS = 4000 # Adjusted for GPT-4's capacity
def split_text(text: str, max_tokens: int) -> List[str]:
"""Split text into chunks that don't exceed max_tokens."""
words = text.split()
chunks = []
current_chunk = []
for word in words:
if len(" ".join(current_chunk + [word])) > max_tokens:
chunks.append(" ".join(current_chunk))
current_chunk = [word]
else:
current_chunk.append(word)
if current_chunk:
chunks.append(" ".join(current_chunk))
return chunks
def translate_text(client: OpenAI, text: str, model: str, temperature: float) -> str:
"""Translate text to B2 level academic English using OpenAI's GPT-4 model."""
chunks = split_text(text, MAX_TOKENS)
translated_chunks = []
for chunk in tqdm(chunks, desc="Translating chunks", unit="chunk"):
messages = [
{"role": "system", "content": """You are a professional translator specializing in academic writing. Your task is to translate the following text to English at a B2 level (upper intermediate) with an academic tone. Follow these guidelines:
1. Maintain the original meaning and style of the text.
2. Use appropriate academic vocabulary and structures, but avoid overly complex or obscure terms.
3. Ensure diversity in word choice and sentence structures. Avoid repetitive use of certain words or phrases.
4. Do not use overly formal or archaic language. Aim for clarity and readability.
5. Preserve the original tone and intent of the text, whether it's persuasive, informative, or analytical.
6. Be aware of context and field-specific terminology, translating them accurately.
7. Avoid using cliché phrases or expressions that are overused in AI-generated text.
8. If the original text includes colloquialisms or idioms, translate them into appropriate English equivalents that maintain the intended meaning and tone.
9. Pay attention to nuances and connotations in the original text, and strive to convey these in the translation.
10. If you encounter any ambiguities in the original text, translate in a way that preserves that ambiguity rather than making assumptions.
Translate the following text, keeping these guidelines in mind:"""},
{"role": "user", "content": chunk}
]
try:
response = client.chat.completions.create(
messages=messages,
model=model,
max_tokens=MAX_TOKENS,
n=1,
stop=None,
temperature=temperature,
)
translated_chunks.append(response.choices[0].message.content.strip())
except Exception as e:
print(f"Translation error: {e}")
translated_chunks.append(chunk)
return " ".join(translated_chunks)
def process_paragraph(client: OpenAI, paragraph: Paragraph, model: str, temperature: float) -> Tuple[str, List[Tuple]]:
"""Process a paragraph and its runs, returning translated text and formatting info."""
translated_text = translate_text(client, paragraph.text, model, temperature)
formatting = []
for run in paragraph.runs:
formatting.append((
run.bold, run.italic, run.underline,
run.font.name or "Default", run.font.size,
run.font.color.rgb if run.font.color.rgb else RGBColor(0, 0, 0)
))
return translated_text, formatting
def apply_formatting(paragraph: Paragraph, text: str, formatting: List[Tuple]) -> None:
"""Apply formatting to a paragraph based on the original formatting."""
paragraph.text = ""
words = text.split()
format_index = 0
current_run = paragraph.add_run()
for word in words:
if format_index < len(formatting):
bold, italic, underline, font_name, font_size, color = formatting[format_index]
current_run = paragraph.add_run(word + " ")
current_run.bold = bold
current_run.italic = italic
current_run.underline = underline
current_run.font.name = font_name
if font_size:
current_run.font.size = font_size
current_run.font.color.rgb = color
# Move to the next format if the current run is longer than the original
if len(current_run.text) >= len(formatting[format_index][3]):
format_index += 1
else:
current_run.add_text(word + " ")
def get_document_margins(doc: Document) -> Tuple[float, float, float, float]:
"""Get the margins of the document in inches."""
section = doc.sections[0]
return (
section.top_margin.inches,
section.bottom_margin.inches,
section.left_margin.inches,
section.right_margin.inches
)
def set_document_margins(doc: Document, margins: Tuple[float, float, float, float]) -> None:
"""Set the margins of the document in inches."""
section = doc.sections[0]
section.top_margin = Inches(margins[0])
section.bottom_margin = Inches(margins[1])
section.left_margin = Inches(margins[2])
section.right_margin = Inches(margins[3])
def translate_word_document(input_path: str, output_path: str, api_key: str, model: str, temperature: float) -> None:
"""Translate a Word document to B2 level academic English, preserving formatting."""
client = OpenAI(api_key=OPENAI_API_KEY)
doc = Document(input_path)
translated_doc = Document()
# Get and set margins
original_margins = get_document_margins(doc)
set_document_margins(translated_doc, original_margins)
for paragraph in tqdm(doc.paragraphs, desc="Processing paragraphs", unit="paragraph"):
translated_text, formatting = process_paragraph(client, paragraph, model, temperature)
translated_paragraph = translated_doc.add_paragraph()
apply_formatting(translated_paragraph, translated_text, formatting)
translated_paragraph.alignment = paragraph.alignment
translated_paragraph.style = paragraph.style
translated_doc.save(output_path)
print(f"Translated Word document saved to {output_path}")
print(f"Original margins (top, bottom, left, right): {original_margins}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Translate Word documents to English.")
parser.add_argument("input_file", help="Path to the input file to be translated")
parser.add_argument("--model", default="gpt-4", help="OpenAI model to use (default: gpt-4)")
parser.add_argument("--temperature", type=float, default=0.5, help="Generation temperature (default: 0.5)")
args = parser.parse_args()
api_key = OPENAI_API_KEY
if not api_key:
raise ValueError("OPENAI_API_KEY is not set.")
input_file = args.input_file
output_dir = os.path.dirname(input_file)
output_file = os.path.join(output_dir, f"translated_{os.path.basename(input_file)}")
translate_word_document(input_file, output_file, api_key, args.model, args.temperature)
.env
# .env
OPENAI_API_KEY=sk-your_key
使用方法
ヘルプ
Japanease-word-file-translate-to-English.py --help
usage: Japanease-word-file-translate-to-English.py [-h] [--model MODEL] [--temperature TEMPERATURE] input_file
Translate Word documents to English.
positional arguments:
input_file Path to the input file to be translated
options:
-h, --help show this help message and exit
--model MODEL OpenAI model to use (default: gpt-4o)
--temperature TEMPERATURE
Generation temperature (default: 0.5)#
使用例
# ファイルあるいはファイルパスに空白がある場合は引用符で囲うこと。
python Japanease-word-file-translate-to-English.py "Path/to/your/file/sss sss.docx"
まとめ
このPythonスクリプトは長い文章でも100円ぐらい?で高品質なワードファイルの英訳が一瞬で可能になります。(自腹しました💦)
これで教育・研究活動に時間が使えるようになりハッピーになると良いですね。
AIの進歩は目覚ましいものがあります。皆さんも、このようなAI技術を活用して、より効率的な作業環境を作り上げていってください!
質問や改善点があれば、ぜひコメントで教えてください。
この記事が気に入ったらサポートをしてみませんか?