WP Residence Help WP Residence Help

  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API
Home / WPEstate Translate Plugin / Translating Posts and Pages — Developer Internals

Translating Posts and Pages — Developer Internals

182 views 0

This article is the developer-facing companion to the post translation workflow in WPEstate Translate 1.0.9. It documents the editor hooks, the AJAX handlers, the draft cloning path and the relationship layer that links a post to its translations. For product context see the user article and the multi-language real estate website landing page.

CONTENT

  • Files Involved
  • Editor Bootstrap
  • Editor Header Language Badge
  • How Posts Are Linked
  • Resolving the Source Post
  • Auto Translate AJAX Endpoints
  • What wpr_translate_auto_translate_post Touches
  • Manual Translation – Draft Cloning
  • Deleting and Trashing
  • Permissions & Nonces
  • Localized JS Settings
  • Extension Points
  • Gotchas
  • Further Reading

Files Involved

File Role
includes/admin/post-editor.php Meta box, publish-box button, asset enqueue, auto-translate AJAX handlers.
includes/admin/editor-header-language.php Localizes the block editor header language badge.
includes/admin/post-list-actions.php Add Translation request and draft cloning.
includes/admin/post-list-meta.php Meta cloning with Custom Field Rules.
includes/translation-relationships-posts.php Post family read / link / resolve.
includes/translation-relationships-lifecycle.php Delete and trash behaviour for linked posts.
includes/translation-runtime.php, includes/translation-hooks.php Active language for the request and runtime filters.

Editor Bootstrap

The post editor integration is wired up in two steps from post-editor.php:

function wpr_translate_bootstrap_post_editor_integration() {
    add_action( 'load-post.php',     'wpr_translate_initialize_post_editor_hooks' );
    add_action( 'load-post-new.php', 'wpr_translate_initialize_post_editor_hooks' );

    add_action( 'wp_ajax_wpr_translate_auto_translate_post',
        'wpr_translate_handle_auto_translate_post_ajax' );
    add_action( 'wp_ajax_wpr_translate_auto_translate_post_publish',
        'wpr_translate_handle_auto_translate_post_publish_ajax' );
}

On editor screens, wpr_translate_initialize_post_editor_hooks() then registers:

add_action( 'post_submitbox_misc_actions', 'wpr_translate_render_post_auto_translation_button' );
add_action( 'add_meta_boxes',              'wpr_translate_register_auto_translate_metabox', 10, 2 );
add_action( 'admin_enqueue_scripts',       'wpr_translate_enqueue_post_editor_assets' );

The meta box wpr-translate-auto-translate is only registered when use_block_editor_for_post() returns true. Classic editor screens use the publish-box section instead.

Editor Header Language Badge

The badge comes from wpr_translate_enqueue_post_editor_language_badge_assets(), hooked to admin_enqueue_scripts at priority 20 so it runs after wpr-translate-admin is registered. It resolves the language in this order:

  1. wpr_translate_return_current_language_admin() – admin selector state.
  2. wpr_translation_get_default_language_code() over wpestate_translation_get_active_languages() – site default.
  3. Legacy wpestate_default_language option.
  4. Hardcoded 'en'.

It then calls wpr_translate_get_language( $code ), builds a flag URL from WPR_TRANSLATE_URL . 'assets/img/flags/4x3/{code}.svg' and passes the payload to the editor JS:

wp_localize_script( 'wpr-translate-admin', 'wprTranslatePostLanguage', $payload );

Payload shape: label, code, flagUrl, flagAlt.

How Posts Are Linked

Post relationships are post meta, read through wpr_translate_relationship_get_post_family( $post_id ), which returns canonical_id, source_language and members (language code => post ID), or WP_Error for inconsistent meta. Reads are cached per request.

Post meta Stored on Value
_wpr_language Every member Language code.
_wpr_translations Source post Map language code => post ID.
wpr_translated_original_post_id Translated post Source post ID.
wpr_translated_language Translated post Language code (must agree with _wpr_language).

Posts without this meta fall back to wpr_translate_relationship_get_legacy_post_family(), which reads the wpestate_translation_translations table (content from older versions). Use wpr_translate_relationship_link_post( $source_id, $source_language, $member_id, $member_language ) to link and wpr_translate_relationship_resolve_post( $post_id, $language, $strict, $availability ) to find a translation.

Resolving the Source Post

wpr_translate_resolve_metabox_source_post_id( $post_id ) returns the family’s canonical_id when it differs from the current post, otherwise 0. If nothing resolves, the button renders disabled with the label Auto translation unavailable.

Auto Translate AJAX Endpoints

Action Handler Purpose
wpr_translate_auto_translate_post wpr_translate_handle_auto_translate_post_ajax() Translate fields inside the editor (manual button).
wpr_translate_auto_translate_post_publish wpr_translate_handle_auto_translate_post_publish_ajax() Translate and publish.

Both handlers:

  • Validate the nonce wpr_translate_auto_translate_post (field nonce).
  • Require current_user_can( 'edit_post', $post_id ) on the target and on source_post_id; a missing source returns an error.
  • Call do_action( 'qm/cease' ) when Query Monitor is active.
  • Read the engine from get_option( 'wpr_translate_auto_translation' )['engine'], validated against openai, google_translate, microsoft_azure, deepl (fallback openai).
  • Delegate to wpr_translate_auto_translate_post( $post_id, $language, $engine, $source_post_id ).

What wpr_translate_auto_translate_post Touches

  • wpr_translate_apply_translated_post_data() – post_title, post_content, post_excerpt, post_name.
  • wpr_translate_apply_translated_post_meta_fields() – per meta key, following Custom Field Rules.
  • wpr_translate_apply_translated_taxonomies() – maps terms to their translations.
  • wpr_translate_log_translated_elementor_data() and wpr_translate_log_translated_wpbakery_data() – page builder data.

WPBakery text is extracted with wpr_translate_extract_wpbakery_text_fields( $post_content ). A finished run stores _wpr_translate_auto_translated_language on the target post, which switches the button to Redo the translation.

Manual Translation – Draft Cloning

The plus icon in the post list Language column calls admin.php?action=wpr_add_translation&post_id=X&language=xx. wpr_translation_handle_add_translation_request():

  1. Checks the nonce from wpr_translation_get_translation_nonce_action( $post_id, $language ) (wpr_add_translation_{post_id}_{language}).
  2. Calls wpr_translation_create_translation_draft( $post_id, $language ).
  3. Redirects to get_edit_post_link( $new_post_id, 'raw' ).

wpr_translation_create_translation_draft() requires edit_post, reads the family and clones from the family source (or the default-language member):

Situation Result
An available translation already exists in that language It is refreshed from the source (content, template, meta, attachments, terms) and returned.
The language slot is held by a trashed translation WP_Error( 'wpr_translate_translation_trashed' ) – restore or permanently delete it first.
No translation yet New draft via wp_insert_post().

Both paths then run wpr_translation_get_validated_page_template(), wpr_translation_clone_meta_fields(), wpr_translation_clone_attachments(), wpr_translation_clone_taxonomies() and link the pair with wpr_translate_relationship_link_post(). The legacy wpr_translation_insert_translation_record() is no longer called by this flow. See Meta Sync developer guide for which meta keys are skipped when cloning.

Deleting and Trashing

  • pre_delete_post (wpr_translate_relationship_before_delete_post()) protects a source post while it still has translations. During Empty Trash, when the source and all its translations are in the trash, the translations are deleted first and the source follows.
  • “Delete all <language> translations” also finds trashed members.
  • Cloned attachments share the source file. On delete_attachment, wpr_translate_keep_shared_attachment_file() keeps the file on disk while another attachment still uses it.

Permissions & Nonces

  • Add Translation: edit_post on the source (and on an existing translation it refreshes); nonce wpr_add_translation_{post_id}_{language}.
  • Auto Translate AJAX: edit_post on target and source; nonce wpr_translate_auto_translate_post.
  • Admin menu pages require manage_options.

Localized JS Settings

wpr_translate_enqueue_post_editor_assets() enqueues assets/js/admin.js and assets/css/admin.css and localizes:

wprTranslateAutoSettings = {
    enabled: bool,
    engine:  'openai' | 'google_translate' | 'microsoft_azure' | 'deepl',
    ajax:    { url, nonce },
    strings: { ... },
};

Extension Points

  • Relationship API – read with wpr_translate_relationship_get_post_family(), link with wpr_translate_relationship_link_post(). Do not write _wpr_translations by hand.
  • Action wpr_translation_created – still fired by wpr_translation_insert_translation_record(), but that function is no longer called by the Add Translation flow. Do not rely on it for new translations.
  • Custom Field Rules – change whether a meta key is copied, translated, copied once or skipped.
  • Auto-translate providers – self-contained files in includes/admin/auto-translate-*.php.

Gotchas

  • The meta box only appears when use_block_editor_for_post() is true; Classic editor sites get the button from post_submitbox_misc_actions.
  • On the source post the publish box lists Add {language} translation links for missing languages; when none are missing it shows Original post -no translation action. Auto-translate runs from the translation, not the source.
  • Inconsistent relationship meta makes the family read return WP_Error; the editor then treats the post as having no source.
  • The asset handle wpr-translate-admin is registered lazily. Code that uses wp_localize_script() on it must hook at priority > 10.

Further Reading

  • Post List Table Enhancements – admin list columns, filters and the language header.
  • Translation Linking (trid system) – how variants are linked.
  • Meta Sync Across Language Variants – how wpr_translation_clone_meta_fields() applies per-key rules.

For the product-level overview, read our guide to a multi-language real estate website.

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