WP Residence Help WP Residence Help

  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API
Home / WPEstate Translate Plugin / Automatic Translation — Developer Guide

Automatic Translation — Developer Guide

220 views 0

This article documents the provider layer of the WPEstate Translate plugin (folder wpestate-translate): the admin view, the settings option, the normalized request, the four provider files, the OpenAI request shape and the AJAX endpoints that drive bulk runs. It complements the user article and the broader multi-language real estate website overview.

CONTENT

  • Files Involved
  • Options & Storage
  • Settings Form Defaults
  • Excluded Post Types
  • Normalized Request
  • Provider Dispatch
  • OpenAI Request Shape
  • AJAX Endpoints
  • Debug Logging
  • Extending With a New Provider
  • UTF-8 Safety
  • Known Limitations
  • Further Reading

Files Involved

File Role
includes/admin/views/automatic-translation.php Settings form + bulk-run UI (post-type selection, target language, start/stop).
includes/admin/post-editor.php wpr_translate_auto_translate_post() – builds the request, dispatches it to the selected engine and writes the result to the translated post. Also hosts the editor AJAX handlers.
includes/admin/translation-request.php wpr_translate_build_normalized_engine_request() and wpr_translate_normalize_engine_response() – one request shape shared by every engine.
includes/admin/auto-translate-openai.php Implemented provider. Transport adapter wpr_translate_openai_transport_request() and the HTTP helper wpr_translate_openai_translate_json_payload(). wpr_translate_auto_translate_post_openai() is kept as a legacy public wrapper.
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

All settings live in one option, wpr_translate_auto_translation, registered with the sanitizer wpr_translate_admin_sanitize_auto_translation_settings() (includes/admin/settings-helpers.php):

Key Purpose
enabled bool.
engine One of openai, google_translate, microsoft_azure, deepl. Anything else is saved as openai.
openai_api_key, openai_system_prompt, openai_user_prompt OpenAI credentials and prompt templates.
post_types Per-post-type choice (yes/no) for bulk runs. Written by the form and by the save_post_type_preferences AJAX call.
target_language Language code selected for the bulk run.

There is no glossary or translation memory: the wpestate_translation_glossary and wpestate_translation_memory tables are dropped on activation, and no admin page renders the glossary view.

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 map in the view contains only ‘openai’ => ‘OpenAI’, so OpenAI is the only engine an admin can pick, even though the sanitizer accepts the other three keys.

Excluded Post Types

wpr_translate_get_auto_translation_excluded_post_types() removes these from the post-type list and from bulk runs: attachment, nav_menu_item, wp_navigation, wp_template, wp_template_part, wp_block, elementor_library, wp_font_family, wp_font_face, wpestate_booking, wpestate_invoice. Change the list with the filter wpr_translate_auto_translation_excluded_post_types.

Normalized Request

wpr_translate_auto_translate_post( $post_id, $language, $engine, $source_post_id ) builds one request for every engine with wpr_translate_build_normalized_engine_request():

array(
  'language'         => <code>,
  'post_type'        => <sanitised key>,
  'post'             => array( 'post_title', 'post_excerpt', 'post_content', 'post_name' ),
  'post_meta_fields' => array( ... translatable meta ... ),
  'taxonomies'       => array( ... terms prepared for LLM ... ),
  'elementor'        => array( 'units' => array( ... ) ), // only when the Elementor document is ready
);

Taxonomy terms pass through wpr_translate_prepare_taxonomy_payload_for_llm(), which strips term identity fields. Elementor text is sent as extracted units, not as raw _elementor_data. The request is encoded with JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES to keep non-Latin characters intact.

The engine response is checked by wpr_translate_normalize_engine_response() before the first write to the target post. It rebuilds the Elementor document and taxonomy identities locally.

Provider Dispatch

wpr_translate_auto_translate_post() switches on the engine key:

case 'google_translate': wpr_translate_auto_translate_post_google( $post_id, $language, $payload, $payload_json );
case 'microsoft_azure':  wpr_translate_auto_translate_post_microsoft_azure( ... );
case 'deepl':            wpr_translate_auto_translate_post_deepl( ... );
case 'openai':
default:                 wpr_translate_openai_transport_request( ... );

Every adapter takes ( $post_id, $language, $payload, $payload_json ) and must return the decoded translated array or a WP_Error. Only OpenAI does real work today.

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>" }
      ]
    }
  • Context: posts use the context posts; string runs use strings, which adds “This request contains WordPress string translations.” before the payload.
  • Timeout: 540 seconds.
  • Retry: up to 3 attempts on WP_Error or non-2xx responses.
  • User-Agent: WPR-Translate/<WPR_TRANSLATE_VERSION>; <home_url>
WP_Error code When
openai_no_api_key No API key saved.
openai_http_error Transport failure after 3 attempts (timeout, DNS, SSL); message includes the transport error.
openai_http_status Non-2xx after 3 attempts; message includes the status, the API message and a hint for 401 (key rejected) and 429 (rate limit).
openai_empty_response Empty body or no message content.
openai_json_decode Response body is not JSON.
openai_api_error 2xx response that carries an error object.
openai_inner_json_decode The model’s content is not valid JSON.

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 screen (WPEstate Translate > Theme & Plugins Strings).
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 Save post_types and target_language into wpr_translate_auto_translation (manage_options, nonce wpr_translate_save_post_types).
wp_ajax_wpr_translate_auto_translate_post wpr_translate_handle_auto_translate_post_ajax Translate one post on demand from the post editor.
wp_ajax_wpr_translate_auto_translate_post_publish wpr_translate_handle_auto_translate_post_publish_ajax Translate and publish.

The two post handlers require edit_post on the target post and on the source post.

Debug Logging

The OpenAI file has no active request logging. wpr_translate_log_openai_elementor_widget_payload() is still called before and after the request, but returns early (“Debug logging intentionally disabled”). To inspect a failed run, read the WP_Error message returned to the editor or add temporary logging around wpr_translate_openai_translate_json_payload().

Extending With a New Provider

  1. Implement your adapter in a file of your own with the signature ( $post_id, $language, $payload, $payload_json ).
  2. Send $payload_json (or the $payload array) to your provider’s endpoint.
  3. Return the translated payload as an array with the same keys, or a WP_Error.
  4. Use one of the engine keys the plugin already accepts (google_translate, microsoft_azure, deepl). The sanitizer (settings-helpers.php) and the editor script data (post-editor.php) reset any other key to openai.
  5. The dispatch switch in wpr_translate_auto_translate_post() calls the matching wpr_translate_auto_translate_post_{engine}() function, and the view’s $engines map must list the engine before an admin can select it. Both are plugin code, so this needs a plugin change, not a child-theme hook.

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, and there is no filter to change it.
  • OpenAI HTTP timeout is 540 seconds. Match your PHP max_execution_time accordingly for very large posts.

Further Reading

  • Gettext Pipeline & MO Files – how translated strings reach the front end.
  • Meta Sync Across Language Variants – which meta keys are copied or translated.
  • Translation Linking (trid system) – how source and translated posts are paired.

For product positioning see the multi-language real estate website page.

WPEstate Translate Plugin

Related Articles

  • String Scanner — Developer Guide
  • The String Scanner
  • Gettext Pipeline & MO Files — Developer Guide
  • Gettext & MO Files — Making Translations Appear on the Front End
MLSImportWordPress IDX Plugin for MLS ListingsImport MLS listings into your WordPress website, customize your property pages, and keep your inventory updated.Start free 30-day trial →

Help Categories

  • 51 Getting Started
  • 72 Installation & Setup
  • 243 Installation FAQ
  • 18Agent, Agency & Developers
  • 5Blog Posts & Blog Lists
  • 39Elementor Shortcodes Built-In
  • 56FAQ
  • 15Footer
  • 37Header
  • 2IDX & MLSImport
  • 23Maps & Location Settings
  • 21Multi-Language - Third Party Plugins
  • 7Other Third party Plugins
  • 21Pages
  • 4Payments & Monetization
  • 20Property Lists, Categories & Archive
  • 37Property Pages & Layouts
  • 32Search & Filtering
  • 162Technical how to | Custom Code
  • 8Technical: Actions and filters
  • 7Technical: Child Theme
  • 86Theme Options & Global Settings
  • 6Translations & Languages
  • 16WPBakery Shortcodes
  • 50WPEstate Translate Plugin
  • 51WPResidence / WPEstate CRM
  • 50WPResidence 5.0 Documentation
  • 9WPResidence Elementor Studio

Join Us On

Powered by WP Estate - All Rights Reserved
  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API