This article is the developer-facing companion to the post translation workflow. It documents the editor hooks, the AJAX handlers, the draft cloning path, and the meta/runtime layer that make the user-facing flow work. For product context see the user article and the multi-language real estate website landing page.
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/translation-runtime.php |
Resolves the active language for the current request. |
includes/translation-hooks.php |
Registers runtime filters (gettext, override_load_textdomain, etc.). |
includes/admin/post-list-actions.php |
Handles the Add Translation request and the draft cloning logic. |
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 editors fall back to 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 current language in this order:
wpr_translate_return_current_language_admin()— admin selector state.wpr_translation_get_default_language_code()overwpestate_translation_get_active_languages()— site default.- Legacy
wpestate_default_languageoption. - Hardcoded
'en'fallback.
It then calls wpr_translate_get_language( $code ), builds a flag URL from WPR_TRANSLATE_URL . 'assets/img/flags/4x3/{code}.svg', and hands the payload to the editor JS via:
wp_localize_script( 'wpr-translate-admin', 'wprTranslatePostLanguage', $payload );
Payload shape: label, code, flagUrl, flagAlt.
Resolving the Source Post
Auto-translate needs a source to translate from. wpr_translate_resolve_metabox_source_post_id( $post_id ) picks it in this order:
- Explicit post meta
wpr_translated_original_post_id. - Translation group row where
original = 1for the sametrid(viawpr_translation_get_translation_group()). - Group’s
source_languageif the original flag is not set.
If nothing resolves, the button renders disabled with the label Auto translation unavailable.
Auto Translate AJAX Endpoints
Two wp_ajax_* actions power the in-editor translation:
| 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 apply on publish. |
Both handlers:
- Validate the nonce
wpr_translate_auto_translate_post. - Enforce
current_user_can( 'edit_post', $post_id ). - Call
do_action( 'qm/cease' )to silence Query Monitor during heavy calls. - Look up the engine from
get_option( 'wpr_translate_auto_translation' )— validated againstopenai,google_translate,microsoft_azure,deepl. - Delegate to
wpr_translate_auto_translate_post( $post_id, $language, $engine, $source_post_id ).
What wpr_translate_auto_translate_post Touches
Downstream, the translation payload is applied in:
wpr_translate_apply_translated_post_data()—post_title,post_content,post_excerpt,post_name.wpr_translate_apply_translated_post_meta_fields()— per-meta-key based on the Custom Field Rules preferences.wpr_translate_apply_translated_taxonomies()— maps terms to translated equivalents via the translation group.wpr_translate_log_translated_elementor_data()andwpr_translate_log_translated_wpbakery_data()— structured builders.
Elementor and WPBakery fields are extracted before translation. For WPBakery, wpr_translate_extract_wpbakery_text_fields( $post_content ) walks shortcodes and pulls human-readable fields. A completed auto-translation is stamped with _wpr_translate_auto_translated_language meta on the target post to power the Redo the translation state.
Manual Translation — Draft Cloning
When an editor clicks the plus icon in the post list Language column, WordPress hits admin.php?action=wpr_add_translation&post_id=X&language=xx. That lands in wpr_translation_handle_add_translation_request(), which:
- Verifies the nonce produced by
wpr_translation_get_translation_nonce_action( $post_id, $language ). - Calls
wpr_translation_create_translation_draft( $post_id, $language ). - Redirects the user to
get_edit_post_link( $new_post_id ).
wpr_translation_create_translation_draft() clones the source using wp_insert_post(), then:
- Copies the page template via
wpr_translation_get_validated_page_template(). - Clones attachments via
wpr_translation_clone_attachments(). - Clones taxonomies via
wpr_translation_clone_taxonomies(). - Clones meta via
wpr_translation_clone_meta_fields()inpost-list-meta.php, which consultswpr_translation_get_cached_custom_field_preferences()for per-key rules. - Writes the translation group row via
wpr_translation_insert_translation_record().
Permissions & Nonces
- Add Translation action: capability
edit_poston the source post; noncewpr_add_translation_{post_id}_{language}. - Auto Translate AJAX: capability
edit_post; noncewpr_translate_auto_translate_post. - Admin menu pages all live behind
manage_options.
Localized JS Settings
wpr_translate_enqueue_post_editor_assets() enqueues assets/js/admin.js + assets/css/admin.css and localizes:
wprTranslateAutoSettings = {
enabled: bool,
engine: 'openai' | 'google_translate' | 'microsoft_azure' | 'deepl',
ajax: { url, nonce },
strings: { error, success, processing },
};
Extension Points
- Action
wpr_translation_created— fires fromwpr_translation_insert_translation_record()after a new translation row is inserted. Arguments:$post_id, $element_type, $trid, $language_code, $source_language, $post_status, $original. - Custom Field Rules — add a rule via the admin page to change whether a meta key is copied, translated, copied-once, or skipped. Rules are cached via
wpr_translation_get_cached_custom_field_preferences(). - Auto-translate providers — implemented as self-contained files in
includes/admin/auto-translate-*.php. To add a provider, replicate the pattern and register the engine key.
Gotchas
- The meta box only appears for posts where
use_block_editor_for_post()returns true. In Classic editor sites, the button is rendered frompost_submitbox_misc_actions. - The publish-box button renders an Original post — no translation action message when the current post’s language equals the default. This is by design — you translate into a language.
- Heavy auto-translate requests call
do_action( 'qm/cease' )— if you rely on Query Monitor during translation debugging, disable Query Monitor or comment out that call in a child plugin. - The asset handle
wpr-translate-adminis registered lazily. Any code that piggy-backs viawp_localize_script()must hook at priority > 10.
Further Reading
- Post List Table Enhancements — the admin list columns, filters, and sortable language header.
- Translation Linking (trid system) — the shared identifier that binds all variants together.
- Meta Sync Across Language Variants — how
wpr_translation_clone_meta_fields()applies per-key rules.
For the product-level overview of what this integration enables, read our guide to a multi-language real estate website.