This article documents the provider-layer of the wpresidence-translate plugin: the admin view, the settings option, the four provider files, the OpenAI request shape, glossary/memory storage, and the AJAX endpoints that drive bulk runs. It complements the user article and the broader multi-language real estate website overview.
Files Involved
| File | Role |
|---|---|
includes/admin/views/automatic-translation.php |
Settings form + bulk-run UI (post-type selection, target language, start/stop). |
includes/admin/views/glossary-memory.php |
Glossary editor (source term, preferred translation, context). |
includes/admin/auto-translate-openai.php |
Implemented provider. Entry point wpr_translate_auto_translate_post_openai() and wpr_translate_openai_translate_json_payload(). |
includes/admin/auto-translate-google.php |
Stub. Returns WP_Error( 'wpr_translate_google_not_implemented' ). |
includes/admin/auto-translate-deepl.php |
Stub. Returns WP_Error( 'wpr_translate_deepl_not_implemented' ). |
includes/admin/auto-translate-microsoft-azure.php |
Stub. Returns WP_Error( 'wpr_translate_azure_not_implemented' ). |
Options & Storage
| Option / Table | Purpose |
|---|---|
wpr_translate_auto_translation |
Core settings. Keys: enabled, engine, openai_api_key, openai_system_prompt, openai_user_prompt, target_language. |
wpr_translate_post_types |
Per-post-type choice (yes/no) controlling bulk runs. |
wpr_translate_glossary |
Array of glossary entries: source, translation, context. |
{prefix}wpestate_translation_glossary |
Optional database-backed glossary used by the auto-translate layer. |
{prefix}wpestate_translation_memory |
Translation memory keyed by similarity_hash for reuse across jobs. |
Settings Form Defaults
The view applies these defaults via wp_parse_args():
'enabled' => false,
'engine' => 'openai',
'openai_api_key' => '',
'openai_system_prompt' => 'You are a translator that converts WordPress post data into the requested language.',
'openai_user_prompt' => 'Translate the following WordPress post payload into the %1$s language.',
The $engines whitelist currently contains only 'openai' => 'OpenAI'. To expose a second provider as a selectable engine, extend this map in the view and wire up its provider file.
Provider Dispatch
Every provider function accepts the same signature, making dispatch trivial:
wpr_translate_auto_translate_post_openai( $post_id, $language, $payload = array(), $payload_json = '' );
wpr_translate_auto_translate_post_google( $post_id, $language, $payload = array(), $payload_json = '' );
wpr_translate_auto_translate_post_deepl( $post_id, $language, $payload = array(), $payload_json = '' );
wpr_translate_auto_translate_post_microsoft_azure( $post_id, $language, $payload = array(), $payload_json = '' );
Each returns true|WP_Error. Only the OpenAI implementation does real work today; the others are scaffolded and should be treated as placeholders.
OpenAI Request Shape
wpr_translate_openai_translate_json_payload( $language, $payload_json, $settings_context = 'default' ) builds:
- Endpoint:
https://api.openai.com/v1/chat/completions - Auth:
Authorization: Bearer <openai_api_key> - Body:
{ "model": "gpt-4o-mini", "response_format": { "type": "json_object" }, "messages": [ { "role": "system", "content": "<system_prompt> Keep the JSON structure identical. Respond with valid JSON only." }, { "role": "user", "content": "<user_prompt_template> Preserve the JSON structure and keys. Respond with JSON only.\n\n<payload_json>" } ] } - Timeout:
540seconds. - Retry: up to 3 attempts on
WP_Erroror non-2xx responses. - User-Agent:
WPR-Translate/<WPR_TRANSLATE_VERSION>; <home_url>.
A non-OpenAI-key configuration returns WP_Error( 'wpr_translate_missing_openai_key' ). Malformed JSON bodies return wpr_translate_openai_invalid_json. Non-2xx responses surface body text via wpr_translate_openai_http_error.
Post Payload Preparation
wpr_translate_auto_translate_post_openai() builds a focused payload so only translatable fields are sent to the model:
array(
'post_id' => <int>,
'language' => <code>,
'post_type' => <sanitised key>,
'post' => array( 'post_title', 'post_excerpt', 'post_content' ),
'post_meta_fields' => array( ... translatable meta ... ),
'taxonomy' => array( ... terms prepared for LLM ... ),
'elementor_translate' => array( ... widget translation hints ... ),
'original_post_id' => <int>,
);
Taxonomy terms are pre-processed via wpr_translate_prepare_taxonomy_payload_for_llm(). Elementor widget content (where present) is preserved in elementor_translate so widget-level text can be applied back after translation. Encoding uses JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES to keep non-Latin characters intact.
AJAX Endpoints
| Action | Callback | Purpose |
|---|---|---|
wp_ajax_wpr_translate_auto_translate_strings |
wpr_translate_admin_ajax_auto_translate_strings |
Auto-translate untranslated rows on the String Translation page. |
wp_ajax_wpr_translate_get_posts_for_auto_translation |
wpr_translate_admin_ajax_get_posts_for_auto_translation |
List posts awaiting bulk translation for the selected language + post types. |
wp_ajax_wpr_translate_save_post_type_preferences |
wpr_translate_admin_ajax_save_post_type_preferences |
Persist the wpr_translate_post_types option from the UI radios. |
wp_ajax_wpr_translate_auto_translate_post |
wpr_translate_handle_auto_translate_post_ajax |
Translate one post on demand from the editor header. |
wp_ajax_wpr_translate_auto_translate_post_publish |
wpr_translate_handle_auto_translate_post_publish_ajax |
Auto-translate on publish. |
Glossary & Translation Memory
The glossary UI (views/glossary-memory.php) writes to the wpr_translate_glossary option as a list of { source, translation, context } entries. The option is registered through settings_fields( 'wpr_translate_glossary' ). The dedicated database tables {prefix}wpestate_translation_glossary and {prefix}wpestate_translation_memory (created at plugin activation) store larger glossary entries and memory hits indexed by similarity_hash.
Debug Logging
wpr_translate_openai_error_log() emits structured log entries for key lifecycle events:
send_payload— payload JSON sent to the LLM.receive_payload_raw— raw response from the LLM.receive_payload_error— HTTP or decoding failure with error code and message.
Use these logs when a translation comes back malformed or a taxonomy mapping ends up under the wrong language code.
Extending With a New Provider
- Copy
auto-translate-openai.phpasauto-translate-myprovider.php. - Replace the HTTP call with your provider’s endpoint and authentication scheme.
- Return the same shape (
array|WP_Error) so the caller doesn’t need to change. - Extend the
$enginesmap inviews/automatic-translation.phpto expose the new engine. - Add API-key / endpoint fields to the same view and include them in the sanitiser callback used with
register_setting( 'wpr_translate_auto_translation', ... ). - Wire dispatch: where the plugin currently branches on
$settings['engine'], add acase 'myprovider'mapping to your new function.
UTF-8 Safety
All payloads are JSON-encoded with JSON_UNESCAPED_UNICODE, and the response content is decoded without any sanitisation that would corrupt non-Latin text. Keep this invariant in custom providers — do not run translation output through sanitize_title() or sanitize_key().
Known Limitations
- Google/DeepL/Azure files are stubs — do not advertise them as working integrations.
- The model is hardcoded to
gpt-4o-mini. If you need a different model, forkwpr_translate_openai_translate_json_payload()in a child-theme copy and filter the provider dispatch. - OpenAI HTTP timeout is 540 seconds — match your PHP
max_execution_timeaccordingly for very large posts.
Further Reading
- Gettext Pipeline & MO Files — how translated strings reach the front end.
- Custom Field Rules — which meta keys reach the translation payload.
- Translation Linking (trid system) — how source and translated posts are paired.
For product positioning see the multi-language real estate website page.