Learn how to use custom PHP code and WPML filters to manually register custom Elementor widgets for translation.
There are two types of custom Elementor widgets:
- Simple widgets: These are widgets without repeater fields. You can register simple widgets using WPML’s Multilingual Tools plugin to avoid extra coding in PHP.
- Complex widgets: Widgets with more complicated structures. They may include one or more repeater fields.
To make your complex widget translatable with WPML, you can use one of two ways:
- Recommended: Using wpml-config.xml, WPML’s configuration file. The process is outlined in our tutorial on how to register page builder widgets for translation.
- Manual: Using custom PHP code. If you want to use this method, you can follow the guidelines on this page.
Adding WPML Translation Support with PHP
To demonstrate the manual process, we took one of Elementor’s widgets and extended it to include WPML support. You can find the widget on this GitHub page.
1. Add a WPML Filter To Your Widget
For every custom widget plugin, you should add the wpml_elementor_widgets_to_translate filter during their init action. WPML uses this filter to gather more information about the widget texts that need translation.
add_filter( 'wpml_elementor_widgets_to_translate', [ $this, 'wpml_widgets_to_translate_filter' ] );
This filter passes the array that lists all the widget types that need to be translated. This array includes default Elementor parts and information about what needs translating.
You should add any new widget types to this structure, as in the following example.
public function wpml_widgets_to_translate_filter( $widgets ) { $widgets[ $this->get_name() ] = [ 'conditions' => [ 'widgetType' => $this->get_name() ], 'fields' => [ [ 'field' => 'title', 'type' => __( 'Hello World Title', 'hello-world' ), 'editor_type' => 'LINE' ], ], ]; return $widgets; }
2. Create a Custom Class That Will Handle Translations
With the filter in place, create a custom class to manage your widget’s translations. Specify the class name in the integration-class field.
public function wpml_widgets_to_translate_filter( $widgets ) { $widgets[ $this->get_name() ] = [ 'conditions' => [ 'widgetType' => $this->get_name() ], 'fields' => [], 'integration-class' => 'My_Custom_Widget_WPML_Support’, ], ]; return $widgets; }
In the fields array, you can specify the following elements:
- field – The id of the field. This is the same as the id used when adding a control via the Control_stack::add_control function.
- type – The type of field. This is the text displayed in the WPML Translation Editor to help the translator know what field is to be translated.
- editor_type – This is the type of text field used in the WPML translation editor. Valid values are LINE, AREA, and VISUAL.
3. Implement the IWPML_Page_Builders_Module Interface
The integration class needs to implement the get and update functions. The get function returns the list of strings that need translation and the update function updates the Elementor data when a translation is received.
/** * Class IWPML_Page_Builders_Module */ interface IWPML_Page_Builders_Module { /** * @param string|int $node_id * @param mixed $element * @param WPML_PB_String[] $strings * * @return WPML_PB_String[] */ public function get( $node_id, $element, $strings ); /** * @param string|int $node_id * @param mixed $element * @param WPML_PB_String $string * * @return array */ public function update( $node_id, $element, WPML_PB_String $string ); }
4. Handle Multiple Occurrences Of The Same Field In a Block Using field_id
If you have multiple occurrences of the same field inside a block, use the field_id to describe a field inside an Elementor block. This allows you to differentiate occurrences of the same field in a node.
public function wpml_widgets_to_translate_filter( $widgets ) { $widgets[ $this->get_name() ] = [ 'conditions' => [ 'widgetType' => $this->get_name() ], 'fields' => [ 'title_1' => [ 'field' => 'title', 'field_id' => 'title_1', 'type' => __( 'Hello World Title', 'hello-world' ), 'editor_type' => 'LINE' ], 'title_2' => [ 'field' => 'title', 'field_id' => 'title_2', 'type' => __( 'Hello World Title', 'hello-world' ), 'editor_type' => 'LINE' ], ], ]; return $widgets; }
5. Handle Widgets With Lists of Other Elements Using The WPML_Elementor_Module_With_Items Class
Some Elementor widgets, like the Slides widget, contain lists of elements (e.g., multiple slides with headings and buttons). If your custom widget has similar lists, you can extend the WPML_Elementor_Module_With_Items class to handle translation for each item individually.
/** * Class WPML_Elementor_Slides */ class WPML_Elementor_Slides extends WPML_Elementor_Module_With_Items { /** * @return string */ public function get_items_field() { return 'slides'; } /** * @return array */ public function get_fields() { return array( 'heading', 'description', 'button_text' ); } /** * @param string $field * * @return string */ protected function get_title( $field ) { switch( $field ) { case 'heading': return esc_html__( 'Slides: heading', 'wpml-string-translation' ); case 'description': return esc_html__( 'Slides: description', 'wpml-string-translation' ); case 'button_text': return esc_html__( 'Slides: button text', 'wpml-string-translation' ); default: return ''; } } /** * @param string $field * * @return string */ protected function get_editor_type( $field ) { switch( $field ) { case 'heading': case 'button_text': return 'LINE'; case 'description': return 'VISUAL'; default: return ''; } } }
You can find more examples in the following folder of the WPML core plugin:
- ..\wp-content\plugins\sitepress-multilingual-cms\addons\wpml-page-builders\classes\Integrations\Elementor\modules\
Make sure to load the classes on the init hook. As of WPML 4.5, loading the classes before the init action hook results in a fatal error.