This article maps the taxonomy translation subsystem in the wpresidence-translate plugin (text domain wpr-translate). It covers the settings storage, the term duplication pipeline, the hierarchy synchronizer, admin list-table filters, and the adv6 search compatibility shim. For the broader plugin architecture underpinning a multi-language real estate website, see the plugin overview.
Files In Scope
| File | Role |
|---|---|
includes/translation-taxonomies.php |
Runtime: mode resolution, term duplication, hierarchy sync, term-list filters. |
includes/admin/taxonomy-translation.php |
Admin UI helpers: columns, language filter views, add-translation request handler, edit-term 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 |
Settings screen view (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 single 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()wpr_translation_get_taxonomy_mode( $taxonomy )— defaulttranslate.wpr_translation_is_taxonomy_translatable( $taxonomy )— main gate used everywhere.wpr_translation_get_taxonomy_auto_duplicate_settings()wpr_translation_is_taxonomy_auto_duplicate_enabled( $taxonomy )
The settings view at includes/admin/views/settings-taxonomy.php renders a row per public taxonomy and preserves hidden values for taxonomies not in the current form (so a temporarily-hidden CPT does not lose its preference).
Element Type & Translation Groups
Each taxonomy maps to a translation element type via wpr_translation_get_taxonomy_element_type( $taxonomy ). Translation groups are stored in {$wpdb->prefix}wpestate_translation_translations with columns including element_id, element_type, trid, language_code, source_language_code, original, and needs_update.
Lookup helpers (defined elsewhere in the plugin but called here):
wpr_translation_get_translation_group( $term_id, $element_type )— returns{ trid, translations[code => id], source_language }.wpr_translate_resolve_original_term_id( $term_id, $taxonomy )— canonical anchor via term meta.wpr_translate_lookup_translated_term_id_strict( $canonical_id, $taxonomy, $language )— strict mapping with no fallback.wpr_translate_link_term_translation_meta( $source, $target, $language, $taxonomy )— writes meta linkage (wpr_original_term_idand language code).
Term Duplication Pipeline
wpr_translation_duplicate_term_for_language( $source_term_id, $taxonomy, $target_language, $args ) is the entry point. It:
- Validates inputs and rejects non-translatable taxonomies.
- Resolves the canonical source via
wpr_translate_resolve_original_term_id(). - Short-circuits if a translation already exists (meta-first lookup, then translation group).
- Resolves the translated parent term id for hierarchical taxonomies.
- Generates a unique slug with
wp_unique_term_slug(). - Sets the global guard
$GLOBALS['wpr_translation_creating_term_translation']to suppress recursivecreated_termhandling. - Calls
wp_insert_term(). Onterm_existserror it reuses the existing ID. - Copies term meta via
wpr_translation_duplicate_term_meta()and the legacytaxonomy_{term_id}option (resettingcategory_tagline). - Links meta and writes a row to the translation table via
wpr_translation_insert_translation_record().
Created / Edited / Deleted Term Hooks
| Action | Callback | What it does |
|---|---|---|
created_term (pri 20) |
wpr_translation_handle_created_term |
Generates a new trid, inserts the source record, and fans out duplication to all other languages when auto-duplicate is enabled. |
edited_term (pri 20) |
wpr_translation_handle_edited_term |
Reads wpr_translation_term_language and wpr_translation_term_source from POST (with nonce wpr_translation_term_language_{term_id}), updates the group, and syncs hierarchy. |
delete_term (pri 20) |
wpr_translation_handle_deleted_term |
Removes the translation row; if the deleted term was the original, promotes another translation to original via translation_id update. |
Hierarchy Sync
wpr_translation_sync_term_translation_hierarchy( $term_id, $taxonomy ) loads the parent’s translation group, finds the per-language translated parent ID, and calls wp_update_term() on each sibling with the correct parent. The recursion guard $GLOBALS['wpr_translation_syncing_term_parents'] prevents re-entry through edited_term while the sync is running.
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()), and non-translatable taxonomies. - Resolves the current language via
wpr_translate_get_current_language(), falls back towpestate_get_current_language(). - For each term: resolves canonical id, then looks up the language-specific id (
wpr_translate_lookup_translated_term_id_strict). For the default language, canonical is always used. - For modes where untranslated terms should not be hidden (
wpr_translation_should_hide_untranslated_terms()), falls back to canonical so output does not disappear. - Rebuilds the result respecting
$args['fields']— supportsall,ids,tt_ids,names,id=>parent,id=>name,id=>slug. - Deduplicates by
taxonomy|parent|lowercase(name)to avoid duplicate labels.
wpr_translation_force_terms_filters_args() hooks get_terms_args and forces suppress_filters = false when any queried taxonomy is translatable, so the filter above can actually run.
Admin List Table & Edit Screen
Registered in wpr_translation_bootstrap_taxonomy_admin():
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( 'edit_term_form', 'wpr_translation_render_term_language_panel', 10, 2 );
The language filter injects per-language views into views_{screen_id} with a wpr_term_language query var. The add-translation handler nonces requests using 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 and uses the four-filter pattern documented at the top of the file:
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() in adv6-terms-compat.php hooks the WPResidence theme options and rewrites wp_estate_adv6_taxonomy_terms so that term IDs configured by admins for the advanced search tab are mapped to their current-language equivalents. This keeps “For Sale / For Rent” and similar action filters pointing at the right terms in every language, using the same wpr_translate_resolve_original_term_id and wpr_translate_lookup_translated_term_id_strict pair as the main filter.
Extension Points
- Add or remove translatable taxonomies by writing directly to
wpr_translate_settings['taxonomy_modes']in a migration. - Skip a specific taxonomy from auto-duplication by setting its
taxonomy_auto_duplicateentry to false. - Use
suppress_filters = trueonget_terms/WP_Term_Querywhen you explicitly need the raw cross-language term list (e.g. admin exports). - Override
wpr_translate_lookup_translated_term_id_strictvia a plugin if you need a custom resolver — it is called everywhere throughfunction_exists()checks.
Gotchas
- The
delete_termhandler promotes a replacement only when the deleted term was the group’s original — non-original deletions simply drop the mapping row. - Non-Latin slugs:
wp_unique_term_slug()usessanitize_title(), which transliterates. If you need to preserve the original script, pre-set a slug before callingwp_insert_term(). - The hierarchy sync only runs for taxonomies marked
hierarchicalinregister_taxonomy(). - REST requests are intentionally skipped by the term filter — term assignment UIs must see the full cross-language term pool.
Related Reading
- Translation Linking (trid system) — the shared TRID mechanism used for both posts and terms.
- WP_Query Language Filtering — the sibling subsystem for posts.
- Database Schema — structure of the translations table.
Product context: multi-language real estate website.