soderlind / ai-translator-for-polylang
Create Polylang translations with the WordPress AI Client.
Package info
github.com/soderlind/ai-translator-for-polylang
Type:wordpress-plugin
pkg:composer/soderlind/ai-translator-for-polylang
Requires
- php: ^8.3
Requires (Dev)
- brain/monkey: ^2.6
- johnpbloch/wordpress-core: ^6.9
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-12 00:06:05 UTC
README
Click Polylang's Add a translation (+) button and get a linked draft that the WordPress AI Client translates in the background.
Requirements
- WordPress 7.0 or later (includes the WordPress AI Client,
wp_ai_client_prompt()) - PHP 8.3 or later
- Polylang
- An AI provider configured for the WordPress AI Client
How it works
- In a post list, click the + next to a language that has no translation yet.
- The plugin creates a draft in that language, linked to the source post. It copies what Polylang's own "Add a translation" copies: menu order, comment and ping status, translated parent, sticky state, and the taxonomies and custom fields Polylang syncs.
- A WP-Cron job sends the title, excerpt and content to the AI and saves the translation into the draft.
- While the job runs, the post list shows Translating to {language}… and the confirmation notice updates automatically. The editor warns you not to edit or publish the draft while translation is running.
- When the job finishes, the post list shows AI translation complete and the status notice links to the draft. If it fails, the notice and post state show the error and a retry link.
- Review the draft and publish it.
What keeps the content safe
- The AI never sees the markup. The WordPress HTML API pulls out the text: text nodes, the
alt,title,aria-labelandplaceholderattributes, and text values in block attributes such as a navigation link'slabel. The AI gets them as an ordered JSON list and returns one translation per item. Each translation is written back into the original markup, so tags, URLs, classes, image IDs and block structure stay exactly as they were. - Text inside
<code>,<kbd>and<samp>isn't translated. - Content goes to the AI in chunks (6000 characters by default). Chunks only split between top-level blocks, or between paragraphs in classic content.
- If the AI doesn't return one translation per item, the plugin tries once more and then marks the job failed.
- If someone edits the draft while it is being translated, the plugin throws away the AI result instead of overwriting their changes. It compares the title, excerpt and content as well as the modified time.
- Only one worker translates a post at a time, using a lock in the options table. The worker refreshes the lock before every AI request. A lock with no refresh for 15 minutes can be taken over, and a worker that loses its lock stops without saving.
- The job saves as the user who asked for the translation, so kses and capability checks work as they do in a normal edit.
Retrying
A Retry translation row action shows up on translations that failed or have been stuck in processing for more than 15 minutes.
If WordPress finds no usable AI provider for a request (it reports "No models found…", often a brief network or rate-limit hiccup), the plugin waits five seconds and tries once more. If that also fails, the job fails with "The AI provider could not be reached. Check Settings → Connectors and retry."
WP-CLI
# Create a Norwegian translation of post 42 and translate it now. wp aitfp translate 42 --language=nb --user=admin # Create it, but let WP-Cron translate it. wp aitfp translate 42 --language=nb --user=admin --async # Run (or re-run) the AI translation of translation post 43. wp aitfp run 43
--user is required by translate so it can check capabilities.
Hooks
Filters
| Filter | Arguments | Description |
|---|---|---|
aitfp_prompt_builder |
WP_AI_Client_Prompt_Builder $builder, string $field, array $language |
Change the prompt builder before a request is sent, for example to choose a provider or model. $field is title, excerpt or content. |
aitfp_content_chunk_size |
int $chunk_size |
Maximum characters of content per AI request. Default 6000, minimum 500. |
aitfp_translatable_html_attributes |
string[] $attributes |
HTML attributes whose values are translated. Default alt, title, aria-label, placeholder. |
aitfp_translatable_block_attributes |
string[] $keys |
Block attribute (JSON) keys whose string values are translated. Default alt, caption, label, placeholder, title, buttonText, text, content, citation. |
aitfp_request_timeout |
int $timeout |
AI request timeout in seconds. Default 180. |
aitfp_translated_post_data |
array $data, WP_Post $post, array $language |
Change the post data before it goes to wp_update_post(). |
aitfp_spawn_cron |
bool $spawn, int $post_id |
Whether to start WP-Cron right after queuing. Default true, even when DISABLE_WP_CRON is set. |
Actions
| Action | Arguments | Description |
|---|---|---|
aitfp_translated |
int $post_id, array $language, int $source_id |
Fires after a post has been translated. |
aitfp_translation_failed |
int $post_id, WP_Error $error, array $language |
Fires when a translation fails. |
$language is an array with code (Polylang slug), name and locale.
Example: choose a model
add_filter( 'aitfp_prompt_builder', function ( $builder ) { return $builder->using_model_preference( 'gpt-5-mini' ); } );
Uninstall
When you delete the plugin, it removes its post meta (_aitfp_*) and any scheduled aitfp_translate_post events, on every site in a multisite network. The translated posts stay.
Development
composer install
composer test
Unit tests run without WordPress: Brain Monkey mocks WordPress functions, and the HTML API classes are loaded from the johnpbloch/wordpress-core dev dependency.
To regenerate the translation template and compile existing translations, install WP-CLI and run:
npm run i18n
The template is saved in languages/. Add translated .po files there before
rerunning the command. If translatable JavaScript is added, map its source files
to their built paths in i18n-map.json and add --use-map=i18n-map.json to the
i18n:make-json script.
To run the end-to-end test (WP-CLI, admin requests and real AI requests) against a running site:
AITFP_URL=https://example.test scripts/acceptance.sh
License
GPL v2 or later. Copyright Per Soderlind.