Search by

soderlind / ai-translator-for-polylang

soderlind

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

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.2.0 2026-11-12 00:04 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

  1. In a post list, click the + next to a language that has no translation yet.
  2. 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.
  3. A WP-Cron job sends the title, excerpt and content to the AI and saves the translation into the draft.
  4. 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.
  5. 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.
  6. 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-label and placeholder attributes, and text values in block attributes such as a navigation link's label. 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

See docs/acceptance-test.md.

License

GPL v2 or later. Copyright Per Soderlind.