This article is the developer companion to the user-facing article on Elementor translation. It documents the code paths that let WPResidence Translate read Elementor layouts, build a translation payload, and write translated values back into _elementor_data. Product context lives on the multi-language real estate website page.
File Map
| File | Role |
|---|---|
| includes/elementor-widget-compat.php | Front-end integration: re-renders Elementor shortcode widgets and primes Search Form Builder term data. |
| includes/admin/elementor-translate-builder.php | Builds translation payloads from stored Elementor data. Loads the widget field map and decodes _elementor_data. |
| includes/admin/elementor-translate-text.php | Walks Elementor layouts and extracts translatable text nodes. |
| includes/admin/elementor-translate-apply.php | Resolves taxonomy/post IDs and writes translated payloads back into the translated post’s Elementor data. |
Bootstrap
Front-end hooks are registered by wpr_translate_bootstrap_elementor_widget_compat(). It is idempotent via a static flag and attaches three hooks:
add_filter( 'elementor/widget/render_content', 'wpr_translate_filter_elementor_shortcode_widget_content', 10, 2 ); add_action( 'elementor/frontend/widget/before_render', 'wpr_translate_prepare_search_form_builder_frontend_ids', 20, 1 ); add_action( 'elementor/frontend/widget/before_render', 'wpr_translate_log_agent_grids_frontend_ids', 20, 1 );
The compat layer is gated by wpr_translate_is_elementor_compat_enabled(), which reads the elementor_compatibility key from the wpr_translate_settings option and defaults to enabled.
Detecting Elementor Pages
Only pages with Elementor builder data enter the translation pipeline. wpr_translate_is_elementor_page( $post_id, $post_type ) checks two meta keys:
_elementor_data // the serialized JSON layout blob _elementor_edit_mode // Elementor's edit mode flag
If either is non-empty on a page post type, the page is considered an Elementor page.
Widget Field Map
wpr_translate_load_translatable_elementor_widget_fields() merges three JSON sources, in order of increasing priority:
- Bundled defaults at WPR_TRANSLATE_PATH . ‘assets/config/translatable_default_elementor_widgets.json’.
- Parent theme override at get_template_directory() . ‘/wpr/translatable_widget_fields.json’.
- Child theme override at get_stylesheet_directory() . ‘/wpr/translatable_widget_fields.json’.
Each widget key maps to a list of fields. Fields can be plain text (translated as strings), taxonomy references (term IDs or slugs), or post ID references.
Text Extraction
wpr_translate_extract_elementor_text_fields( $raw_elementor_data ) walks the decoded layout recursively and collects string values from known widget settings keys (title, heading, text, button_text, label). Each value is stripped of HTML via wp_strip_all_tags() and returned as an ordered, filtered list of unique text nodes for the translation editor / auto-translate providers.
Taxonomy Alias Resolution
WPResidence Elementor widgets store taxonomy references under field aliases like action_ids, category_data, county_data. Real taxonomy slugs are required to look up translated terms. wpr_translate_resolve_elementor_taxonomy_slug( $field_name, $field_value, $allow_alias_without_numeric ) maps the alias to the registered taxonomy, using an explicit alias map:
action_ids => property_action_category area_ids => property_area category_ids => property_category city_ids => property_city county_data => property_county_state features_ids => property_features status_ids => property_status
The mapping only runs when the field value contains numeric term IDs, unless $allow_alias_without_numeric is true.
ID Normalizers
| Function | Purpose |
|---|---|
| wpr_translate_normalize_elementor_ids() | Accepts string, JSON-encoded list, or array. Returns an array of absint IDs. |
| wpr_translate_normalize_elementor_slugs() | Returns a de-duplicated list of sanitized slugs. |
Apply Pipeline
wpr_translate_apply_elementor_widget_translations( &$elements, $config, $elementor_translate, $language, &$term_report ) is the workhorse. It traverses the Elementor element tree in place and for each widget listed in the field map it:
- Overwrites text fields with entries from $elementor_translate.
- Resolves taxonomy payloads via wpr_translate_resolve_elementor_taxonomy_ids() or wpr_translate_resolve_elementor_taxonomy_slugs().
- Shapes the result to match the original field via wpr_translate_match_elementor_setting_shape().
- Appends audit rows to $term_report via wpr_translate_collect_elementor_term_report_entry().
wpr_translate_apply_elementor_translations( $post_id, $translated_payload, $target_language ) is the public entry point that persists the updated payload back to _elementor_data.
Taxonomy Term Resolution
wpr_translate_resolve_elementor_taxonomy_ids( $taxonomy_entry, $language ) tries, in order:
- Strict term-ID lookup via wpr_translate_lookup_translated_term_id_strict().
- Fallback term-ID lookup via wpr_translate_lookup_translated_term_id().
- Slug lookup via wpr_translate_lookup_translated_term_id_by_slug().
- Name lookup as a last resort.
Every candidate must pass term_exists() in the target taxonomy before being accepted.
Front-end Search Form Builder Priming
wpr_translate_prepare_search_form_builder_frontend_ids() runs on elementor/frontend/widget/before_render for widgets whose get_name() is WpResidence_Search_Form_Builder. It:
- Reads the tabs_field setting and the matching taxonomy data setting (action_data, category_data, etc.).
- Normalizes term IDs, loads WP_Term instances, and builds a term_id => name map.
- Writes the map to a transient keyed by ‘wpestate_elementor_tax_’. When wpestate_set_transient_name_multilang() exists, the transient key is language-scoped to avoid cross-language cache collisions.
- The transient TTL is six hours.
Elementor Shortcode Widget
wpr_translate_filter_elementor_shortcode_widget_content() handles Elementor’s generic shortcode widget. If Elementor returned the shortcode string verbatim (i.e., the shortcode was not executed, typical in live previews), the filter calls do_shortcode() so the plugin’s pre_do_shortcode_tag translation hook in shortcode-compat.php can run.
Extension Points
- Add a widget – drop an entry keyed by the Elementor widget name into wpr/translatable_widget_fields.json in your child theme.
- Override a resolver – copy wpr_translate_resolve_elementor_taxonomy_slug() behaviour by extending the alias map in a custom filter wrapping this function, guarded by function_exists().
- Disable compat – set elementor_compatibility to false in the wpr_translate_settings option.
Gotchas
- Elementor stores layouts as JSON inside a post meta; maybe_unserialize() plus json_decode() is needed. Always round-trip through both functions when writing back.
- The apply function mutates $elements by reference. Clone the array first if you need the original.
- Non-Latin text fields must not be pushed through sanitize_title(). Use sanitize_text_field() or a custom escaper.
- Search Form Builder uses the front-end transient strictly as a display cache. It is not authoritative – the real term resolution happens through the translation tables.
Further Reading
- Shortcode & Widget ID Remapping – the shortcode-level ID swap used by the Elementor shortcode widget.
- Taxonomy Translation – the lookup helpers referenced here.
- Translation Linking (trid system) – how translated post IDs are resolved.
For the broader feature set, see our multi-language real estate website page.