Translating additional Elementor widgets
Gato AI Translations for Polylang can translate widget-based Elementor pages.
The plugin ships with support for all Elementor and Elementor PRO widgets, and provides the ability for users to support additional widgets.
Supporting additional Elementor widgets
You can translate custom widgets from your application, or widgets from 3rd-party plugins.
Please read guide Extending Page Builder elements to translate first.
The documentation below is a continuation of that process, applied to Elementor widgets.
For instance, let's say we have a "My Call to Action" widget with the following JSON structure (visible via field elementorFlattenedDataItems
, as explained in the next section):
{
"id": "206a911",
"elType": "widget",
"settings": {
"title": "This is the heading",
"description": "Your Call to Action content comes here",
"button": "Click Here"
},
"elements": [],
"widgetType": "my-call-to-action"
}
Let's see how to add support for translating this widget.
Identify the widget properties to translate
The plugin translates widgets by extracting all the widget properties from within the _elementor_data
JSON, translating those, injecting the translated properties back into the JSON, and then saving the JSON back to the post meta.
In order to extract the properties from a widget, the plugin needs to know which properties are translatable.
Execute the Translate custom posts GraphQL query, and browse all the widgets and their properties in the GraphQL response, under key elementorFlattenedDataItems
.

Explore that entry to identify the widget properties that must be translated, and based on its structure, replicate the corresponding GraphQL query logic from the examples below.
Widget structure patterns
There are 3 common patterns for storing widget data in the _elementor_data
JSON:
- Properties only
- Arrays of elements only
- Properties + arrays of elements
1. Properties only: Animated Headline
The animated-headline
widget stores properties only (before_text
, highlighted_text
, rotating_text
, and after_text
) in its JSON structure:
{
"id": "6dac79c",
"elType": "widget",
"settings": {
"before_text": "This page is",
"highlighted_text": "Amazing",
"rotating_text": "Better\nBigger\nFaster",
"after_text": "And super brilliant"
},
"elements": [],
"widgetType": "animated-headline"
}
This is the GraphQL query implementation:
The GraphQL query in this doc may be outdated compared to the latest version of the plugin.
Please copy the code from the Translate custom posts GraphQL query instead.
query InitializeVariablesForElementor(
$customPostIds: [ID!]!
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "CheckPageBuilderToUserForCustomPostType")
@include(if: $hasCustomPosts)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
initCustomPostsForElementor: customPosts(filter: { ids: $customPostIds, customPostTypes: [$customPostType], status: any }, pagination: { limit: -1 }) {
emptyArray: _echo(value: [])
@export(
as: "originElementorAnimatedHeadlineIDs"
type: DICTIONARY
)
@export(
as: "originElementorAnimatedHeadlineBeforeTextItems"
type: DICTIONARY
)
@export(
as: "originElementorAnimatedHeadlineHighlightedTextItems"
type: DICTIONARY
)
@export(
as: "originElementorAnimatedHeadlineRotatingTextItems"
type: DICTIONARY
)
@export(
as: "originElementorAnimatedHeadlineAfterTextItems"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-1)
####################################################
}
}
# ...
query ExportOriginCustomPostDataForElementor(
$customPostIds: [ID!]!
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "ExportOriginCustomPostData")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
originCustomPostsForElementor: customPosts(filter: { ids: $customPostIds, customPostTypes: [$customPostType], status: any }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
elementorFlattenedDataItems
@underEachArrayItem(
passValueOnwardsAs: "elementJSON"
affectDirectivesUnderPos: [1, 2, 3]
)
@applyField(
name: "_objectProperty",
arguments: {
object: $elementJSON,
by: { key: "widgetType" }
failIfNonExistingKeyOrPath: false,
},
passOnwardsAs: "widgetType"
)
@applyField(
name: "_equals",
arguments: {
value1: $widgetType,
value2: "animated-headline"
},
passOnwardsAs: "isMatch"
)
@if(
condition: $isMatch
affectDirectivesUnderPos: [1, 3, 5, 7, 9]
)
@underJSONObjectProperty(
by: { key: "id" }
)
@export(
as: "originElementorAnimatedHeadlineIDs"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.before_text" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorAnimatedHeadlineBeforeTextItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.highlighted_text" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorAnimatedHeadlineHighlightedTextItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.rotating_text" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorAnimatedHeadlineRotatingTextItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.after_text" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorAnimatedHeadlineAfterTextItems"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-3)
####################################################
}
}
}
# ...
query InitializeTranslationVariablesForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft,
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "FixExportOriginCustomPostDataForElementor")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useGutenbergEditor)
{
emptyTranslationCustomPostVarsForElementor: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
id
emptyArray: _echo(value: [])
@export(
as: "destinationElementorAnimatedHeadlineIDs"
type: DICTIONARY
)
@export(
as: "destinationElementorAnimatedHeadlineBeforeTextItems"
type: DICTIONARY
)
@export(
as: "destinationElementorAnimatedHeadlineHighlightedTextItems"
type: DICTIONARY
)
@export(
as: "destinationElementorAnimatedHeadlineRotatingTextItems"
type: DICTIONARY
)
@export(
as: "destinationElementorAnimatedHeadlineAfterTextItems"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-5)
####################################################
@remove
}
}
}
# ...
query FetchDataForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "FetchData")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
translationCustomPostsForElementor: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
originElementorAnimatedHeadlineIDs: _objectProperty(
object: $originElementorAnimatedHeadlineIDs
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorAnimatedHeadlineIDs"
type: DICTIONARY
)
@remove
originElementorAnimatedHeadlineBeforeTextItems: _objectProperty(
object: $originElementorAnimatedHeadlineBeforeTextItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorAnimatedHeadlineBeforeTextItems"
type: DICTIONARY
)
@remove
originElementorAnimatedHeadlineHighlightedTextItems: _objectProperty(
object: $originElementorAnimatedHeadlineHighlightedTextItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorAnimatedHeadlineHighlightedTextItems"
type: DICTIONARY
)
@remove
originElementorAnimatedHeadlineRotatingTextItems: _objectProperty(
object: $originElementorAnimatedHeadlineRotatingTextItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorAnimatedHeadlineRotatingTextItems"
type: DICTIONARY
)
@remove
originElementorAnimatedHeadlineAfterTextItems: _objectProperty(
object: $originElementorAnimatedHeadlineAfterTextItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorAnimatedHeadlineAfterTextItems"
type: DICTIONARY
)
@remove
####################################################
##### Insert code for Elementor widgets (e-6)
####################################################
}
}
}
# ...
query ExportTransformationDataSourceForElementor
@depends(on: "ExportTransformationDataSourceForClassicEditor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
allTransformationSourcesElementor: _objectMerge(
objects: [
$transformationSources,
{
elementorWidgets: {
animatedHeadlineBeforeText: {
to: $destinationElementorAnimatedHeadlineBeforeTextItems,
},
animatedHeadlineHighlightedText: {
to: $destinationElementorAnimatedHeadlineHighlightedTextItems,
},
animatedHeadlineRotatingText: {
to: $destinationElementorAnimatedHeadlineRotatingTextItems,
},
animatedHeadlineAfterText: {
to: $destinationElementorAnimatedHeadlineAfterTextItems,
},
####################################################
##### Insert code for Elementor widgets (e-7)
####################################################
},
}
]
)
@export(as: "transformationSources")
}
# ...
query PrepareReplacementsForElementor
@depends(on: "PrepareReplacementsForClassicEditor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
transformedElementorAnimatedHeadline: _echo(value: $destinationElementorAnimatedHeadlineIDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.animatedHeadlineBeforeText.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorAnimatedHeadlineBeforeTextItems"
)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.animatedHeadlineHighlightedText.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorAnimatedHeadlineHighlightedTextItems"
)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.animatedHeadlineRotatingText.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorAnimatedHeadlineRotatingTextItems"
)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.animatedHeadlineAfterText.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorAnimatedHeadlineAfterTextItems"
)
####################################################
##### Insert code for Elementor widgets (e-8)
####################################################
}
# ...
query PrepareTranslationInputsForElementor
@depends(on: "TranslateCustomPosts")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
transformedElementorAnimatedHeadlineEntries: _echo(value: $destinationElementorAnimatedHeadlineIDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
)
@underEachArrayItem(
passIndexOnwardsAs: "key"
passValueOnwardsAs: "elementID"
affectDirectivesUnderPos: [1, 2, 3, 4, 5, 6]
)
@applyField(
name: "_sprintf",
arguments: {
string: "%s.%s",
values: [$customPostID, $key]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorAnimatedHeadlineBeforeTextItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "before_text"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorAnimatedHeadlineHighlightedTextItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "highlighted_text"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorAnimatedHeadlineRotatingTextItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "rotating_text"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorAnimatedHeadlineAfterTextItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "after_text"
)
@applyField(
name: "_echo",
arguments: {
value: {
id: $elementID,
settings: {
before_text: $before_text,
highlighted_text: $highlighted_text,
rotating_text: $rotating_text,
after_text: $after_text,
}
}
},
setResultInResponse: true
)
@export(as: "transformedElementorAnimatedHeadlineEntries")
####################################################
##### Insert code for Elementor widgets (e-10)
####################################################
}
query GenerateTranslationInputsForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft
$customPostType: CustomPostEnumString!
)
@depends(on: "PrepareTranslationInputsForElementor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
elementorGenerateMutationInputs: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
transformedElementorAnimatedHeadlineEntries: _objectProperty(
object: $transformedElementorAnimatedHeadlineEntries,
by: {
key: $__id
}
)
####################################################
##### Insert code for Elementor widgets (e-11)
####################################################
elements: _arrayMerge(arrays: [
$__transformedElementorAnimatedHeadlineEntries,
####################################################
##### Insert code for Elementor widgets (e-12)
####################################################
])
}
}
}
2. Arrays of elements only: Price List
The price-list
widget stores arrays of elements only (properties title
and item_description
under entry price_list
) in its JSON structure:
{
"id": "1fc4fc6",
"elType": "widget",
"settings": {
"price_list": [
{
"title": "First item on the list",
"item_description": "Enter the description for the first item on the list",
"price": "$20",
"link": {
"url": "#"
},
"_id": "764b2a8"
},
{
"title": "Second item on the list",
"item_description": "Enter the description for the second item on the list",
"price": "$9",
"link": {
"url": "#"
},
"_id": "80ec42a"
},
{
"title": "Third item on the list",
"item_description": "Enter the description for the third item on the list",
"price": "$32",
"link": {
"url": "#"
},
"_id": "d10acc9"
}
]
},
"elements": [],
"widgetType": "price-list"
}
This is the GraphQL query implementation:
query InitializeVariablesForElementor(
$customPostIds: [ID!]!
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "CheckPageBuilderToUserForCustomPostType")
@include(if: $hasCustomPosts)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
initCustomPostsForElementor: customPosts(filter: { ids: $customPostIds, customPostTypes: [$customPostType], status: any }, pagination: { limit: -1 }) {
emptyArray: _echo(value: [])
@export(
as: "originElementorPriceListIDs"
type: DICTIONARY
)
@export(
as: "originElementorPriceListListTitleItems"
type: DICTIONARY
)
@export(
as: "originElementorPriceListListDescriptionItems"
type: DICTIONARY
)
@export(
as: "originElementorPriceListListIDs"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-1)
####################################################
emptyObject: _echo(value: {})
@export(
as: "originElementorPriceListListElementProps"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-2)
####################################################
}
}
# ...
query ExportOriginCustomPostDataForElementor(
$customPostIds: [ID!]!
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "ExportOriginCustomPostData")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
originCustomPostsForElementor: customPosts(filter: { ids: $customPostIds, customPostTypes: [$customPostType], status: any }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
elementorFlattenedDataItems
@underEachArrayItem(
passValueOnwardsAs: "elementJSON"
affectDirectivesUnderPos: [1, 2, 3]
)
@applyField(
name: "_objectProperty",
arguments: {
object: $elementJSON,
by: { key: "widgetType" }
failIfNonExistingKeyOrPath: false,
},
passOnwardsAs: "widgetType"
)
@applyField(
name: "_equals",
arguments: {
value1: $widgetType,
value2: "price-list"
},
passOnwardsAs: "isMatch"
)
@if(
condition: $isMatch
affectDirectivesUnderPos: [1, 3]
)
@underJSONObjectProperty(
by: { key: "id" }
)
@export(
as: "originElementorPriceListIDs"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.price_list" }
failIfNonExistingKeyOrPath: false
affectDirectivesUnderPos: [1, 2, 3, 10, 11, 12, 13]
)
@passOnwards(as: "list")
@applyField(
name: "_objectProperty",
arguments: {
object: $elementJSON,
by: { key: "id" }
}
passOnwardsAs: "elementID"
)
@underEachArrayItem(
affectDirectivesUnderPos: [1, 3, 5, 6]
)
@underJSONObjectProperty(
by: { key: "title" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorPriceListListTitleItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { key: "item_description" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorPriceListListDescriptionItems"
type: DICTIONARY
)
@applyField(
name: "_echo",
arguments: {
value: $elementID
}
setResultInResponse: true
)
@export(
as: "originElementorPriceListListIDs"
type: DICTIONARY
)
@applyField(
name: "_arrayLength",
arguments: {
array: $list,
}
passOnwardsAs: "arrayLength"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $originElementorPriceListListElementProps,
by: { key: $customPostID }
}
passOnwardsAs: "props"
)
@applyField(
name: "_objectAddEntry",
arguments: {
object: $props,
key: $elementID,
value: {
length: $arrayLength,
}
}
setResultInResponse: true
)
@export(
as: "originElementorPriceListListElementProps"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-3)
####################################################
}
}
}
query FixExportOriginCustomPostDataForElementor
@depends(on: "ExportOriginCustomPostDataForElementor")
@include(if: $hasCustomPosts)
@include(if: $useElementorPageBuilder)
@include(if: $executeTranslation)
{
originElementorPriceListListElementProps: _echo(value: $originElementorPriceListListElementProps)
@underEachJSONObjectProperty(
passValueOnwardsAs: "list"
)
@applyField(
name: "_objectMerge",
arguments: {
objects: $list,
},
setResultInResponse: true
)
@export(
as: "originElementorPriceListListElementProps"
)
####################################################
##### Insert code for Elementor widgets (e-4)
####################################################
}
# ...
query InitializeTranslationVariablesForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft,
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "FixExportOriginCustomPostDataForElementor")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useGutenbergEditor)
{
emptyTranslationCustomPostVarsForElementor: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
id
emptyArray: _echo(value: [])
@export(
as: "destinationElementorPriceListIDs"
type: DICTIONARY
)
@export(
as: "destinationElementorPriceListListTitleItems"
type: DICTIONARY
)
@export(
as: "destinationElementorPriceListListDescriptionItems"
type: DICTIONARY
)
@export(
as: "destinationElementorPriceListListElementProps"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-5)
####################################################
@remove
}
}
}
# ...
query FetchDataForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "FetchData")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
translationCustomPostsForElementor: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
originElementorPriceListIDs: _objectProperty(
object: $originElementorPriceListIDs
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorPriceListIDs"
type: DICTIONARY
)
@remove
originElementorPriceListListIDs: _objectProperty(
object: $originElementorPriceListListIDs
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorPriceListListIDs"
type: DICTIONARY
)
@remove
originElementorPriceListListTitleItems: _objectProperty(
object: $originElementorPriceListListTitleItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorPriceListListTitleItems"
type: DICTIONARY
)
@remove
originElementorPriceListListDescriptionItems: _objectProperty(
object: $originElementorPriceListListDescriptionItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorPriceListListDescriptionItems"
type: DICTIONARY
)
@remove
originElementorPriceListListElementProps: _objectProperty(
object: $originElementorPriceListListElementProps
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorPriceListListElementProps"
type: DICTIONARY
)
@remove
####################################################
##### Insert code for Elementor widgets (e-6)
####################################################
}
}
}
# ...
query ExportTransformationDataSourceForElementor
@depends(on: "ExportTransformationDataSourceForClassicEditor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
allTransformationSourcesElementor: _objectMerge(
objects: [
$transformationSources,
{
elementorWidgets: {
priceListListTitle: {
to: $destinationElementorPriceListListTitleItems,
},
priceListListDescription: {
to: $destinationElementorPriceListListDescriptionItems,
},
####################################################
##### Insert code for Elementor widgets (e-7)
####################################################
},
}
]
)
@export(as: "transformationSources")
}
# ...
query PrepareReplacementsForElementor
@depends(on: "PrepareReplacementsForClassicEditor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
transformedElementorPriceList: _echo(value: $destinationElementorPriceListIDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.priceListListTitle.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorPriceListListTitleItems"
)
####################################################
##### Insert code for Elementor widgets (e-8)
####################################################
}
query AdaptReplacementsForElementor
@depends(on: "PrepareReplacementsForElementor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
adaptedElementorPriceListListItems: _echo(value: $destinationElementorPriceListListIDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
)
@underEachArrayItem(
passIndexOnwardsAs: "key"
affectDirectivesUnderPos: [1, 2, 3, 4]
)
@applyField(
name: "_sprintf",
arguments: {
string: "%s.%s",
values: [$customPostID, $key]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorPriceListListTitleItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "title"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorPriceListListDescriptionItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "item_description"
)
@applyField(
name: "_echo",
arguments: {
value: {
title: $title,
item_description: $item_description,
}
}
setResultInResponse: true
)
@export(
as: "transformedElementorPriceListListItems"
)
####################################################
##### Insert code for Elementor widgets (e-9)
####################################################
}
# ...
query PrepareTranslationInputsForElementor
@depends(on: "TranslateCustomPosts")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
transformedElementorPriceListEntries: _echo(value: $destinationElementorPriceListIDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
)
@underEachArrayItem(
passIndexOnwardsAs: "key"
passValueOnwardsAs: "elementID"
affectDirectivesUnderPos: [1, 2, 3, 4, 5, 6, 7, 8, 9]
)
@applyField(
name: "_sprintf",
arguments: {
string: "%s.%s",
values: [$customPostID, $key]
}
passOnwardsAs: "path"
)
@applyField(
name: "_sprintf",
arguments: {
string: "%s.%s",
values: [$customPostID, $elementID]
}
passOnwardsAs: "idPath"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $destinationElementorPriceListListIDs
by: { key: $customPostID }
}
passOnwardsAs: "priceListIDs"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorPriceListListItems
by: { key: $customPostID }
}
passOnwardsAs: "priceListItems"
)
@applyField(
name: "_arraySearch",
arguments: {
array: $priceListIDs
element: $elementID
}
passOnwardsAs: "offset"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $destinationElementorPriceListListElementProps
by: { path: $idPath }
}
passOnwardsAs: "priceListItemProps"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $priceListItemProps
by: { key: "length" }
}
passOnwardsAs: "length"
)
@applyField(
name: "_arraySlice",
arguments: {
array: $priceListItems
length: $length,
offset: $offset
}
passOnwardsAs: "price_list"
)
@applyField(
name: "_echo",
arguments: {
value: {
id: $elementID,
settings: {
price_list: $price_list,
}
}
},
setResultInResponse: true
)
@export(as: "transformedElementorPriceListEntries")
####################################################
##### Insert code for Elementor widgets (e-10)
####################################################
}
query GenerateTranslationInputsForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft
$customPostType: CustomPostEnumString!
)
@depends(on: "PrepareTranslationInputsForElementor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
elementorGenerateMutationInputs: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
id
originCustomPostId: _objectProperty(
object: $translationCustomPostIdOringCustomPostIDs
by: { key: $__id }
)
transformedElementorPriceListEntries: _objectProperty(
object: $transformedElementorPriceListEntries,
by: {
key: $__id
}
)
####################################################
##### Insert code for Elementor widgets (e-11)
####################################################
elements: _arrayMerge(arrays: [
$__transformedElementorPriceListEntries,
####################################################
##### Insert code for Elementor widgets (e-12)
####################################################
])
}
}
}
3. Properties + Arrays of elements: Link in Bio
The link-in-bio
widget (and also its variations link-in-bio-var-2
til link-in-bio-var-5
) stores both properties (bio_heading
, bio_title
, and bio_description
) and arrays of elements (property cta_link_text
under cta_link
) in its JSON structure:
{
"id": "fe5104f",
"elType": "widget",
"settings": {
"bio_heading": "Sara Parker",
"bio_title": "Kitchen Chronicles",
"bio_description": "Join me on my journey to a healthier lifestyle",
"icon": [
{
"_id": "3ef8485"
},
{
"icon_platform": "Instagram",
"_id": "c1ad656"
},
{
"icon_platform": "TikTok",
"_id": "563c5b0"
}
],
"cta_link": [
{
"cta_link_text": "Get Healthy",
"_id": "d84226c"
},
{
"cta_link_text": "Top 10 Recipes",
"_id": "8c7f165"
},
{
"cta_link_text": "Meal Prep",
"_id": "17a129f"
},
{
"cta_link_text": "Healthy Living Resources",
"_id": "9c1b615"
}
]
},
"elements": [],
"widgetType": "link-in-bio"
}
This is the GraphQL query implementation:
query InitializeVariablesForElementor(
$customPostIds: [ID!]!
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "CheckPageBuilderToUserForCustomPostType")
@include(if: $hasCustomPosts)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
initCustomPostsForElementor: customPosts(filter: { ids: $customPostIds, customPostTypes: [$customPostType], status: any }, pagination: { limit: -1 }) {
emptyArray: _echo(value: [])
@export(
as: "originElementorLinkInBio1To5IDs"
type: DICTIONARY
)
@export(
as: "originElementorLinkInBio1To5HeadingItems"
type: DICTIONARY
)
@export(
as: "originElementorLinkInBio1To5TitleItems"
type: DICTIONARY
)
@export(
as: "originElementorLinkInBio1To5DescriptionItems"
type: DICTIONARY
)
@export(
as: "originElementorLinkInBio1To5CtaLinkListItemTexts"
type: DICTIONARY
)
@export(
as: "originElementorLinkInBio1To5CtaLinkListIDs"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-1)
####################################################
emptyObject: _echo(value: {})
@export(
as: "originElementorLinkInBio1To5CtaLinkListElementProps"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-2)
####################################################
}
}
# ...
query ExportOriginCustomPostDataForElementor(
$customPostIds: [ID!]!
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "ExportOriginCustomPostData")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
originCustomPostsForElementor: customPosts(filter: { ids: $customPostIds, customPostTypes: [$customPostType], status: any }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
elementorFlattenedDataItems
@underEachArrayItem(
passValueOnwardsAs: "elementJSON"
affectDirectivesUnderPos: [1, 2, 3]
)
@applyField(
name: "_objectProperty",
arguments: {
object: $elementJSON,
by: { key: "widgetType" }
failIfNonExistingKeyOrPath: false,
},
passOnwardsAs: "widgetType"
)
@applyField(
name: "_inArray",
arguments: {
value: $widgetType,
array: [
"link-in-bio",
"link-in-bio-var-2",
"link-in-bio-var-3",
"link-in-bio-var-4",
"link-in-bio-var-5",
]
},
passOnwardsAs: "isMatch"
)
@if(
condition: $isMatch
affectDirectivesUnderPos: [1, 3, 5, 7, 9]
)
@underJSONObjectProperty(
by: { key: "id" }
)
@export(
as: "originElementorLinkInBio1To5IDs"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.bio_heading" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorLinkInBio1To5HeadingItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.bio_title" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorLinkInBio1To5TitleItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.bio_description" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorLinkInBio1To5DescriptionItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.cta_link" }
failIfNonExistingKeyOrPath: false
affectDirectivesUnderPos: [1, 2, 3, 8, 9, 10, 11]
)
@passOnwards(as: "list")
@applyField(
name: "_objectProperty",
arguments: {
object: $elementJSON,
by: { key: "id" }
}
passOnwardsAs: "elementID"
)
@underEachArrayItem(
affectDirectivesUnderPos: [1, 3, 4]
)
@underJSONObjectProperty(
by: { key: "cta_link_text" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorLinkInBio1To5CtaLinkListItemTexts"
type: DICTIONARY
)
@applyField(
name: "_echo",
arguments: {
value: $elementID
}
setResultInResponse: true
)
@export(
as: "originElementorLinkInBio1To5CtaLinkListIDs"
type: DICTIONARY
)
@applyField(
name: "_arrayLength",
arguments: {
array: $list,
}
passOnwardsAs: "arrayLength"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $originElementorLinkInBio1To5CtaLinkListElementProps,
by: { key: $customPostID }
}
passOnwardsAs: "props"
)
@applyField(
name: "_objectAddEntry",
arguments: {
object: $props,
key: $elementID,
value: {
length: $arrayLength,
}
}
setResultInResponse: true
)
@export(
as: "originElementorLinkInBio1To5CtaLinkListElementProps"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-3)
####################################################
}
}
}
query FixExportOriginCustomPostDataForElementor
@depends(on: "ExportOriginCustomPostDataForElementor")
@include(if: $hasCustomPosts)
@include(if: $useElementorPageBuilder)
@include(if: $executeTranslation)
{
originElementorLinkInBio1To5CtaLinkListElementProps: _echo(value: $originElementorLinkInBio1To5CtaLinkListElementProps)
@underEachJSONObjectProperty(
passValueOnwardsAs: "list"
)
@applyField(
name: "_objectMerge",
arguments: {
objects: $list,
},
setResultInResponse: true
)
@export(
as: "originElementorLinkInBio1To5CtaLinkListElementProps"
)
####################################################
##### Insert code for Elementor widgets (e-4)
####################################################
}
# ...
query InitializeTranslationVariablesForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft,
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "FixExportOriginCustomPostDataForElementor")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useGutenbergEditor)
{
emptyTranslationCustomPostVarsForElementor: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
id
emptyArray: _echo(value: [])
@export(
as: "destinationElementorLinkInBio1To5IDs"
type: DICTIONARY
)
@export(
as: "destinationElementorLinkInBio1To5HeadingItems"
type: DICTIONARY
)
@export(
as: "destinationElementorLinkInBio1To5TitleItems"
type: DICTIONARY
)
@export(
as: "destinationElementorLinkInBio1To5DescriptionItems"
type: DICTIONARY
)
@export(
as: "destinationElementorLinkInBio1To5CtaLinkListItemTexts"
type: DICTIONARY
)
@export(
as: "destinationElementorLinkInBio1To5CtaLinkListElementProps"
type: DICTIONARY
)
####################################################
##### Insert code for Elementor widgets (e-5)
####################################################
@remove
}
}
}
# ...
query FetchDataForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft
$customPostType: CustomPostEnumString!
$translateContent: Boolean! = true
)
@depends(on: "FetchData")
@include(if: $executeTranslation)
@include(if: $translateContent)
@include(if: $useElementorPageBuilder)
{
translationCustomPostsForElementor: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
originElementorLinkInBio1To5IDs: _objectProperty(
object: $originElementorLinkInBio1To5IDs
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorLinkInBio1To5IDs"
type: DICTIONARY
)
@remove
originElementorLinkInBio1To5HeadingItems: _objectProperty(
object: $originElementorLinkInBio1To5HeadingItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorLinkInBio1To5HeadingItems"
type: DICTIONARY
)
@remove
originElementorLinkInBio1To5TitleItems: _objectProperty(
object: $originElementorLinkInBio1To5TitleItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorLinkInBio1To5TitleItems"
type: DICTIONARY
)
@remove
originElementorLinkInBio1To5DescriptionItems: _objectProperty(
object: $originElementorLinkInBio1To5DescriptionItems
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorLinkInBio1To5DescriptionItems"
type: DICTIONARY
)
@remove
originElementorLinkInBio1To5CtaLinkListIDs: _objectProperty(
object: $originElementorLinkInBio1To5CtaLinkListIDs
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorLinkInBio1To5CtaLinkListIDs"
type: DICTIONARY
)
@remove
originElementorLinkInBio1To5CtaLinkListItemTexts: _objectProperty(
object: $originElementorLinkInBio1To5CtaLinkListItemTexts
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorLinkInBio1To5CtaLinkListItemTexts"
type: DICTIONARY
)
@remove
originElementorLinkInBio1To5CtaLinkListElementProps: _objectProperty(
object: $originElementorLinkInBio1To5CtaLinkListElementProps
by: { key: $__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorLinkInBio1To5CtaLinkListElementProps"
type: DICTIONARY
)
@remove
####################################################
##### Insert code for Elementor widgets (e-6)
####################################################
}
}
}
# ...
query ExportTransformationDataSourceForElementor
@depends(on: "ExportTransformationDataSourceForClassicEditor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
allTransformationSourcesElementor: _objectMerge(
objects: [
$transformationSources,
{
elementorWidgets: {
linkInBio1To5Heading: {
to: $destinationElementorLinkInBio1To5HeadingItems,
},
linkInBio1To5Title: {
to: $destinationElementorLinkInBio1To5TitleItems,
},
linkInBio1To5Description: {
to: $destinationElementorLinkInBio1To5DescriptionItems,
},
linkInBio1To5CtaLinkList: {
to: $destinationElementorLinkInBio1To5CtaLinkListItemTexts,
},
####################################################
##### Insert code for Elementor widgets (e-7)
####################################################
},
}
]
)
@export(as: "transformationSources")
}
# ...
query PrepareReplacementsForElementor
@depends(on: "PrepareReplacementsForClassicEditor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
transformedElementorLinkInBio1To5: _echo(value: $destinationElementorLinkInBio1To5IDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.linkInBio1To5Heading.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorLinkInBio1To5HeadingItems"
)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.linkInBio1To5Title.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorLinkInBio1To5TitleItems"
)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.linkInBio1To5Description.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorLinkInBio1To5DescriptionItems"
)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.linkInBio1To5CtaLinkList.to.%s",
values: [$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformations
by: { path: $path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorLinkInBio1To5CtaLinkListItemTexts"
)
####################################################
##### Insert code for Elementor widgets (e-8)
####################################################
}
query AdaptReplacementsForElementor
@depends(on: "PrepareReplacementsForElementor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
adaptedElementorLinkInBio1To5CtaLinkListItemTexts: _echo(value: $transformedElementorLinkInBio1To5CtaLinkListItemTexts)
@underEachJSONObjectProperty
@underEachArrayItem(
passValueOnwardsAs: "value"
)
@applyField(
name: "_echo",
arguments: {
value: {
cta_link_text: $value,
}
}
setResultInResponse: true
)
@export(
as: "transformedElementorLinkInBio1To5CtaLinkListItemTexts"
)
####################################################
##### Insert code for Elementor widgets (e-9)
####################################################
}
# ...
query PrepareTranslationInputsForElementor
@depends(on: "TranslateCustomPosts")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
transformedElementorLinkInBio1To5Entries: _echo(value: $destinationElementorLinkInBio1To5IDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
)
@underEachArrayItem(
passIndexOnwardsAs: "key"
passValueOnwardsAs: "elementID"
affectDirectivesUnderPos: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
)
@applyField(
name: "_sprintf",
arguments: {
string: "%s.%s",
values: [$customPostID, $key]
}
passOnwardsAs: "path"
)
@applyField(
name: "_sprintf",
arguments: {
string: "%s.%s",
values: [$customPostID, $elementID]
}
passOnwardsAs: "idPath"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorLinkInBio1To5HeadingItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "bio_heading"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorLinkInBio1To5TitleItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "bio_title"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorLinkInBio1To5DescriptionItems
by: { path: $path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "bio_description"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $destinationElementorLinkInBio1To5CtaLinkListIDs
by: { key: $customPostID }
}
passOnwardsAs: "ctaLinkListIDs"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $transformedElementorLinkInBio1To5CtaLinkListItemTexts
by: { key: $customPostID }
}
passOnwardsAs: "ctaLinkListItems"
)
@applyField(
name: "_arraySearch",
arguments: {
array: $ctaLinkListIDs
element: $elementID
}
passOnwardsAs: "offset"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $destinationElementorLinkInBio1To5CtaLinkListElementProps
by: { path: $idPath }
}
passOnwardsAs: "ctaLinkListItemProps"
)
@applyField(
name: "_objectProperty",
arguments: {
object: $ctaLinkListItemProps
by: { key: "length" }
}
passOnwardsAs: "length"
)
@applyField(
name: "_arraySlice",
arguments: {
array: $ctaLinkListItems
length: $length,
offset: $offset
}
passOnwardsAs: "cta_link"
)
@applyField(
name: "_echo",
arguments: {
value: {
id: $elementID,
settings: {
bio_heading: $bio_heading,
bio_title: $bio_title,
bio_description: $bio_description,
cta_link: $cta_link,
}
}
},
setResultInResponse: true
)
@export(as: "transformedElementorLinkInBio1To5Entries")
####################################################
##### Insert code for Elementor widgets (e-10)
####################################################
}
query GenerateTranslationInputsForElementor(
$statusToUpdate: CustomPostStatusEnum! = draft
$customPostType: CustomPostEnumString!
)
@depends(on: "PrepareTranslationInputsForElementor")
@include(if: $executeTranslation)
@include(if: $useElementorPageBuilder)
{
elementorGenerateMutationInputs: customPosts(filter: { ids: $translationCustomPostIds, customPostTypes: [$customPostType], status: $statusToUpdate }, pagination: { limit: -1 }) {
__typename
...on CustomPost {
transformedElementorLinkInBio1To5Entries: _objectProperty(
object: $transformedElementorLinkInBio1To5Entries,
by: {
key: $__id
}
)
####################################################
##### Insert code for Elementor widgets (e-11)
####################################################
elements: _arrayMerge(arrays: [
$__transformedElementorLinkInBio1To5Entries,
####################################################
##### Insert code for Elementor widgets (e-12)
####################################################
])
}
}
}
Working on some code editor, copy the GraphQL query into a new file (you can format it as GraphQL to have syntax highlighting), add the 12 sections for the new widget, and copy the adapted query back into the GraphiQL client.
Then execute the query, and check if the translated post has the widget properties translated (by editing the translated post in Elementor and refreshing the page).
Repeat until it all works, and then persist the query via a hook in PHP code.
Persist the GraphQL query via hooks in PHP code
When using the gatompl:persisted_query
hook, the placeholders in the GraphQL query to inject the custom logic are:
##### Insert code for Elementor widgets (e-1)
##### Insert code for Elementor widgets (e-2)
- ...
##### Insert code for Elementor widgets (e-12)
Example implementation
Let's add support for the "My Call to Action" widget, which has the following JSON structure:
{
"id": "206a911",
"elType": "widget",
"settings": {
"title": "This is the heading",
"description": "Your Call to Action content comes here",
"button": "Click Here"
},
"elements": [],
"widgetType": "my-call-to-action"
}
As this widget stores properties only, we can replicate the pattern from the "Animated Headline" widget.
Using the gatompl:persisted_query
hook to inject the logic into the GraphQL query, the PHP logic is this one (notice that inside <<<GRAPHQL
, GraphQL variables must be escaped: \$
):
add_filter(
'gatompl:persisted_query',
function (string $persistedQuery, string $persistedQueryFile): string {
if (str_ends_with($persistedQueryFile, '/translate-customposts-for-polylang.gql')) {
return str_replace(
[
'##### Insert code for Elementor widgets (e-1)',
// '##### Insert code for Elementor widgets (e-2)', <= Not needed for this widget
'##### Insert code for Elementor widgets (e-3)',
// '##### Insert code for Elementor widgets (e-4)', <= Not needed for this widget
'##### Insert code for Elementor widgets (e-5)',
'##### Insert code for Elementor widgets (e-6)',
'##### Insert code for Elementor widgets (e-7)',
'##### Insert code for Elementor widgets (e-8)',
// '##### Insert code for Elementor widgets (e-9)', <= Not needed for this widget
'##### Insert code for Elementor widgets (e-10)',
'##### Insert code for Elementor widgets (e-11)',
'##### Insert code for Elementor widgets (e-12)',
],
[
<<<GRAPHQL
##### Insert code for Elementor widgets (e-1)
@export(
as: "originElementorMyCallToActionIDs"
type: DICTIONARY
)
@export(
as: "originElementorMyCallToActionTitleItems"
type: DICTIONARY
)
@export(
as: "originElementorMyCallToActionDescriptionItems"
type: DICTIONARY
)
@export(
as: "originElementorMyCallToActionButtonItems"
type: DICTIONARY
)
GRAPHQL,
<<<GRAPHQL
##### Insert code for Elementor widgets (e-3)
@underEachArrayItem(
passValueOnwardsAs: "elementJSON"
affectDirectivesUnderPos: [1, 2, 3]
)
@applyField(
name: "_objectProperty",
arguments: {
object: \$elementJSON,
by: { key: "widgetType" }
failIfNonExistingKeyOrPath: false,
},
passOnwardsAs: "widgetType"
)
@applyField(
name: "_equals",
arguments: {
value1: \$widgetType,
value2: "my-call-to-action"
},
passOnwardsAs: "isMatch"
)
@if(
condition: \$isMatch
affectDirectivesUnderPos: [1, 3, 5, 7]
)
@underJSONObjectProperty(
by: { key: "id" }
)
@export(
as: "originElementorMyCallToActionIDs"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.title" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorMyCallToActionTitleItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.description" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorMyCallToActionDescriptionItems"
type: DICTIONARY
)
@underJSONObjectProperty(
by: { path: "settings.button" }
failIfNonExistingKeyOrPath: false
)
@export(
as: "originElementorMyCallToActionButtonItems"
type: DICTIONARY
)
GRAPHQL,
<<<GRAPHQL
##### Insert code for Elementor widgets (e-5)
@export(
as: "destinationElementorMyCallToActionIDs"
type: DICTIONARY
)
@export(
as: "destinationElementorMyCallToActionTitleItems"
type: DICTIONARY
)
@export(
as: "destinationElementorMyCallToActionDescriptionItems"
type: DICTIONARY
)
@export(
as: "destinationElementorMyCallToActionButtonItems"
type: DICTIONARY
)
GRAPHQL,
<<<GRAPHQL
##### Insert code for Elementor widgets (e-6)
originElementorMyCallToActionIDs: _objectProperty(
object: \$originElementorMyCallToActionIDs
by: { key: \$__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorMyCallToActionIDs"
type: DICTIONARY
)
@remove
originElementorMyCallToActionTitleItems: _objectProperty(
object: \$originElementorMyCallToActionTitleItems
by: { key: \$__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorMyCallToActionTitleItems"
type: DICTIONARY
)
@remove
originElementorMyCallToActionDescriptionItems: _objectProperty(
object: \$originElementorMyCallToActionDescriptionItems
by: { key: \$__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorMyCallToActionDescriptionItems"
type: DICTIONARY
)
@remove
originElementorMyCallToActionButtonItems: _objectProperty(
object: \$originElementorMyCallToActionButtonItems
by: { key: \$__originCustomPostId }
failIfNonExistingKeyOrPath: false
valueWhenNonExistingKeyOrPath: []
)
@export(
as: "destinationElementorMyCallToActionButtonItems"
type: DICTIONARY
)
@remove
GRAPHQL,
<<<GRAPHQL
##### Insert code for Elementor widgets (e-7)
callToActionTitle: {
to: \$destinationElementorMyCallToActionTitleItems,
},
callToActionDescription: {
to: \$destinationElementorMyCallToActionDescriptionItems,
},
callToActionButton: {
to: \$destinationElementorMyCallToActionButtonItems,
},
GRAPHQL,
<<<GRAPHQL
##### Insert code for Elementor widgets (e-8)
transformedElementorMyCallToAction: _echo(value: \$destinationElementorMyCallToActionIDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.callToActionTitle.to.%s",
values: [\$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: \$transformations
by: { path: \$path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorMyCallToActionTitleItems"
)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.callToActionDescription.to.%s",
values: [\$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: \$transformations
by: { path: \$path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorMyCallToActionDescriptionItems"
)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_sprintf",
arguments: {
string: "elementorWidgets.callToActionButton.to.%s",
values: [\$customPostID]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: \$transformations
by: { path: \$path }
}
setResultInResponse: true
)
@export(
as: "transformedElementorMyCallToActionButtonItems"
)
GRAPHQL,
<<<GRAPHQL
##### Insert code for Elementor widgets (e-10)
transformedElementorMyCallToActionEntries: _echo(value: \$destinationElementorMyCallToActionIDs)
@underEachJSONObjectProperty(
passKeyOnwardsAs: "customPostID"
)
@underEachArrayItem(
passIndexOnwardsAs: "key"
passValueOnwardsAs: "elementID"
affectDirectivesUnderPos: [1, 2, 3, 4, 5]
)
@applyField(
name: "_sprintf",
arguments: {
string: "%s.%s",
values: [\$customPostID, \$key]
}
passOnwardsAs: "path"
)
@applyField(
name: "_objectProperty",
arguments: {
object: \$transformedElementorMyCallToActionTitleItems
by: { path: \$path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "title"
)
@applyField(
name: "_objectProperty",
arguments: {
object: \$transformedElementorMyCallToActionDescriptionItems
by: { path: \$path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "description"
)
@applyField(
name: "_objectProperty",
arguments: {
object: \$transformedElementorMyCallToActionButtonItems
by: { path: \$path }
failIfNonExistingKeyOrPath: false
}
passOnwardsAs: "button"
)
@applyField(
name: "_echo",
arguments: {
value: {
id: \$elementID,
settings: {
title: \$title,
description: \$description,
button: \$button,
}
}
},
setResultInResponse: true
)
@export(as: "transformedElementorMyCallToActionEntries")
GRAPHQL,
<<<GRAPHQL
##### Insert code for Elementor widgets (e-11)
transformedElementorMyCallToActionEntries: _objectProperty(
object: \$transformedElementorMyCallToActionEntries,
by: {
key: \$__id
}
)
GRAPHQL,
<<<GRAPHQL
##### Insert code for Elementor widgets (e-12)
\$__transformedElementorMyCallToActionEntries,
GRAPHQL,
],
$persistedQuery
);
}
return $persistedQuery;
},
10,
2
);