27 KiB
Translate WordPress posts and ACF fields using DeepL and OpenAI
Translate WordPress posts and ACF fields using DeepL and OpenAI
1. Workflow Overview
Purpose:
This workflow receives a WordPress post event (via webhook), fetches the full post data from WordPress, detects the source language using OpenAI, determines which target translations are missing, optionally corrects the original post’s language flag in WordPress, then translates Gutenberg content + selected ACF text fields using DeepL, reconstructs the translated post payload while preserving ACF structure, and finally creates translated draft posts in WordPress linked to the original.
Typical use cases:
- Multilingual WordPress sites (e.g., Polylang/WPML-like setups) where posts must be replicated per language.
- Sites using Gutenberg content and Advanced Custom Fields (ACF), including nested structures (repeaters/flexible content).
- Avoiding duplicate translations by checking existing
translationsmetadata from WordPress.
1.1 Trigger & Fetch Raw WP Post Data
Receives a POST webhook containing a post identifier/type, then fetches the canonical WP REST API post object.
1.2 AI Language Detection & Smart Routing
Builds a short text snippet, detects language via OpenAI, decides whether the WP language flag must be fixed, and computes which target languages still need translations.
1.3 Flatten ACF Structure & Translate with DeepL
Extracts translatable strings from post title/content + ACF (based on configured keys), splits execution by each target language, and sends a batch translation request to DeepL (HTML-aware).
1.4 Rebuild ACF Object & Push to WordPress
Reapplies translated strings into the correct title/content/ACF paths, cleans the ACF JSON, and creates translated WordPress posts as drafts linked to the original.
2. Block-by-Block Analysis
Block 1.1 — Trigger & Fetch Raw WP Post Data
Overview:
This block starts the workflow via webhook and retrieves the complete post JSON from the WordPress REST API using Basic Auth.
Nodes Involved:
- Webhook
- Get Post Data
Node: Webhook
- Type / Role:
n8n-nodes-base.webhook— Entry point (HTTP trigger). - Config (interpreted):
- Method:
POST - Path:
translate-post(endpoint becomes.../webhook/translate-postdepending on n8n setup)
- Method:
- Inputs/Outputs:
- Output →
Get Post Data
- Output →
- Key data expected in input:
- Uses expressions later like:
$json.body.post.IDand$json.body.post.post_type
- Uses expressions later like:
- Edge cases / failures:
- Missing/invalid payload structure (no
body.post.IDorbody.post.post_type) will break URL construction downstream. - If webhook is publicly accessible, consider authentication/secret header validation (not implemented here).
- Missing/invalid payload structure (no
- Version notes: Node typeVersion
1.
Node: Get Post Data
- Type / Role:
n8n-nodes-base.httpRequest— Fetch post from WP REST API. - Config (interpreted):
- URL is dynamic:
- Base:
https://your-wordpress-site.com/wp-json/wp/v2/ - Endpoint chosen by
post_type:- if
post→posts - if
solution→solutions - else → uses the raw
post_typedirectly
- if
- Then appends
/{ID}
- Base:
- Authentication: HTTP Basic Auth via n8n “generic credential type”
- URL is dynamic:
- Key expressions:
{{$json.body.post.post_type}},{{$json.body.post.ID}}
- Inputs/Outputs:
- Input ←
Webhook - Output →
Prepare Text Snippet
- Input ←
- Edge cases / failures:
- 401/403 if Application Password / user lacks permissions.
- 404 if post type endpoint does not exist or ID invalid.
- If WP returns a different schema (missing
content.rendered,_links.self[0].href, etc.), later nodes may fail.
- Sticky notes applying:
- “## 1. Trigger & Fetch Raw WP Post Data”
- “### URL and WP APP Password\nUpdate the URL to your WP domain and select your WP App Password credentials.”
- Version notes: typeVersion
3.
Block 1.2 — AI Language Detection & Smart Routing
Overview:
Creates a short plain-text snippet, detects its language using OpenAI, determines required target translations, and optionally corrects the original post’s language flag in WordPress. A merge node reunifies the “fix language” and “no fix” paths.
Nodes Involved:
- Prepare Text Snippet
- OpenAI Language Detect
- Smart Router & Targets
- Needs WP Lang Fix?
- Fix WP Language Flag
- Merge
- Needs Translations?
Node: Prepare Text Snippet
- Type / Role:
n8n-nodes-base.function— Build a short sample text for detection. - Config (interpreted):
- Creates
textSnippetfrom:post.title.rendered- plus stripped HTML from
post.content.renderedif present - else falls back to
post.acf.introif present
- Truncates to 1000 chars to reduce OpenAI token usage.
- Creates
- Output:
- Passes through the full post JSON plus
textSnippet.
- Passes through the full post JSON plus
- Inputs/Outputs:
- Input ←
Get Post Data - Output →
OpenAI Language Detect
- Input ←
- Edge cases / failures:
- If
title.renderedmissing, may throw. - If content is extremely large, still safe due to substring but HTML stripping can be expensive (usually fine).
- If
- Version notes: typeVersion
1.
Node: OpenAI Language Detect
- Type / Role:
n8n-nodes-base.openAi— Chat completion for language detection. - Config (interpreted):
- Resource:
chat - Model:
gpt-4o-mini - System message instructs: output ONLY 2-letter ISO 639-1 code.
- Note: The node as shown includes only a system message; in a working setup you typically also include a user message containing
{{$json.textSnippet}}. (If not configured in the UI, detection will not work.)
- Resource:
- Inputs/Outputs:
- Input ←
Prepare Text Snippet - Output →
Smart Router & Targets
- Input ←
- Edge cases / failures:
- OpenAI credential/auth errors, rate limits, transient 5xx.
- Model may output unexpected whitespace/newline; downstream
.trim().toLowerCase()mitigates. - If prompt does not include the snippet, output is undefined/unreliable.
- Sticky notes applying:
- “## 2. AI Language Detection & Smart Routing”
- Version notes: typeVersion
1.
Node: Smart Router & Targets
- Type / Role:
n8n-nodes-base.function— Decide detected language, targets, and whether to fix WP language flag. - Config (interpreted):
- Reads:
originalPostfrom$items("Prepare Text Snippet")[0].jsondetectedLangfromitems[0].json.message.contentcurrentWpLangfromoriginalPost.lang(defaults toen)
- Supported languages configured in
allSupported = ['en', 'fr', 'de'](must be updated to match site). - Computes
languagesNeeded:- targets = allSupported excluding detectedLang
- then removes languages already present in
originalPost.translationsmap
- Computes
needsLangFixif WP language flag differs from detected language. - Outputs
postTypeUrlfromoriginalPost._links.self[0].href
- Reads:
- Inputs/Outputs:
- Input ←
OpenAI Language Detect - Output →
Needs WP Lang Fix?
- Input ←
- Edge cases / failures:
- If OpenAI output not ISO code,
toLowerCase()still works but logic may mis-route. - If
_links.self[0].hrefmissing, language fix and base URL reconstruction later will fail. - If
translationsstructure differs (not a{lang: id}map), duplicate-prevention fails.
- If OpenAI output not ISO code,
- Sticky notes applying:
- “## 2. AI Language Detection & Smart Routing”
- “### Configuration:\nOpen this node and update the "allSupported" array to match your website's languages.”
- Version notes: typeVersion
1.
Node: Needs WP Lang Fix?
- Type / Role:
n8n-nodes-base.if— Conditional branch. - Condition:
{{$json.needsLangFix}} == true
- Routing:
- True →
Fix WP Language Flag - False →
Merge(input 2 / index 1)
- True →
- Inputs/Outputs:
- Input ←
Smart Router & Targets
- Input ←
- Edge cases:
- If
needsLangFixis missing/non-boolean, condition may evaluate unexpectedly.
- If
- Version notes: typeVersion
1.
Node: Fix WP Language Flag
- Type / Role:
n8n-nodes-base.httpRequest— Updates original post language in WP. - Config (interpreted):
- URL:
{{$json.postTypeUrl}}(self link of the original post) - Method:
POST - Body parameter:
lang = {{$json.detectedLang}} - Authentication: HTTP Basic Auth (WordPress Application Password)
- URL:
- Inputs/Outputs:
- Input ←
Needs WP Lang Fix?(true branch) - Output →
Merge(input 1 / index 0)
- Input ←
- Edge cases / failures:
- WP may require
PUTdepending on configuration; many WP REST endpoints acceptPOSTfor updates, but not all. - If multilingual plugin uses different field name than
lang, update will have no effect. - Auth/permission errors, or REST API disabled.
- WP may require
- Version notes: typeVersion
3.
Node: Merge
- Type / Role:
n8n-nodes-base.merge— Joins the “fixed” and “not fixed” branches back into a single flow. - Config:
- Default merge behavior (no explicit mode shown).
- Inputs/Outputs:
- Input 0 ←
Fix WP Language Flag - Input 1 ←
Needs WP Lang Fix?(false path) - Output →
Needs Translations?
- Input 0 ←
- Edge cases:
- Depending on merge mode, you may get unexpected output if both branches run (here only one runs).
- Version notes: typeVersion
2.1.
Node: Needs Translations?
- Type / Role:
n8n-nodes-base.if— Stops early if nothing to translate. - Condition:
{{$json.languagesNeeded.length > 0}} == true
- Routing:
- True →
Extract Content - False → flow ends (no further nodes)
- True →
- Inputs/Outputs:
- Input ←
Merge
- Input ←
- Edge cases:
- If
languagesNeededmissing/not an array, expression errors.
- If
- Version notes: typeVersion
1.
Block 1.3 — Flatten ACF Structure & Translate with DeepL
Overview:
Builds an ordered list of strings to translate and a matching list of “map paths” indicating where each translated string should be written back. Then splits the execution into one item per target language and sends the translation request to DeepL.
Nodes Involved:
- Extract Content
- Split by Target Lang
- DeepL Translate
Node: Extract Content
- Type / Role:
n8n-nodes-base.code— Extracts translatable strings from Gutenberg + ACF, preserving mapping. - Config (interpreted):
- Builds:
stringsToTranslate: ordered arraymapPaths: same length; entries likepost_title,post_content,acf.intro,acf.product_flexible[0].featureTitle, etc.
- Always includes
post.title.renderedmapped topost_title - Includes
post.content.renderedmapped topost_contentif non-empty - Adds
acf.introif present - Walks nested ACF objects/arrays to find keys listed in
textKeys(must be customized to your ACF schema) - Special handling: if
acf.product_flexibleexists, it recursively walks it
- Builds:
- Inputs/Outputs:
- Input ←
Needs Translations?(true branch) - Output →
Split by Target Lang
- Input ←
- Edge cases / failures:
- If ACF field names differ from
textKeys, important text won’t be translated. - If ACF contains rich structures with unexpected types, walk() is defensive but can still traverse huge objects (performance).
- Gutenberg content is translated as HTML; later DeepL uses
tag_handling=html.
- If ACF field names differ from
- Sticky notes applying:
- “## 3. Flatten ACF Structure & Translate with DeepL”
- “### ACF Keys\nOpen this node and update the "textKeys" array to match the actual ACF field names used on your WordPress site”
- Version notes: typeVersion
2(Code node, JS).
Node: Split by Target Lang
- Type / Role:
n8n-nodes-base.itemLists— Creates one item per needed target language. - Config (interpreted):
- Splits field:
languagesNeeded - Include: “allOtherFields” (keeps the rest of the JSON on each split item)
alwaysOutputData: true(prevents empty output in some scenarios)
- Splits field:
- Resulting shape:
- Each item has
languagesNeededset to a single language (string), plus original data.
- Each item has
- Inputs/Outputs:
- Input ←
Extract Content - Output →
DeepL Translate
- Input ←
- Edge cases:
- If
languagesNeededis empty, behavior depends on node internals; upstream IF prevents this.
- If
- Version notes: typeVersion
1.
Node: DeepL Translate
- Type / Role:
n8n-nodes-base.httpRequest— Calls DeepL translate API. - Config (interpreted):
- URL:
https://api-free.deepl.com/v2/translate(free endpoint; paid isapi.deepl.com) - Method:
POST - Headers:
Authorization: DeepL-Auth-Key YOUR_DEEPL_API_KEY_HERE(must be replaced)
- Body parameters:
text = {{$json.stringsToTranslate}}(array; DeepL supports repeatedtextparameters—n8n will serialize)target_lang = {{$json.languagesNeeded.toUpperCase()}}source_lang = {{$json.detectedLang.toUpperCase()}}tag_handling = html
- URL:
- Inputs/Outputs:
- Input ←
Split by Target Lang - Output →
RECONSTRUCT JSON
- Input ←
- Edge cases / failures:
- 403 if key invalid/wrong endpoint (free vs paid mismatch).
- DeepL target language codes differ from ISO639-1 in some cases (e.g.,
EN-GB,PT-PT); this workflow assumes simple 2-letter codes. - HTML handling can still mangle Gutenberg blocks in edge cases; consider
preserve_formattingor glossary if needed.
- Sticky notes applying:
- “## 3. Flatten ACF Structure & Translate with DeepL”
- “### DEEPL Translate\nAdd your DeepL API Key in the Header Parameters.”
- Version notes: typeVersion
3.
Block 1.4 — Rebuild ACF Object & Push to WordPress
Overview:
Matches DeepL’s returned translations to the original mapPaths, writes them back into a cloned post object (title/content/ACF), cleans empty strings, then POSTs a new translated draft post to WordPress with translation linkage.
Nodes Involved:
- RECONSTRUCT JSON
- Create Translated Post
Node: RECONSTRUCT JSON
- Type / Role:
n8n-nodes-base.code— Rebuilds a translated WP payload per language. - Config (interpreted):
- Pulls the original split items via
$items("Split by Target Lang")to align translation results with the correct language and map paths. - For each translated item:
translatedTexts = items[i].json.translations(expects DeepL response shape: array of{ text: "..." })- Clones
originalPost - For each
mapPaths[index]:post_title→ writesnewPost.title.raw+newPost.title.renderedpost_content→ writesnewPost.content.raw+newPost.content.renderedacf.*→ uses a customset()to write nested values (supports arrays via[index])
- Runs
cleanAcf():- converts empty strings
""tonullrecursively
- converts empty strings
- Derives
createUrl:- takes
originalData.postTypeUrland removes the trailing/{id}segment
- takes
- Outputs per language:
lang(target language)originalIdnewTitle,newAcf,newContentcreateUrl(collection endpoint)
- Pulls the original split items via
- Inputs/Outputs:
- Input ←
DeepL Translate - Output →
Create Translated Post
- Input ←
- Edge cases / failures:
- Assumes DeepL response is
items[i].json.translationsand index-aligned withstringsToTranslate; if DeepL returns errors/partial results, mapping breaks. set()assumes intermediate objects/arrays exist; if ACF structure differs between original and expected path, may throw.splitItems[i]alignment relies on n8n preserving item order between Split → DeepL → this node; usually true, but consider adding explicit correlation IDs if you later parallelize.
- Assumes DeepL response is
- Sticky notes applying:
- “## 4. Rebuild ACF Object & Push to WordPress”
- Version notes: typeVersion
2.
Node: Create Translated Post
- Type / Role:
n8n-nodes-base.httpRequest— Creates translated WP post (draft). - Config (interpreted):
- URL:
{{$json.createUrl}}(e.g.,https://.../wp-json/wp/v2/posts) - Method:
POST - Auth: HTTP Basic Auth (WordPress Application Password)
- Body parameters:
title = {{$json.newTitle}}acf = {{$json.newAcf}}status = draftlang = {{$json.lang}}translation_of = {{$json.originalId}}contentexpression is set as=={{ $json.newContent }}(note the double==, likely a mistake; should be={{ $json.newContent }})
- URL:
- Inputs/Outputs:
- Input ←
RECONSTRUCT JSON - Output: ends workflow
- Input ←
- Edge cases / failures:
- Expression bug risk:
contentuses=={{ ... }}which may post literal text or fail; should be corrected. - WP may reject
acfunless ACF-to-REST is enabled and user has permissions. translation_ofandlangfields depend on multilingual plugin; if unsupported, these fields will be ignored or cause validation errors.
- Expression bug risk:
- Sticky notes applying:
- “## 4. Rebuild ACF Object & Push to WordPress”
- “### URL and WP APP Password\nUpdate the URL to your WP domain and select your WP App Password credentials.”
- Version notes: typeVersion
3.
3. Summary Table
| Node Name | Node Type | Functional Role | Input Node(s) | Output Node(s) | Sticky Note |
|---|---|---|---|---|---|
| Webhook | Webhook Trigger | Receives WP post event payload | — | Get Post Data | ## 1. Trigger & Fetch Raw WP Post Data |
| Get Post Data | HTTP Request | Fetches full post JSON from WP REST API | Webhook | Prepare Text Snippet | ## 1. Trigger & Fetch Raw WP Post Data; ### URL and WP APP Password\nUpdate the URL to your WP domain and select your WP App Password credentials. |
| Prepare Text Snippet | Function | Builds 1000-char snippet for language detection | Get Post Data | OpenAI Language Detect | ## 2. AI Language Detection & Smart Routing |
| OpenAI Language Detect | OpenAI (Chat) | Detects source language ISO code | Prepare Text Snippet | Smart Router & Targets | ## 2. AI Language Detection & Smart Routing |
| Smart Router & Targets | Function | Computes needed target languages + whether to fix WP lang | OpenAI Language Detect | Needs WP Lang Fix? | ## 2. AI Language Detection & Smart Routing; ### Configuration:\nOpen this node and update the "allSupported" array to match your website's languages. |
| Needs WP Lang Fix? | IF | Branch: update WP language flag or skip | Smart Router & Targets | Fix WP Language Flag; Merge | ## 2. AI Language Detection & Smart Routing |
| Fix WP Language Flag | HTTP Request | Updates original post lang in WP |
Needs WP Lang Fix? | Merge | ## 2. AI Language Detection & Smart Routing |
| Merge | Merge | Rejoins “fixed” and “not fixed” paths | Fix WP Language Flag; Needs WP Lang Fix? | Needs Translations? | ## 2. AI Language Detection & Smart Routing |
| Needs Translations? | IF | Stops if no missing translations | Merge | Extract Content | ## 3. Flatten ACF Structure & Translate with DeepL |
| Extract Content | Code | Flattens title/content/ACF strings + creates mapping paths | Needs Translations? | Split by Target Lang | ## 3. Flatten ACF Structure & Translate with DeepL; ### ACF Keys\nOpen this node and update the "textKeys" array to match the actual ACF field names used on your WordPress site |
| Split by Target Lang | Item Lists | One item per language to translate | Extract Content | DeepL Translate | ## 3. Flatten ACF Structure & Translate with DeepL |
| DeepL Translate | HTTP Request | Batch-translates strings with HTML handling | Split by Target Lang | RECONSTRUCT JSON | ## 3. Flatten ACF Structure & Translate with DeepL; ### DEEPL Translate\nAdd your DeepL API Key in the Header Parameters. |
| RECONSTRUCT JSON | Code | Rebuilds translated post payload + ACF structure | DeepL Translate | Create Translated Post | ## 4. Rebuild ACF Object & Push to WordPress |
| Create Translated Post | HTTP Request | Creates translated draft post linked to original | RECONSTRUCT JSON | — | ## 4. Rebuild ACF Object & Push to WordPress; ### URL and WP APP Password\nUpdate the URL to your WP domain and select your WP App Password credentials. |
| Sticky Note | Sticky Note | Project info / prerequisites / setup notes | — | — | # Smart WP Translation (Gutenberg + ACF)\n\nThis workflow automatically translates new or updated WordPress posts into multiple languages using DeepL, while keeping your Advanced Custom Fields (ACF) JSON structure intact. It uses OpenAI to detect the source language and prevent duplicate translations.\n\n🛠️ Prerequisites:\n1. WordPress Application Password (for HTTP Basic Auth)\n2. OpenAI API Key\n3. DeepL API Key\n\n🚀 Setup Instructions:\n1. Add your credentials to the Webhook, Get Post Data, OpenAI, and Create Post nodes.\n2. In the "Smart Router & Targets" node, configure your supported languages.\n3. In the "Extract Content" node, update the array with your site's specific ACF keys.\n4. In the "DeepL Translate" node, add your DeepL API Key to the Header.\n\nP.S.\n\nYou can use the Deep Node, but in this template I use a generic node in case you get issues with the Deepl Node |
| Sticky Note1 | Sticky Note | Section label | — | — | |
| Sticky Note2 | Sticky Note | Section label | — | — | |
| Sticky Note3 | Sticky Note | Section label | — | — | |
| Sticky Note4 | Sticky Note | Section label | — | — | |
| Sticky Note5 | Sticky Note | Reminder | — | — | |
| Sticky Note6 | Sticky Note | Reminder | — | — | |
| Sticky Note7 | Sticky Note | Reminder | — | — | |
| Sticky Note8 | Sticky Note | Reminder | — | — | |
| Sticky Note9 | Sticky Note | Reminder | — | — |
4. Reproducing the Workflow from Scratch
-
Create a Webhook trigger
- Add node: Webhook
- Set:
- HTTP Method:
POST - Path:
translate-post
- HTTP Method:
- (Recommended) Add authentication/secret validation (not included in template).
-
Fetch the WordPress post from REST API
- Add node: HTTP Request named Get Post Data
- Configure:
- Authentication: HTTP Basic Auth (use WP Application Password credentials)
- URL (expression):
https://your-wordpress-site.com/wp-json/wp/v2/{{ $json.body.post.post_type === 'post' ? 'posts' : $json.body.post.post_type === 'solution' ? 'solutions' : $json.body.post.post_type }}/{{ $json.body.post.ID }}
- Create credentials in n8n:
- Credential type: HTTP Basic Auth
- Username: WP username
- Password: WordPress Application Password
- Connect: Webhook → Get Post Data
-
Prepare a short text snippet
- Add node: Function named Prepare Text Snippet
- Paste logic that:
- Builds
textSnippetfromtitle.renderedand strippedcontent.rendered(oracf.intro) - Truncates to 1000 chars
- Builds
- Connect: Get Post Data → Prepare Text Snippet
-
Detect language with OpenAI
- Add node: OpenAI named OpenAI Language Detect
- Resource: Chat
- Model:
gpt-4o-mini - Messages:
- System: instruction to output only 2-letter code
- User: include the snippet, e.g.
{{ $json.textSnippet }}
- Add OpenAI credentials (API key).
- Connect: Prepare Text Snippet → OpenAI Language Detect
-
Compute routing (targets + fix flag)
- Add node: Function named Smart Router & Targets
- Implement:
allSupportedarray (edit to match your site, e.g.['en','fr','de'])detectedLangfrom OpenAI output- Remove already existing translations using
originalPost.translations - Output:
languagesNeeded,needsLangFix,postTypeUrl,originalId
- Connect: OpenAI Language Detect → Smart Router & Targets
-
Optional: Fix original WP language flag
- Add node: IF named Needs WP Lang Fix?
- Condition:
{{ $json.needsLangFix }} is true
- Condition:
- Add node: HTTP Request named Fix WP Language Flag
- URL:
{{ $json.postTypeUrl }} - Method:
POST(or adapt to PUT if your WP endpoint requires it) - Auth: same WP Basic Auth credential
- Body:
lang = {{ $json.detectedLang }}
- URL:
- Add node: Merge named Merge to rejoin branches
- Connect:
- IF true → Fix WP Language Flag → Merge (Input 1)
- IF false → Merge (Input 2)
- Connect:
- Add node: IF named Needs WP Lang Fix?
-
Stop if nothing to translate
- Add node: IF named Needs Translations?
- Condition:
{{ $json.languagesNeeded.length > 0 }} is true
- Condition:
- Connect: Merge → Needs Translations?
- Add node: IF named Needs Translations?
-
Extract/flatten content and ACF strings
- Add node: Code named Extract Content
- Configure:
- Maintain
textKeyslist and customize it to your ACF field names. - If you have flexible/repeater fields, adapt the
walk()entry points (e.g.,acf.product_flexible).
- Maintain
- Connect: Needs Translations? (true) → Extract Content
-
Split per target language
- Add node: Item Lists named Split by Target Lang
- Operation: split out items by field
languagesNeeded - Include: “all other fields”
- Connect: Extract Content → Split by Target Lang
-
Translate with DeepL
-
Add node: HTTP Request named DeepL Translate
-
Configure:
- URL:
https://api-free.deepl.com/v2/translate(or paid endpoint) - Method:
POST - Header:
Authorization = DeepL-Auth-Key <YOUR_KEY> - Body:
text = {{ $json.stringsToTranslate }}source_lang = {{ $json.detectedLang.toUpperCase() }}target_lang = {{ $json.languagesNeeded.toUpperCase() }}tag_handling = html
- URL:
-
Connect: Split by Target Lang → DeepL Translate
-
Reconstruct translated payload
-
Add node: Code named RECONSTRUCT JSON
-
Implement:
- Reapply DeepL translations to the cloned post using
mapPaths - Write back to title/content/ACF
- Clean empty strings in ACF (optional but recommended)
- Compute
createUrlfrom the original self URL
- Reapply DeepL translations to the cloned post using
-
Connect: DeepL Translate → RECONSTRUCT JSON
-
Create translated draft in WordPress
-
Add node: HTTP Request named Create Translated Post
-
Configure:
- URL:
{{ $json.createUrl }} - Method:
POST - Auth: WP Basic Auth credential
- Body:
title = {{ $json.newTitle }}content = {{ $json.newContent }}(ensure the expression starts with a single=)acf = {{ $json.newAcf }}status = draftlang = {{ $json.lang }}translation_of = {{ $json.originalId }}(plugin-dependent)
- URL:
-
Connect: RECONSTRUCT JSON → Create Translated Post
5. General Notes & Resources
| Note Content | Context or Link |
|---|---|
| Smart WP Translation (Gutenberg + ACF): automatically translates posts into multiple languages using DeepL, preserves ACF JSON, uses OpenAI to detect source language and prevent duplicates. | Workflow sticky note (project overview) |
| Prerequisites: WordPress Application Password (HTTP Basic Auth), OpenAI API key, DeepL API key. | Workflow sticky note (prerequisites) |
Setup: update WP domain URLs, configure supported languages in “Smart Router & Targets”, update ACF textKeys in “Extract Content”, add DeepL key in “DeepL Translate” header. |
Workflow sticky note (setup instructions) |
| “You can use the DeepL node, but in this template I use a generic node in case you get issues with the Deepl Node.” | Workflow sticky note (implementation choice) |