i do have a curious problem on a new wordpress developpement.

i have many CPT and WPML working on the site (default language is "french").

example, i have a custom post type who is : "marche" ( for Market in french ).

i add a market in default language (french) inside of it (named "Gordes") i fill it with infos.

Market is correct, and accessible here :


url is ok, slug is ok, everything is ok. (default language).

then i activate translation string in WPML settings, and defined the EN & NL string for "marche".

then i translate the market in NL ( dutch ) with copying french content, and there is where the problem starts :

  1. on back office, the generated URL is the same as in default language (slug in FR in place of NL and /nl/ don't show).
  2. on front office, same problem, but this time url comes with /nl/ but go on 404, because slug is wrong...

ex :


that has to be

-https://www.beprovence.be/nl/markt/gordes.html (here is no 404, page is ok).

even if i search for "gordes" in search box, the url are the same (in french) and goes to 404 instead of NL version.

And final bug, in my langage selector, on those CPT, the link under the active language (NL) is same, showing French slug. And when i look at FR link, it still have /nl/ in place of /fr/

i have reads hundreds of posts on WPML forum, and google, but i still can not go rid of this bugs. ( i have the same bug on all my custom posts ).

If needed, here is the code of my CPT.

function marches_CPT() {

  // Post Type
  $labels = array(
    'name' => 'Marchés',
    'all_items' => 'Tous les marchés',
    'singular_name' => 'Marché',
    'add_new_item' => 'Ajouter un camping',
    'edit_item' => "Modifier le marché",
    'menu_name' => 'Be | Marchés'

  $args = array(
    'labels' => $labels,
    'public' => true,
    'has_archive' => true,
    'capability_type' => 'page',
    //added rewrite for test purpose, with it or without, same problem
    'rewrite' => array('slug' => 'marche'),
    'supports' => array('title', 'editor','thumbnail','custom-fields'),
    'menu_position' => 12,
    'menu_icon' => 'dashicons-cart',



add_action('init', 'marches_CPT', '0');

Thanks in advance for your precious help.

  • Did you translate the post independently ? If you copy the content from the original, the slug is the same.
    – Beee
    Mar 19, 2018 at 13:01
  • thank you @Beee for your answear. of course i have translated the page, i added with the "+" next to original article, then clic on publish. as you can see, the page with good slug is ok, wrong slug is 404. but i don't know why, all my links link to wrong slug... i tried several time to flush rewrite rules, etc... nothing change at all :s
    – Gregory
    Mar 20, 2018 at 9:44
  • even in admin, it's strange it says : "this page is in NL and is a translation of FR", but show french slug and no /nl/ in url.
    – Gregory
    Mar 20, 2018 at 9:48
  • ok, i get more info ! i found a part of the solution. (i found the solution to be clear), it is my function for adding .html at the url of CPT that create the bug. Here is the function, i don't know if it is better to open a new post, or to continue here ?
    – Gregory
    Mar 20, 2018 at 12:57
  • the function : codefunction custom_post_permalink ($post_link) { global $post; if ( $post ) { $type = get_post_type($post->ID); return home_url() . '/' . $type . '/' . $post->post_name . '.html'; } } add_filter('post_type_link', 'custom_post_permalink'); // for cpt post_type_link (rather than post_link) code
    – Gregory
    Mar 20, 2018 at 12:58

1 Answer 1


My solution is for a WordPress Installation (Version 5.2.2), for translation is only WPML Multilingual CMS (Version installed.

The modules WPML String Translation (which is very performance-consuming) and WPML Translation Management are not required.

I'm using this WPML url structure: www.url.com/en/postname.

My Custom Post Type should have the slug "produkte" in German, "products" in English and "produkty" in Czech. In this example i'm using "produkte" as the custom post type slug of my source language, and "products" and "produkty" for the other languages. Replace this words with your slugs, add languages as you need.

First of all, assign a variable to the rewrite-slug of your custom post definition, not a fixed name (yes, that works...;-):

if( ICL_LANGUAGE_CODE == "de" ) $stt_product_slug = 'produkte';
if( ICL_LANGUAGE_CODE == "en" ) $stt_product_slug = 'products';
if( ICL_LANGUAGE_CODE == "cs" ) $stt_product_slug = 'produkty';

  register_post_type( 'stt-products',
    array( 'labels' => array( 'name' => 'Products', 'singular_name' => 'Product' ),
        'public' => true,
        'has_archive' => true,
        'supports' => array('title','custom-fields','editor','thumbnail','revisions','page-attributes'),
        'hierarchical' => true,
        'rewrite' => array( 'slug' => $stt_product_slug, 'with-front' => false )

Note: the custom post type registration has to be called in the 'init' hook.

Go to Wordpress admin/permalinks and click "Save changes" without any changing: flush_rewrite_rules() is called once and so the rewrite rules for the slug-name of your SOURCE language (my case DE "produkte") should be generated in the database table wp_options, option name "rewrite_rules".

Go into the table wp_options and copy all the text of the option name "rewrite_rules", "option_value" into a text editor (eg. Notepad++). The value of the database field is presented as a single line in the editor. For easier editing, replace all entries of




Find out, which rewrite rules are added for "produkte". Eg. one entry pair will look like:

s:11:"produkte/?$"; s:32:"index.php?post_type=stt-products";

Each of this pairs should be transformed into:

add_rewrite_rule( '^produkte/?$', 'index.php?post_type=stt-products' );

Create a list with all properly formed rewrite rules. Then, replace at once "produkte" with "products". Copy the whole list of rewrite rules, paste it beneath, and replace "products" with "produkty" and so on.

Then, you should have several blocks prepared looking like this:

add_rewrite_rule( '^products/?$', 'index.php?post_type=stt-products' );


add_rewrite_rule( '^produkty/?$', 'index.php?post_type=stt-products' );

Copy and paste this blocks beneath you custom post type registration in the 'init' hook. Go to Wordpress admin and press Permalinks/Save changes again (for writing the new rewrite rules in the database).

Now, the translated custom post type slugs should work.

If not, go into your database, table wp_options and check under the option_name rewrite_rules if the new rewrite rules are added. Wordpress is rather picky about the correct syntax and will reject rules without notice if they are not well formed.

Additionally, you have to customize your language switch manually, eg:

function stt_wpml_language_switch() {
    $languages = icl_get_languages('skip_missing=0&orderby=KEY&order=DIR');
        echo '<ul>';
        foreach($languages as $l) {
            if( ICL_LANGUAGE_CODE == "de" ) {
                if( $l['code'] == "en" ) $l['url'] = str_replace( "produkte", "products", $l['url'] );
                if( $l['code'] == "cs" ) $l['url'] = str_replace( "produkte", "produkty", $l['url'] );
            if( ICL_LANGUAGE_CODE == "en" ) {
                if( $l['code'] == "de" ) $l['url'] = str_replace( "products", "produkte", $l['url'] );
                if( $l['code'] == "cs" ) $l['url'] = str_replace( "products", "produkty", $l['url'] );
            if( ICL_LANGUAGE_CODE == "cs" ) {
                if( $l['code'] == "de" ) $l['url'] = str_replace( "produkty", "produkte", $l['url'] );
                if( $l['code'] == "en" ) $l['url'] = str_replace( "produkty", "products", $l['url'] );
            echo '<li>';
            if(!$l['active']) echo '<a href="'.$l['url'].'" class="stt-languageswitch-link">';
            if($l['active']) echo '<span class="wpml-ls-current-language">' . icl_disp_language($l['native_name']) . '</span>';
            if(!$l['active']) echo icl_disp_language($l['native_name']);
            if(!$l['active']) echo '</a>';
            echo '</li>';
        echo '</ul>';

Of course, all language-specific renderings of the translated custom post type slug (eg. in header.php, footer.php etc.) should be manually reformed in this manner.

Maybe this is a not so clean workaround, on the other hand, the setting of the rewrite rules seems to be correct.

Please leave a note, if you have better suggestions on this topic.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.