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()orwpr_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_reportviawpr_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_fieldsetting and the matching taxonomy data setting (action_data,category_data, etc.). - Normalizes term IDs, loads
WP_Terminstances, and builds aterm_id => namemap. - Writes the map to a transient keyed by
'wpestate_elementor_tax_'. Whenwpestate_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.jsonin 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 byfunction_exists(). - Disable compat — set
elementor_compatibilitytofalsein thewpr_translate_settingsoption.
Gotchas
- Elementor stores layouts as JSON inside a post meta;
maybe_unserialize()plusjson_decode()is needed. Always round-trip through both functions when writing back. - The apply function mutates
$elementsby reference. Clone the array first if you need the original. - Non-Latin text fields must not be pushed through
sanitize_title(). Usesanitize_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.