In a customer project we have been asked how to get the WPSiteSync Plugin working together with WPML in a multisite / multilanguage scenario. Even the creators of WPSiteSync apologized, that their tool isn’t working correctly together with WPML and they have – at least currently – no plans to integrate that. Therefore so here’s an approach that is not thoroughly tested to work under all circumstances but at least tested good enough to work in a predefined scenario to solve the issues.
The basic free version of WPSiteSync was created for a realistic setup in mid-scale to larger WordPress Projects where you would have a LIVE/PROD instance, a QA or TESTING instance and perhaps – for development purpose – a DEV instance. WPSiteSync is linking 2 instances together (here PROD and QA) so content editors could work on their content on QA and if done, checked, ready to go (or whatever the workflow might be…) push it to the Live site (PROD).
Would be also quite enough to call the scenario SOURCE <-> TARGET but that’s more academic, so let’s stick with a realistic QA <-> LIVE setup
To put it that way: it is very useful for pushing content from one instance to another, e.g. from QA to LIVE. Only Content, not source files from the system. If you stick with that and perhaps have also added the premium plugins bundle which offers custom post types, forms and WooCommerce support, menus, authors and such, then it might be a good choice to handle the sync between an (internal) QA instance and the LIVE website.
Let’s imagine your company is internationally engaged or at least wants to offer its website content to a more international audience like English, French, Spanish, German. Then you might have translations. And if so, the technical solution for that might be implementing this by using the well-known WPML Plugin (The WordPress multilingual plugin). There are other translation plugins available also for free, but this scenario covers only WPML and is thus not suitable for other translation plugins, but might show a way how to solve such issues in a similar manner.
WPML is widely know and perhaps the most complete and stable multilanguage solution for WordPress in a single plugin. For the ease of it, let’s stick with the features from the WPML developer’s site:
One WordPress Installation – Multiple Languages
WPML makes it easy to run a multilingual website with a single WordPress installation. Choose languages for your site and start translating content.
WPML comes with over 40 languages. You can also add your own language variants (like Canadian French or Mexican Spanish) using WPML’s languages editor.
Posts and pages are just the beginning. With WPML, you can translate custom post types, custom fields, widgets, menus, images, taxonomy, media – even texts in your site’s admin.
By default translated content will be pushed to the default language of the target system. Let’s assume we have
in our multilanguage setup, DE content will get pushed to EN on the target system and thus needs at least manual fixing. So the extra-value of using WPSiteSync might be gone, since it might me nearly the same effort as if re-creating same content on LIVE by copy/paste from QA.
With a little tweak of only two files it is possible to make WPSiteSync working nice with WPML and not only pushing it to the correct language but even glueing translations together on the target system.
The described workaround might not be perfect and feel free to create an extra addon plugin that hooks into original WPSiteSync or such, but here is just to explain what changes to the original source code make WPSiteSync treat WPML multilanguage scenarios correctly.
Open the file /classes/apicontroller.php and add the following
//****************************
//set correct language
global $sitepress;
//TODO: set langauge only if does not match source_post language
$target_post_language = $sitepress->get_language_for_element($target_post_id);
//TO DO: use $element_language___language_code instead of $current_language
// SyncDebug::log(__METHOD__.'():'.__LINE__ . ' Check if languages match: SOURCE='.$current_language.' / TARGET='. $target_post_language);
// if($current_language === $target_post_language) SyncDebug::log(__METHOD__.'():'.__LINE__ . ' *** MATCH ***');
SyncDebug::log(__METHOD__.'():'.__LINE__ . ' Check if languages match: SOURCE='.$element_language___language_code.' / TARGET='. $target_post_language);
if($element_language___language_code=== $target_post_language) SyncDebug::log(__METHOD__.'():'.__LINE__ . ' *** MATCH ***');
else {
SyncDebug::log(__METHOD__.'():'.__LINE__ . ' *** DO NOT MATCH ***');
//TODO: check if translation and set all needed values
//important! has to check if > 1, since original always shows up, too
if(count($element_translations) >1 )
SyncDebug::log(__METHOD__.'():'.__LINE__ . ' BEFORE SETTING TARGET LANG: CHECK IF HAS TRANSLATIONS '. var_export(count($element_translations), TRUE));
//get siblings from source and match to target siblings
SyncDebug::log(__METHOD__.'():'.__LINE__ . ' element_type => '. $content_type);
$target_post_type = 'post_post';
//maybe better to not check for each post type, but would not work for taxonomies and so on
if($post_data['post_type'] === 'post' || 'page' || 'news' /*or other custom post types*/)
$target_post_type = 'post_'.$post_data['post_type'];
else $target_post_type = 'tax_'.$post_data['post_type'];
SyncDebug::log(__METHOD__.'():'.__LINE__ . ' $target_post_type => '. $target_post_type);
$set_language_args = array(
'element_id' => $target_post_id,
'element_type' => $target_post_type,
'trid' => $target_post_id,
'language_code' => $element_language___language_code,
'source_language_code' => $element_language___source_language_code
);
$check_do_action= do_action( 'wpml_set_element_language_details', $set_language_args );
SyncDebug::log(__METHOD__.'():'.__LINE__ . ' $check_do_action '. var_export($check_do_action , TRUE));
//if not successful, might be a taxonomy
}
// this lets add-ons know that the Push operation is complete. Ex: CPT add-on can handle additional taxonomies to update
SyncDebug::log(__METHOD__.'():'.__LINE__ . " calling action 'spectrom_sync_push_content'");
do_action('spectrom_sync_push_content', $target_post_id, $post_data, $response);
at around line 357 where marked
$post_data = $this->post_raw('post_data', array());
SyncDebug::log(__METHOD__.'():' . __LINE__ . ' - post_data=' . var_export($post_data, TRUE));
>>>>>>>>>>>
$this->source_post_id = abs($post_data['ID']);
SyncDebug::log(__METHOD__.'():' . __LINE__ . ' syncing post data Source ID#'. $this->source_post_id . ' - "' . $post_data['post_title'] . '"');
Insert the following code to /classes/apirequest.php
$log_model = new SyncLogModel();
$log_model->log($remote_args);
global $sitepress;
// update settings
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' abs($data[\'post_id\']) =' . abs($data['post_id']));
$default_language = $sitepress->get_default_language();
$current_language = $sitepress->get_current_language();
$element_trid=$sitepress->get_element_trid(abs($data['post_id']));
$translations = $sitepress->get_original_element_translation($element_trid, 'page');
$element_language = $sitepress->get_element_language_details(abs($data['post_id']));
$element_language___trid= $post_data['sitepress']['element_language']['trid'];
$element_translations = $sitepress->get_element_translations($element_trid);
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' $$$translations =' . var_export($translations, TRUE));
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' $$element_translations =' . var_export($element_translations, TRUE));
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' $$$element_language___trid =' . $element_language___trid);
$test_translation=$sitepress->get_element_translations_object($data['post_type']);
$sitepress->language_attributes($output);
$the_languages=$sitepress->get_languages();
$active_langs = $sitepress->get_active_languages();
$active_langs = apply_filters( 'wpml_active_languages_access', $active_langs, array( 'action' => 'edit', 'post_type' => $this->post_type_label, 'post_id' => $this->post->ID ) );
foreach ( $active_langs as $lang ) {
$test_lang1=esc_html( $lang['display_name'] );
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' $test_lang1 =' . $test_lang1);
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' abs($data[\'post_id\']) =' . abs($data['post_id']));
}
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' $test_translation =' . var_export($test_translation, TRUE));
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' $test_translation =' . var_export($the_languages, TRUE));
SyncDebug::log(__METHOD__ . '():' . __LINE__ . ' ########## END OF LANGUAGES');
if (!isset($remote_args['sitepress'])) {
$remote_args['body']['post_data']['sitepress']['language'] = "test";
$remote_args['body']['post_data']['sitepress']['default_language'] = $default_language;
$remote_args['body']['post_data']['sitepress']['current_language'] = $current_language;
$remote_args['body']['post_data']['sitepress']['element_language'] = $element_language;
$remote_args['body']['post_data']['sitepress']['translations'] = $translations;
$remote_args['body']['post_data']['sitepress']['element_translations'] = $element_translations;
$remote_args['body']['post_data']['sitepress']['translations'] = "yes/no";
$remote_args['body']['post_data']['sitepress']['transl_ids'] = "1,2,3";
}
//########################
SyncDebug::log(__METHOD__ . '########## sitepress ##########');
$log_model = new SyncLogModel();
$log_model->log($remote_args);
at around line 190 before
// send data where it's going
and then
if (isset($post_data['sitepress']))
$data['sitepress'] = $post_data['sitepress'];
somewhere at line 722 where all the post_data gets set
Since this still might be not perfect, test it yourself first in a localhost environment with 2 instances Source & Target. You need to have WPML installed for that and at least 1 additional language to the default.
There might still be superfluous or debug code but it should point a way how to use it and might also give hints for adding other translation plugins than WPML