toolset-classifiedsThis tutorial explains how to build multilingual-ready listing and classifieds sites using WordPress and WPML.

In this tutorial, we will use our Toolset Classifieds reference theme. It’s a fully featured, multilingual-ready, classifieds site, built using Toolset plugins.

What does it mean to have a multilingual listings site?

Listing, classifieds and all sorts of ‘marketplace’ sites offer visitors to create content from the site’s front-end. That content is the listings, which other visitors will be looking for.

When we talk about multilingual listing sites, we mean that:

  1. Visitors can switch the language, in which they are browsing the site.
  2. The site’s structure, navigation and all ‘hard coded’ texts will appear in the correct language.
  3. Listings that are translated will appear in the current language.
  4. Untranslated listings will appear in their original language.

With these four ground rules, we can develop our multilingual listing sites!

English homepage French homepage
Everything in English

Everything in English

Structure in French, user content in original English

Structure in French, user content in original English

Structure of the site

As we are talking about listing sites, we will need to store the listing items and organize them. For this, we will use custom post types and custom taxonomy.

  • Listings – custom posts
  • Listing categories – custom taxonomy

We want to allow visitors to write their own listings, without going through the WordPress admin. The site admin will set up the listing categories. Visitors will write ‘listings’, which belong to these categories.

What gets translated and what gets duplicated

WPML’s way for displaying untranslated content is by duplicating it to all languages. This means that visitors see the same text, but from a different entry in the database, depending on the selected language.

Why is this a good idea? Because each piece of content belongs to just one language. The site will display everything in the correct language, even if the content is not really translated.

Since the categories are really part of the site’s structure, the site admins should translate them. Admins should translate everything that they create. This includes the pages, strings and categories.

Visitors will enter their listings and the site will automatically duplicate them to all languages. Then, if the visitor wishes to translate a listing to a certain language, the duplicated copy will get overwritten with proper translation.

Duplicating listings to all languages after front-end submission

CRED form for creating a new ad

CRED form for creating a new ad

Toolset Classifieds uses a CRED form to create listings in the front-end. CRED is a plugin that lets you build forms for creating and editing WordPress content from front-end pages. CRED comes with an extension called CRED Commerce, which also allows to charge payments for creating content. We need this, so that we can charge members for ‘premium’ ads.

We use the CRED Commerce hook ‘cred_commerce_after_notifications’ to duplicate the listings, right after they are created.

Here is the code that does this:

add_action('cred_commerce_after_send_notifications', 'classifieds_cred_commerce_after_send_notifications',10,1);

This hook allows us to check the status of the WooCommerce order associated to our listing and only when the status is ‘completed’, to trigger the duplication process.

This is the code that we run after listings are created:

//duplicate posts created with CRED
function classifieds_cred_commerce_after_send_notifications($data){
    if (isset($data['new_status']) && 'completed' == $data['new_status']) {
        if (isset($data['cred_meta'][0]['cred_post_id']) && isset($data['cred_meta'][0]['cred_form_id'])) {
            $cred_form_id = get_post($data['cred_meta'][0]['cred_form_id']);
            // if a specific form
            $evaluate_cred_form_slug = $cred_form_id->post_name;
            switch ($evaluate_cred_form_slug) {
                case 'add-new-free-ad':
                case 'add-new-premium-ad':
                    classifieds_duplicate_on_publish($data['cred_meta'][0]['cred_post_id']);
                    break;
                default:
                    break;
            }
        }
    }
}

This function does the following:

  1. Check that the order is completed (we only want to duplicate listings that are ready to go live)
  2. Check that we have all needed arguments (if something is missing, avoid a PHP error)
  3. Load the post (get_post…)
  4. Check that we are submitting a new ad
  5. If so, duplicate the ad to all languages – the classifieds_duplicate_on_publish function does this.

This action hook will be executed after the notification has been sent and on order status change. It accepts one parameter $data. $data is an associative array that holds the form/post/product combinations and information about the WooCommerce order associated.

$data=>array(
   ‘order_id’ => WooCommerce Order ID associated to the buying process
   ‘previous_status’=> the initial status of the order, usually ‘pending’
   ‘new_status’=> the new status of the order direct consequence of the payment. It expects one of the following options ('pending', 'failed', 'processing', 'completed', 'on-hold', 'cancelled', 'refunded' )
   'cred_meta'=>array( array(
      'cred_product_id'=> Product ID that was associated with the form and payment,
      'cred_form_id'=> ID of CRED Commerce form that customer used,
      'cred_post_id'=> Post ID that was created/edited with the form
    ))
);

When we use $data[‘new_status’] we are checking for the status of the WooCommerce order and will call the duplication function if the order status is ’completed’.

Full details about this filter is available in the CRED Commerce API documentation.

The actual duplication is managed by WPML, and we are using a built-in function that we can call upon to build the duplicated posts. If you are writing your custom classifieds sites, you can use this function, in WPML, to duplicate content to different languages.

function icl_makes_duplicates( $master_post_id ) {
    global $sitepress, $iclTranslationManagement;
    if ( !isset( $iclTranslationManagement ) ) {
        $iclTranslationManagement = new TranslationManagement;
    }
    $post_type = get_post_type( $master_post_id );
    if ( $sitepress->is_translated_post_type( $post_type ) ) {
        $iclTranslationManagement->make_duplicates_all( $master_post_id );
    }
}

This function is available from the WPML Coding API. The $master_post_id is the ID of the post to duplicate. If duplicates already exist, they will be updated. Otherwise, if duplicates don’t exist, they will be created. The master post can be in any language and will be duplicated to the other languages in the site.

Our classifieds site should work with and without WPML active, so before we call icl_makes_duplicates, we check if the function exists.

//duplicate a post/page/custom post on publish or update
function classifieds_duplicate_on_publish ($post_id) {
    if(function_exists('icl_makes_duplicates')){    
        icl_makes_duplicates($post_id);
    }
}

Translating ads (replacing the duplicates)

Of course, the duplicate ads that we’ve created are separate database entries. This makes it easy to translate some ads to some languages. When we do that, we just need to unset the flag which tells WPML that this content is duplicate. Unsetting this flag is very important. If we don’t do it, WPML will overwrite our translations, every time the original ad is updated.

This is what we do:

  1. Use a CRED form to edit the ads in the different languages.
  2. Set up another CRED hook to unset the ‘duplicate’ flag, after manually translating ads.

WPML’s icl_makes_duplicates API function verifies that the translations are duplicates. If so, it will overwrite them when the original changes. Otherwise, the translations are left unchanged.

We use another CRED hook, ‘cred_save_data’, to unset the ‘duplicate’ status, after manually translating ads. We wrote the function ‘cred_save_data_message_action()’ and connected it to that hook.

add_action('cred_save_data', 'cred_save_data_message_action',10,2);

function cred_save_data_message_action($post_id, $form_data){
    global $post;    
    $cred_form_id = get_post($form_data['id']);
    // if a specific form
    $evaluate_cred_form_slug=$cred_form_id->post_name;
    switch ($evaluate_cred_form_slug) {
        case 'edit-product':
            classifieds_reset_duplicate_flag($post_id);
            break;
    }
}

This function does the following:

  1. Check that this is indeed a product-edit form.
  2. Call ‘classifieds_reset_duplicate_flag’ to reset the ‘duplicate’ flag for the post that’s been edited.

This hook is also detailed in the CRED API documentation.

The classifieds_reset_duplicate_flag calls TranslationManagement::reset_duplicate_flag, which will unset the ‘duplicate’ status for the translated post, and allow it to run independent of the original ad.

 function classifieds_reset_duplicate_flag($post_id) {
    global $sitepress, $iclTranslationManagement;
    if (is_object($sitepress)) {
        if ('TranslationManagement' !== get_class($iclTranslationManagement)) {
            require_once( ICL_PLUGIN_PATH . '/lib/icl_api.php' );
            require_once( ICL_PLUGIN_PATH . '/lib/xml2array.php' );
            require_once( ICL_PLUGIN_PATH . '/inc/translation-management/translation-management.class.php' );
            $iclTranslationManagement = new TranslationManagement();
        }
        $iclTranslationManagement->reset_duplicate_flag($post_id);
    }
}

This function does:

  1. Make sure that $sitepress object and the TranslationManagement class are available.
  2. Load the needed libraries.
  3. Calls the ‘reset_duplicate_flag’ function.

Note: this is a temporary solution. The above code will go into WPML soon and we will use an API function for this process.


Need help?

Toolset - WPML