n8nとGeminiでWordPress記事を自動翻訳
ブログを長期にわたり運営していると、過去に蓄積された技術記事や制作ログが貴重な情報資産となります。しかし、日本語のみで発信を続けている場合、リーチできる読者は国内ユーザーに限られ、英語圏をはじめとする海外からの検索流入を獲得する機会を逃してしまいます。
当ブログ「納戸工房」においても180件以上の記事が存在しており、これらを多言語化して海外展開を進めたいと考えていました。しかし、180件を超える記事を手動で翻訳し、WordPressの管理画面から1記事ずつコピー&ペーストして投稿する作業は膨大な工数を要し、現実的ではありません。
そこで、セルフホスト環境で稼働しているワークフロー自動化ツール「n8n」と生成AIのAPIを組み合わせ、過去記事を自動で取得・翻訳し、多言語プラグイン「Bogo」の仕様に沿った形で新規公開していく完全自動化パイプラインの構築に着手しました。
n8nと生成AIによる完全自動翻訳パイプラインの構築
本システムでは、外部に専用のデータベースを新設することなく、n8nの組み込み機能である「Data Tables」を活用して翻訳キューを管理します。
システム全体の処理手順は以下の通りです。
- キュー管理: Data Tablesに対象記事のID一覧とステータス(pending / completed)を保持します。
- 元記事取得: WordPress REST API経由で未処理記事の本文・スラッグ・アイキャッチ・カテゴリ・タグを取得します。
- AI翻訳: Gemini APIへタイトルおよび本文HTMLを渡し、HTML構造を維持したまま英語に翻訳します。
- データ統合: n8nのCodeノードでJSONレスポンスを整形し、アイキャッチやタクソノミー情報をマッピングします。
- WordPress投稿: REST API経由で英語版記事を新規登録し、Bogoの言語ペアを自動で紐付けます。
- ステータス更新: Data Tablesの対象レコードを completed に更新し、次回の定期実行に備えます。
この一連のフローをスケジュールトリガーで定期実行させることで、管理者の手を一切煩わせることなく、過去記事を順次英語化していく仕組みを目指しました。
無料枠500件の活用とデータ整合性を担保する要件
パイプラインの構築にあたり、以下の定量的・定性的な要件を設定しました。
- 処理速度とコスト: 無料枠のAPIを活用し、180件以上の記事を数日以内に完全消化すること。
- データ破損率0%: 本文中のHTML構造、WordPressのブロックコメント(Gutenberg形式)、YouTube等の埋め込みiframe、コードブロック(SyntaxHighlighter形式)を一切破損させずに翻訳すること。
- 多言語情報の完全同期: 英語版記事のスラッグ、カテゴリ、タグ、アイキャッチ画像を日本語版と完全に一致させ、Bogoの言語切り替えリンクを正常に機能させること。
特にLLMモデルの選定において、当初検討していた「Gemini 3 Flash」は無料枠の1日あたりリクエスト上限(RPD)が20件に制限されており、全記事の処理に9日以上かかる計算となりました。そこで、RPDが500件に設定されている「Gemini 3.5 Flash Lite」を採用することで、APIクォータ制限に抵触することなく1〜2日で全件を処理できる基盤を整えました。
API自動投稿で直面したBogo連携の3つの技術的障壁
実装を進める中で、WordPress REST APIおよびBogoプラグインの仕様に起因する3つの重大な技術的障壁に直面しました。
プライベートメタデータ登録時の403エラー
Bogoは多言語情報を投稿メタデータ(_locale, _original_post)で管理しています。しかし、先頭にアンダースコアが付くメタキーはWordPress REST APIのセキュリティ仕様により「保護されたカスタムフィールド」とみなされ、管理者権限のアプリケーションパスワードを用いても 403 rest_cannot_update エラーが返され更新が拒絶されました。
スラッグ重複検知による -2 付与問題
Bogoで言語を切り替えるには、日本語版と英語版で完全に同一のスラッグを保持する必要があります。しかし、REST API経由で元記事と同じスラッグを指定してPOSTすると、WordPress標準の重複判定(wp_unique_post_slug)が先に働き、スラッグ末尾に強制的に -2 が付加されてしまいました。
MariaDB解析で判明したBogoの照合条件
作成した英語記事が日本語版とリンクしない問題を調査するため、MariaDBに直接ログインして wp_postmeta テーブルの実データを照合しました。
SELECT post_id, meta_key, meta_value
FROM wp_postmeta
WHERE post_id IN (58, 4712)
AND meta_key IN ('_locale', '_original_post');
調査の結果、管理画面で作成した正常な多言語ペアでは、英語記事側だけでなく「日本語元記事側にも自分自身のURL(https://donguri3.net/?p=ID)が _original_post として保存されていること」が判明しました。Bogoは双方の記事に同一の _original_post URLが存在して初めてグループとして認識する仕様になっていました。
サーバー側フックの実装とワークフローの最適化
これらの技術的障壁を解消するため、WordPressのサーバー側フック処理とn8nのワークフロー設計を統合しました。
functions.php による双方向メタ同期とスラッグ自動修正
REST API標準のメタ処理をバイパスし、記事作成フック(rest_insert_post)内で双方向のメタデータ書き込みとスラッグの再適用を実行するコードを追加しました。
add_action( 'rest_insert_post', function( $post, $request,$creating ) {
$params =$request->get_json_params();
// 1. 英語記事のロケール保存
if ( ! empty( $params['bogo_locale'] ) ) {
update_post_meta( $post->ID, '_locale', sanitize_text_field($params['bogo_locale'] ) );
}
// 2. 元記事URLを「英語記事」と「日本語元記事」の双方に保存
if ( ! empty( $params['bogo_original_post'] ) ) {
$orig_url = esc_url_raw($params['bogo_original_post'] );
// 英語記事側へ保存
update_post_meta( $post->ID, '_original_post',$orig_url );
// 日本語元記事側へも自分自身のURLを保存
if ( preg_match( '/[?&]p=(\d+)/', $orig_url,$matches ) ) {
$orig_post_id = (int)$matches[1];
update_post_meta( $orig_post_id, '_original_post',$orig_url );
}
}
// 3. スラッグの再適用(-2 を除去して本来のスラッグへ上書き)
if ( ! empty( $params['slug'] ) ) {
remove_action( 'rest_insert_post', __FUNCTION__ );
wp_update_post( array(
'ID' => $post->ID,
'post_name' => sanitize_title( $params['slug'] ),
) );
}
}, 10, 3 );
LLM翻訳プロンプトの厳格化
タグ崩れやコード破壊を防ぐため、Gemini APIに渡すシステムプロンプトを整理・最適化しました。
You are an expert technical translator for the engineering blog "Nando Kobo" (納戸工房). Translate the provided Japanese blog title and HTML content into natural, accurate, and professional English. Strict Translation Rules: 1. HTML & Block Preservation: - Preserve all HTML structures, tags, Gutenberg block comments (), , , and elements exactly as they are. - DO NOT modify, translate, or delete any URLs inside "src", "href", or "data-*" attributes (especially YouTube embed URLs). 2. Code & Technical Terms: - DO NOT translate anything inside [sourcecode]...
blocks, … tags, command lines, file paths, or configuration keys.
– Keep Japanese proper nouns or specific brand names accurate.
3. Output Format:
–
Output strictly a single valid JSON object containing exactly two keys: “title" and “content".
[/sourcecode]
n8n Codeノードによる堅牢なデータマッピング
Gemini APIからの出力テキストを安全にパースし、元記事のメタ情報(アイキャッチ画像ID、カテゴリ、タグ)と合体させてWordPress POST用データを生成します。
const rawResponse = $input.first().json;
let responseText = rawResponse.candidates[0].content.parts[0].text;
responseText = responseText.replace(/^(?:json)?\s*/i, '').replace(/\s*$/i, '').trim();
let parsedData;
try {
parsedData = JSON.parse(responseText);
} catch (e) {
// 生の改行が含まれる場合のサニタイズ処理
const sanitized = responseText.replace(/\r?\n/g, '\n');
parsedData = JSON.parse(sanitized);
}
const origPost = $('HTTP Request: GET Post').first().json;
return [{
json: {
original_id: origPost.id,
slug: origPost.slug,
title: parsedData.title,
content: parsedData.content,
status: 'publish',
featured_media: origPost.featured_media || 0,
categories: origPost.categories || [],
tags: origPost.tags || [],
bogo_locale: 'en_US',
bogo_original_post: [https://donguri3.net/?p=$](https://donguri3.net/?p=$){origPost.id}
}
}];
運用工数ゼロで実現した多言語連動と今後のアクセス検証
構築したパイプラインを稼働させた結果、1記事あたり約15〜30秒で翻訳からWordPressへの自動投稿までが完了するようになりました。
- Bogoの完全同期: 作成された英語版記事のスラッグは -2 が付かず日本語版と一致し、公開ページ上で言語切り替えスイッチが双方向に正しく動作することを確認しました。
- コンテンツの再現性: YouTube動画の埋め込みiframeやシンタックスハイライトブロック、アイキャッチ画像、タクソノミー情報が一切損なわれることなく英語化されました。
- 安定稼働: Gemini 3.5 Flash Liteの500RPD枠を活用することで、レート制限によるエラー停止を起こすことなく、全記事の多言語化が自動で進行しています。
なお、今回過去記事を一括で英語化したことによって、実際の海外アクセス数や検索パフォーマンスがどのように推移するのかについては、一定期間のデータが集まり次第、アナリティクスレポートとして改めて結果をまとめたいと考えています。
まとめ
180件以上の過去記事を英語化するため、n8nとGemini 3.5 Flash Liteを用いた自動翻訳パイプラインを構築しました。REST APIのメタデータ403拒絶やスラッグ重複、Bogo固有の双方向メタ照合仕様に対してMariaDB解析をもとにサーバー側フックを実装して解決しました。これにより、HTMLや埋め込み構造を完全に保護したまま、日本語記事と連動する英語版記事を安定して自動生成・公開できる運用体制を確立しました。


