This article maps the taxonomy translation subsystem in WPEstate Translate 1.0.9 (text domain wpr-translate): settings storage, how terms are linked, the duplication pipeline, the hierarchy synchronizer, term filters and the adv6 search compatibility shim. For the broader architecture behind a multi-language real estate website, see the plugin overview.
Files In Scope
| File | Role |
|---|---|
| includes/translation-taxonomies.php | Mode resolution, term duplication, created/edited term handlers, hierarchy sync, front-end term filters. |
| includes/translation-relationships-terms.php | Term family read / link / unlink / resolve (the relationship layer). |
| includes/translation-relationships-lifecycle.php | Term delete handling (pre_delete_term, delete_term). |
| includes/admin/taxonomy-translation.php | Admin columns, language filter views, add-translation request, term edit Language/Source panel. |
| includes/admin/taxonomy-metabox-filters.php | Post-editor metabox filters (get_terms_args, terms_clauses, get_terms, get_the_terms). |
| includes/admin/views/settings-taxonomy.php | Taxonomy Translation screen (posted to options.php, group wpr_translate_settings). |
| includes/adv6-terms-compat.php | Maps wp_estate_adv6_taxonomy_terms term IDs to the active language. |
Settings Storage
Everything lives under the plugin option wpr_translate_settings:
array(
'taxonomy_modes' => array( $taxonomy_slug => 'translate' | 'not_translatable' ),
'taxonomy_auto_duplicate' => array( $taxonomy_slug => bool ),
...
)
Readers:
wpr_translation_get_taxonomy_modes_settings()– unknown modes becometranslate.wpr_translation_get_taxonomy_mode( $taxonomy )– defaulttranslate.wpr_translation_is_taxonomy_translatable( $taxonomy )– main gate ('not_translatable' !== mode).wpr_translation_get_taxonomy_auto_duplicate_settings(),wpr_translation_is_taxonomy_auto_duplicate_enabled( $taxonomy ).wpr_translation_should_hide_untranslated_terms()– alwaysfalse; the old hide mode was removed.
The settings view renders one row per public taxonomy and posts hidden values for taxonomies not shown, so a temporarily hidden CPT keeps its preference.
How Terms Are Linked
Term relationships are stored as term meta and read through wpr_translate_relationship_get_term_family( $term_id, $taxonomy ), which returns canonical_id, source_language and members (language code => term ID).
| Term meta | Stored on | Value |
|---|---|---|
| wpr_term_language | Every term | Language code of the term. |
| wpr_original_term_id | Translated term | ID of the source (canonical) term. |
| wpr_translated_term_{code} | Source term | ID of the translation in that language. |
Terms without this meta fall back to wpr_translate_relationship_get_legacy_term_family(), which reads the wpestate_translation_translations table for data created by older versions. Family reads are cached per request.
Helpers:
wpr_translate_relationship_link_term( $source_id, $source_language, $member_id, $member_language, $taxonomy )– writes both pointers under a family lock.wpr_translate_relationship_unlink_term( $member_id, $taxonomy )– removes the member’s reverse pointer and the source’s forward pointer.wpr_translate_relationship_resolve_term( $term_id, $taxonomy, $language, $strict, $availability )– translated term ID for a language.wpr_translate_resolve_original_term_id( $term_id, $taxonomy ),wpr_translate_lookup_translated_term_id_strict( $canonical_id, $taxonomy, $language )– used by the front-end filter, the language switcher and adv6.
Term Duplication Pipeline
wpr_translation_duplicate_term_for_language( $source_term_id, $taxonomy, $target_language, $args ):
- Validates inputs and returns
WP_Error( 'wpr_translate_taxonomy_not_translatable' )for Not Translatable taxonomies. - Returns the existing translation if
wpr_translate_relationship_resolve_term()or the family already has one. - Resolves the translated parent for hierarchical taxonomies.
- Builds a unique slug with
wp_unique_term_slug(). - Sets
$GLOBALS['wpr_translation_creating_term_translation']to stop recursivecreated_termhandling, then callswp_insert_term(). Onterm_existsit reuses that ID. - Copies term meta with
wpr_translation_duplicate_term_meta()(new terms only). - Links the pair with
wpr_translate_relationship_link_term().
Returns array( 'term_id' => int, 'created' => bool ) or WP_Error.
Terms on a New Post Translation
When a post translation is created, wpr_translation_clone_taxonomies() (includes/admin/post-list-actions.php) assigns terms per taxonomy:
| Case | Result |
|---|---|
| Not Translatable taxonomy | The source term IDs are copied as they are. |
| Translatable, target is the default language | The canonical term is assigned. |
| Translatable, the term has a translation in the target language | The translated term is assigned (strict lookup with wpr_translate_relationship_resolve_term()). |
| Translatable, no translation, auto-duplicate on | wpr_translation_duplicate_term_for_language() creates it, then it is assigned. |
| Translatable, no translation, auto-duplicate off | The term is dropped from the new post. This is by design. |
Created / Edited / Deleted Term Hooks
| Action | Callback | What it does |
|---|---|---|
| created_term (20) | wpr_translation_handle_created_term | Stores wpr_term_language (posted wpr_translation_term_language, else the front-end language, else the default) and, when auto-duplicate is on, creates a copy in every other active language. Skips nav_menu. |
| edited_term (20) | wpr_translation_handle_edited_term | With a valid nonce (wpr_translation_term_language_nonce, action wpr_translation_term_language_{term_id}) reads wpr_translation_term_language and wpr_translation_term_source. A source links the term to that family; Source: None on a linked member unlinks it and saves its language. Then syncs hierarchy. |
| pre_delete_term (10) | wpr_translate_relationship_before_delete_term | Deleting a source term that has translations deletes those translations too. For a translated member the family is remembered. |
| delete_term (10) | wpr_translate_relationship_after_delete_term | Removes the deleted member’s wpr_translated_term_{code} pointer from the source. |
Hierarchy Sync
wpr_translation_sync_term_translation_hierarchy( $term_id, $taxonomy ) loads the term family and the parent’s family, then calls wp_update_term() on each member with the parent in its own language. $GLOBALS['wpr_translation_syncing_term_parents'] prevents re-entry through edited_term.
Front-End Term Filtering
wpr_translation_filter_terms_for_hidden_modes() hooks get_terms at priority 20. It:
- Skips admin, REST (
wpr_translate_language_router_is_rest_request()),nav_menuand Not Translatable taxonomies. - Resolves the current language via
wpr_translate_get_current_language(), falling back towpestate_get_current_language(). - Maps each term to its current-language equivalent (
wpr_translate_resolve_original_term_id()+wpr_translate_lookup_translated_term_id_strict()); the default language uses the canonical term. - Rebuilds the result for
$args['fields']–all,ids,tt_ids,names,id=>parent,id=>name,id=>slug. - Deduplicates by
taxonomy|parent|lowercase(name).
wpr_translation_force_terms_filters_args() hooks get_terms_args (20) and forces suppress_filters = false when a queried taxonomy is translatable.
Terms of a Not Translatable taxonomy are shared by all languages. Their element language is the language being browsed, so their links keep the current language prefix and the switcher, canonical and hreflang list them under every language.
Admin List Table & Edit Screen
Registered in wpr_translation_bootstrap_taxonomy_admin() on init:
add_action( 'admin_init', 'wpr_translation_setup_taxonomy_columns' ); add_action( 'admin_init', 'wpr_translation_handle_add_term_translation_request' ); add_action( 'current_screen', 'wpr_translation_register_taxonomy_language_filter_hooks' ); add_action( 'current_screen', 'wpr_translation_register_term_language_panel_hook' );
On the term.php screen the panel hook registers wpr_translation_render_term_language_panel() on {$taxonomy}_edit_form. (Older versions used edit_term_form, which core never fires, so the panel did not appear.) The language filter adds per-language views to views_{screen_id} with a wpr_term_language query var and filters the list on get_terms (30). Add-translation requests are nonced with wpr_translation_get_term_translation_nonce_action( $term_id, $taxonomy, $language ).
Post Editor Metabox Filters
includes/admin/taxonomy-metabox-filters.php registers filters on current_screen using a four-filter pattern:
get_terms_args -> high-level arg adjustments terms_clauses -> SQL JOIN/WHERE for language get_terms -> post-query list filtering get_the_terms -> per-post term list filtering
Advanced Search (adv6) Compatibility
wpr_translate_filter_adv6_terms_in_theme_options() is hooked on option_wpresidence_admin and option_wprentals_admin (priority 20). It rewrites wp_estate_adv6_taxonomy_terms so the term IDs set for the advanced search tabs point to their current-language equivalents, using the same wpr_translate_resolve_original_term_id() / wpr_translate_lookup_translated_term_id_strict() pair.
Extension Points
- Change translatable taxonomies by writing
wpr_translate_settings['taxonomy_modes']in a migration. - Turn off auto-duplication for a taxonomy by setting its
taxonomy_auto_duplicateentry to false. - Link or unlink terms in code with
wpr_translate_relationship_link_term()/wpr_translate_relationship_unlink_term()instead of writing term meta directly. - Use
suppress_filters = trueonget_terms/WP_Term_Querywhen you need the raw cross-language term list.
Gotchas
- Deleting a source term deletes all its translations. Delete a translation only when you want to remove just that language.
- Non-Latin slugs:
wp_unique_term_slug()usessanitize_title(), which may transliterate. Pre-set a slug if you need the original script. - Hierarchy sync only matters for taxonomies registered as hierarchical.
- REST requests are skipped by the term filter so term assignment UIs see the full cross-language pool.
Related Reading
- Translation Linking (trid system) – how posts and terms are linked.
- WP_Query Language Filtering – the sibling subsystem for posts.
- Database Schema – the legacy translations table.
Product context: multi-language real estate website.