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 from wpr_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}_columns with priority > 0 to insert your own column after Language.
- Hook wpr_translation_created (fired in wpr_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 on pre_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_id to 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.