The WPResidence Translate post list integration is split into focused files under includes/admin/post-list-*.php. This article maps each file to a UI responsibility so you can trace behavior, override it, or extend it from a child plugin. For product context, read the user article and our multi-language real estate website guide.
File Map
| File | Responsibility |
|---|---|
post-list-hooks.php |
Registers all filters/actions for the post list screen. |
post-list-columns.php |
Registers the Language column and renders its cells. |
post-list-filters.php |
Language filter dropdown, views, and pre_get_posts filtering. |
post-list-title.php |
Translation links below the post title + sortable language ordering. |
post-list-badge.php |
Edit-screen language badge. |
post-list-actions.php |
Add Translation handler and draft cloning. |
post-list-meta.php |
Meta cloning with per-key rules. |
post-list-helpers.php |
Translation group lookups and shared helpers. |
Hook Registration
All post list hooks are registered from wpr_translation_register_post_list_hooks( $screen ), triggered on current_screen. The function only hooks when the screen is a translatable post type list.
add_filter( "manage_edit-{$post_type}_columns", 'wpr_translation_register_language_column', 0 );
add_filter( "manage_{$post_type}_posts_columns", 'wpr_translation_register_language_column', 0 );
add_filter( "manage_edit-{$post_type}_sortable_columns", 'wpr_translation_register_sortable_language_column', 10 );
add_action( 'manage_posts_custom_column', 'wpr_translation_render_language_column', 0, 2 );
add_action( 'manage_pages_custom_column', 'wpr_translation_render_language_column', 0, 2 );
add_action( "manage_{$post_type}_posts_custom_column", 'wpr_translation_render_language_column', 0, 2 );
add_filter( "views_edit-{$post_type}", 'wpr_translation_register_language_views', 20 );
add_action( 'restrict_manage_posts', 'wpr_translation_render_language_filter_control' );
add_action( 'pre_get_posts', 'wpr_translation_filter_admin_query_by_language' );
Assets are enqueued by wpr_translation_enqueue_post_list_assets() on the admin_enqueue_scripts hook.
Column Registration & Rendering
wpr_translation_register_language_column( $columns ) inserts a language key into the columns array. The cell is rendered by wpr_translation_render_language_column( $column, $post_id ), which:
- Resolves the element type via
wpr_translation_get_element_type( $post_type ). - Loads the translation group via
wpr_translation_get_translation_group( $post_id, $element_type ). - Iterates active languages from
wpr_translation_get_active_languages(). - For each language, prints either:
- An edit link to the existing translation post ID.
- A link to
admin.php?action=wpr_add_translation&post_id={id}&language={code}with a nonce fromwpr_translation_get_translation_nonce_action(). - A needs update badge when the group flags that language.
An additional helper, wpr_translation_add_post_list_column_class_aliases(), runs on admin_head-edit.php to keep CSS aliases stable across column variants.
Language Filter & Views
The dropdown above the table comes from wpr_translation_render_language_filter_control( $post_type ), registered on restrict_manage_posts. It emits a <select name="wpr_language"> with one option per active language plus an Untranslated entry.
Filtering is applied in wpr_translation_filter_admin_query_by_language( $query ) on pre_get_posts. It converts the selected language into a post__in list of element IDs pulled from {$wpdb->prefix}wpestate_translation_translations, or a post__not_in set for the untranslated view.
Quick links above the table are added via wpr_translation_register_language_views( $views ), which merges per-language counts into the WordPress views_edit-{post_type} filter.
Sortable Language Column
wpr_translation_register_sortable_language_column( $columns ) exposes the language column as sortable. The real work happens in wpr_translation_apply_language_orderby_clause( $clauses, $query ) (hooked to posts_clauses), which joins against the translations table and orders by language_code.
A separate hook, wpr_translation_set_default_language_ordering( $query ) on pre_get_posts priority 5, ensures default-language posts appear first when no explicit order is chosen.
Title Row Links
wpr_translation_build_post_title_translation_links_markup( $post_id ) builds the HTML; wpr_translation_render_post_title_links_payload( $post_id ) wraps it for the admin footer script; wpr_translation_move_title_translation_links_under_title() (on admin_footer-edit.php) injects the markup after the post title via JavaScript.
Edit-Screen Badge
wpr_translation_render_edit_screen_language_badge() is bound to both admin_head-post.php and admin_head-post-new.php. It prints a minimal inline badge above the title block so the post’s language is visible even before the block editor JS loads.
The block editor’s rich header badge is injected by wpr_translate_enqueue_post_editor_language_badge_assets() in editor-header-language.php via the localized wprTranslatePostLanguage object.
The Add Translation Action
wpr_translation_handle_add_translation_request() is wired to a custom admin.php?action=wpr_add_translation URL. It:
- Validates the nonce with
wpr_translation_get_translation_nonce_action( $post_id, $language ). - Calls
wpr_translation_create_translation_draft( $post_id, $language ). - Redirects via
wp_safe_redirect( get_edit_post_link( $new_post_id, 'raw' ) ).
wpr_translation_create_translation_draft() inserts a cloned draft (wp_insert_post), then calls wpr_translation_clone_attachments(), wpr_translation_clone_taxonomies(), wpr_translation_clone_meta_fields(), and finally wpr_translation_insert_translation_record() which writes the translation group row.
Meta Cloning Rules
wpr_translation_clone_meta_fields( $source_post_id, $new_post_id ) loads preferences from wpr_translation_get_cached_custom_field_preferences() and for each meta key uses wpr_translation_resolve_custom_field_rule() to decide:
- copy — value is duplicated verbatim into the translation.
- translate — value is placed with a translate placeholder via
wpr_translation_prepare_translate_placeholder(). - copy-once — value is copied on first clone only; later clones skip it. Tracked by
wpr_translation_get_copy_once_tracker()/wpr_translation_mark_copy_once_key(). - ignore — skipped entirely.
Shared Helpers
| Function | Purpose |
|---|---|
wpr_translation_get_active_languages() |
Returns the language array used by every post-list component. |
wpr_translation_get_element_type( $post_type ) |
Maps a WordPress post type slug to the plugin’s element_type key. |
wpr_translation_get_translation_group( $post_id, $element_type ) |
Reads {$wpdb->prefix}wpestate_translation_translations and returns trid, translations array, source language, and needs-update flags. |
wpr_translation_is_translation_post_available( $post_id ) |
Guards against trashed or missing posts before rendering edit links. |
wpr_translation_resolve_post_language_code( $post_id, $post_type ) |
Resolves a single post’s language code. |
wpr_translation_format_language_version_label( $code ) |
Formats the display label used on badges and column cells. |
wpr_translation_generate_trid() |
Allocates a new group ID with SELECT MAX(trid)+1. |
Extension Points
- Use
manage_edit-{post_type}_columnswith priority > 0 to insert your own column after Language. - Hook
wpr_translation_created(fired inwpr_translation_insert_translation_record()) to run code every time a translation is first created. - Override
wpr_translation_get_element_type()by registering your own filter before the plugin loads, or by defining a replacement in a must-use plugin. - Add your custom post type to the translatable set by registering it publicly and enabling it in the Taxonomy Translation / Custom Field Rules screens.
Gotchas
- Column filters are registered at priority 0 so they run before third-party plugins that modify the same columns. Re-register at priority 10+ to land after.
wpr_translation_set_default_language_ordering()runs at priority 5 onpre_get_posts. If you add your own ordering hook, run it after priority 5 or your order will be overwritten.- The Untranslated filter uses
post__not_in, which is expensive on large sites. Pre-filter by author/date first if you hit performance issues at 10K+ posts. - The nonce key ties a specific
post_idto a specific target language. Reusing the same link across two different source posts will fail nonce verification.
Further Reading
- Translating Posts & Pages — Developer Internals — the editor hooks that pair with these list-screen hooks.
- Translation Linking (trid system) — the schema these columns query.
- WP_Query Language Filtering — how the same translation data shapes frontend queries.
For product context, visit the multi-language real estate website page.