ID remapping is the plugin feature that rewrites numeric references inside shortcodes and Elementor widgets so translated pages render the translated equivalents of properties, agents, developers, and taxonomy terms. This article is the implementation-level companion to the user-facing article. Product context is on our multi-language real estate website page.
File Map
| File | Role |
|---|---|
includes/shortcode-compat.php |
Intercepts shortcodes via pre_do_shortcode_tag and rewrites ID attributes. |
includes/elementor-widget-compat.php |
Front-end Elementor hooks — re-runs shortcode widgets and primes Search Form Builder term data. |
includes/admin/elementor-translate-apply.php |
Applies translated payloads back to _elementor_data, including post-ID and term-ID resolution. |
Shortcode Interception
Bootstrap via wpr_translate_bootstrap_shortcode_compat() attaches a single filter:
add_filter( 'pre_do_shortcode_tag', 'wpr_translate_filter_wpresidence_shortcodes', 10, 4 );
The callback short-circuits the normal shortcode pipeline, rewrites attributes, and invokes the registered callback manually with the translated attributes.
Shortcode Tag Allow-List
wpr_translate_is_wpresidence_shortcode_tag( $tag ) accepts:
- Tags prefixed with
wpestate_,wpresidence_, orestate_. - A fixed list:
property_page,property_page_advanced,estate_property,estate_properties.
All other shortcodes pass through untouched (the filter returns null).
Attribute Classification
wpr_translate_identify_shortcode_attribute_type( $attribute ) classifies each attribute as single, list, or empty string (skip). Explicit name lists:
single: id, post_id, postid, property_id, propertyid,
agent_id, agentid, developer_id, developerid
list: ids, post_ids, postids, property_ids, propertyids,
agent_ids, agentids, developer_ids, developerids
A regex fallback matches trailing *id / *ids patterns for keys like estateid, listingids, itemid.
Single ID Conversion
wpr_translate_convert_shortcode_single_id( $value ) returns null for non-numeric input (so the attribute is left unchanged) and otherwise delegates to wpr_translate_resolve_post_for_current_language(). A translated ID is returned as a string only when it differs from the original.
List ID Conversion
wpr_translate_convert_shortcode_id_list( $value ) preserves the original delimiter. Detection order:
|(pipe);(semicolon)(space),(comma, default)
Arrays are also accepted; they are rejoined with a comma and returned as a string. Non-numeric entries are preserved in place.
Translation Resolution
wpr_translate_resolve_post_for_current_language( $post_id ) looks up the current language code via wpr_translate_get_current_language() and calls:
wpr_translate_resolve_post_for_language( $post_id, $post_type, $language )
The resolver requires two helpers to be available:
wpr_translation_get_element_type( $post_type )— maps a post type (estate_property,estate_agent, etc.) to an element type.wpr_translation_get_translation_group( $post_id, $element_type )— returns the group payload withsource_languageandtranslationskeyed by language code.
Fallback order in wpr_translate_resolve_post_for_language():
- Exact language match in the translations array.
- Source-language translation.
- First translation returned by
wpr_translation_is_translation_post_available(). - Original post ID.
Recursion Guard
wpr_translate_filter_wpresidence_shortcodes() uses a static $processing array keyed by tag to prevent unbounded recursion when a shortcode callback re-invokes do_shortcode() internally. The flag is cleared after the outer call returns.
Elementor Shortcode Widget Bridge
Elementor’s built-in shortcode widget stores a literal shortcode string. If Elementor rendered the widget in preview mode it may not have executed the shortcode at all. wpr_translate_filter_elementor_shortcode_widget_content() detects this by comparing the rendered content with the raw shortcode string (after trim()). When they match, it calls do_shortcode() so the pre_do_shortcode_tag filter can run.
Elementor Widget ID Remapping
Widgets that carry ID arrays are handled by the apply pipeline in elementor-translate-apply.php. Post-type widgets (Agent Grids, Property lists) are walked via wpr_translate_apply_elementor_widget_translations(). Each target post ID is resolved via the same translation group helpers used for shortcodes.
Search Form Builder Term Priming
wpr_translate_prepare_search_form_builder_frontend_ids() runs on elementor/frontend/widget/before_render:
- Widget gate:
$widget->get_name() === 'WpResidence_Search_Form_Builder'. - Reads the
tabs_fieldsetting. Falls back to scanning for a non-empty*_datasetting iftabs_fieldis missing. - Normalizes term IDs via
wpr_translate_normalize_agent_grids_ids_for_log(). - Builds a map of
term_id => sanitized nameand writes it to a transient keyed by'wpestate_elementor_tax_', language-scoped viawpestate_set_transient_name_multilang()when available. - TTL: six hours.
Agent Grids Front-End Hook
wpr_translate_log_agent_grids_frontend_ids() targets widgets named Wpresidence_Agent_Grids. It reads grid_taxonomy, normalizes the ID list, and (under WP_DEBUG_LOG) records the resolved agent posts — used for debugging why a grid is empty in a given language.
Settings Gate
wpr_translate_is_elementor_compat_enabled() reads wpr_translate_settings['elementor_compatibility']. When set to false, both the shortcode filter and the Elementor widget hooks become no-ops.
Extension Points
- Custom attribute types — extend
wpr_translate_identify_shortcode_attribute_type()via a child theme override guarded byfunction_exists()if you need to recognize additional ID keys. - Custom post types — make sure
wpr_translation_get_element_type()handles your CPT; otherwise the resolver returns the original ID. - Delimiter support —
wpr_translate_convert_shortcode_id_list()handles comma, pipe, semicolon, and space; extend there for exotic delimiters.
Gotchas
- The
pre_do_shortcode_tagfilter returnsnullto let WordPress run the shortcode normally, and the rewritten output (or the original if unchanged) otherwise. Do not short-circuit with an empty string — it suppresses the shortcode. - Non-numeric entries in ID lists are preserved verbatim. Do not assume every comma-separated item is a numeric ID.
- The Search Form Builder transient is a display hint, not the authoritative term map. Actual query filtering still goes through the translation taxonomy tables.
- Element IDs remain numeric strings in shortcode output to match WordPress’s native shortcode attribute shape — do not cast to
intwhen writing back.
Further Reading
- Elementor Widget Translation — the widget-level translation pipeline that depends on this remapping layer.
- Translation Linking (trid system) — how translations are glued together.
- Taxonomy Translation — term-level translation lookups used by the resolvers.
Product-level overview lives on the multi-language real estate website page.