Skip to content Skip to sidebar

This thread is resolved. Here is a description of the problem and solution.

Problem:
You are experiencing 404 errors on all links in translations using the WYSIWYG Classic editor or Gutenberg. This issue does not appear on your staging website.
Solution:
Ensure that the option 'Translate Link Targets' under WPML > Settings is enabled, as this resolved the issue on our test site. If this does not resolve your issue, check if the custom post type (CPT) archive and the taxonomy base are using the same slug 'recettes'. This is not recommended as it can cause conflicts in rewrite rules and lead to unexpected behaviors. Using different slugs for the CPT archive and taxonomy base may resolve the issue.

If these steps do not resolve your issue, or if the solution seems outdated or irrelevant to your case, we highly recommend checking related known issues at https://wpml.org/known-issues/, verifying the version of the permanent fix, and confirming that you have installed the latest versions of themes and plugins. If the problem persists, please open a new support ticket at WPML support forum for further assistance.

This is the technical support forum for WPML - the multilingual WordPress plugin.

Everyone can read, but only WPML clients can post here. WPML team is replying on the forum 6 days per week, 22 hours per day.

Tagged: 

This topic contains 41 replies, has 0 voices.

Last updated by Andreas W. 1 month, 3 weeks ago.

Assisted by: Andreas W..

Author Posts
June 26, 2025 at 7:47 am #17171769

nicolasG-15

I understand that this is not a conventional way of declaring a CPT but it's working before updates. What's more, with your solution I completely lose the structure of my permalinks : .../subsite/{recipe_category}/{child_recipe_category}/{cpt_slug}.

I couldn't translate the argument 'has_archive' either.

I'm going to downgrad the site's updates so that you can see for yourself

June 26, 2025 at 9:45 am #17172582

nicolasG-15

At last I've been able to put the site back to the way it was before.

So you can try adding a link with an article in English (in your test post), it won't be modified.

The site can't lose its recipe link hierarchy. There was a lot of SEO work for several months because it worked before the plugins updates.

June 26, 2025 at 3:58 pm #17174622

Andreas W.
WPML Supporter since 12/2018

Languages: English (English ) Spanish (Español ) German (Deutsch )

Timezone: America/Lima (GMT-05:00)

If you have undone the changes by recovering from backup, it is expected that the issue will occur again.

My earlier suggestion solved the issue with the internal link, and I was able to access all CPTs and CPT archives.

You translate the slug at WPML > Settings > Post Type Translation.

This worked on my test.

If you hardcode the slug this way, WPML can not redirect correctly:
'recettes/%recipe_category%'

June 27, 2025 at 2:12 pm #17177824

nicolasG-15

OK, let's start from the beginning.

The current version of the site is the same as when it went online on November 18, 2024. WordPress 6.7.2., WPML 4.6.13., WPML String Translation 3.2.14. etc.

The ‘recipes/%recipe_category%’ works because that's what's expected: a permalink hierarchy : /recipe_category_slug/recipe_category_child_slug/recipe_slug...

When a recipe link is added from an article, the link is not changed to something like ?recipe_category=slug.

You can check in the first subsite (Dispatching) with the post Test B.

So my point is with the latest versions of the WPML suite - the theme hasn't changed - there's something that transforms the links after saving in Gutenberg.

June 28, 2025 at 7:23 am #17178993

Andreas W.
WPML Supporter since 12/2018

Languages: English (English ) Spanish (Español ) German (Deutsch )

Timezone: America/Lima (GMT-05:00)

I can sadly no longer connect to your website.

WPML does have an issue translating such hardcoded permalink bases.

If you would, for example, a base like /posts/%post_category% directly in WordPress's permalink settings, you will run into the same issue. WPML is sadly not able to translate the base /posts/ in such cases.

But what you can do is use my proposed workaround and then translate the slug of your Custom Post Type at WPML > Settings > Post Type Translation.

If this will still not solve the issue, please replicate the same issue on the sandbox and explain in short details what you are expecting:
hidden link

I further found the following errata, which I would like to consider, as there is a known issue with using "has_archive":
https://wpml.org/errata/the-has-category-function-does-not-work-for-translated-posts/

If this still does not solve the issue, please let me know, and we can take further steps.

June 28, 2025 at 10:12 am #17179217

nicolasG-15

Please try to log in again. The host sometimes blocks connections to wp-login.php for security reasons.

On my website demo everything is up to date except the WPML plugins. If you create a post with recipe link in english : it's work as exepted, no link transformation.

On your sandbox I've done the following :

1. Creating an English article with an English recipe link
2. Add your translation with WPML's classic translator Gutenberg
3. Go to WPML settings and click on Translate Link Targets

Expected link : .../de/recipes/test-a/recipe-test-de/
After saving : .../?recipe_category=recipe-test-de

WPML is capable of managing a custom permalink structure, as it works on my site with an earlier version. SO it's quite likely that between version 4.6.13. and version 4.7.6. such a modification was made.

June 28, 2025 at 10:53 pm #17179747

Andreas W.
WPML Supporter since 12/2018

Languages: English (English ) Spanish (Español ) German (Deutsch )

Timezone: America/Lima (GMT-05:00)

Note, our support can not take responsibility for debugging custom code.

WPML Support Policy:
https://wpml.org/purchase/support-policy/

The following example works as expected on my test site using the latest version of WPML.

Test Site:
hidden link

The following example will display the CPTs as:
/custom-post-type/custom-post-type-category/custom-post-type-title

See functions.php of Twenty Twenty-One:

function myplugin_register_recipes_cpt_and_taxonomy() {
    // WPML-translatable slugs
    $recipe_slug       = apply_filters('wpml_translate_single_string', 'recipes', 'Custom Slugs', 'Recipe CPT Slug');
    $archive_slug      = apply_filters('wpml_translate_single_string', 'recipes-archive', 'Custom Slugs', 'Recipe Archive Slug');
    $category_slug     = apply_filters('wpml_translate_single_string', 'recipe-category', 'Custom Slugs', 'Recipe Category Slug');

    // Register CPT: Recipe
    $labels = array(
        'name'               => _x('Recipes', 'Post Type General Name', 'textdomain'),
        'singular_name'      => _x('Recipe', 'Post Type Singular Name', 'textdomain'),
        'menu_name'          => __('Recipes', 'textdomain'),
        'add_new_item'       => __('Add New Recipe', 'textdomain'),
        'edit_item'          => __('Edit Recipe', 'textdomain'),
        'new_item'           => __('New Recipe', 'textdomain'),
        'view_item'          => __('View Recipe', 'textdomain'),
        'all_items'          => __('All Recipes', 'textdomain'),
        'search_items'       => __('Search Recipes', 'textdomain'),
        'not_found'          => __('No recipes found', 'textdomain'),
        'not_found_in_trash' => __('No recipes found in Trash', 'textdomain'),
    );

    $args = array(
        'label'           => __('Recipes', 'textdomain'),
        'labels'          => $labels,
        'public'          => true,
        'has_archive'     => $archive_slug,
        'rewrite'         => array(
            'slug'       => $recipe_slug . '/%recipe_category%',
            'with_front' => false,
        ),
        'supports'        => array('title', 'editor', 'thumbnail', 'excerpt'),
        'taxonomies'      => array('recipe_category'),
        'show_in_rest'    => true,
        'show_in_menu'    => true,
    );

    register_post_type('recipe', $args);

    // Register Taxonomy: Recipe Category
    $taxonomy_labels = array(
        'name'              => _x('Recipe Categories', 'taxonomy general name', 'textdomain'),
        'singular_name'     => _x('Recipe Category', 'taxonomy singular name', 'textdomain'),
        'menu_name'         => __('Recipe Categories', 'textdomain'),
        'search_items'      => __('Search Recipe Categories', 'textdomain'),
        'all_items'         => __('All Recipe Categories', 'textdomain'),
        'edit_item'         => __('Edit Recipe Category', 'textdomain'),
        'add_new_item'      => __('Add New Recipe Category', 'textdomain'),
        'new_item_name'     => __('New Recipe Category Name', 'textdomain'),
    );

    $taxonomy_args = array(
        'hierarchical'      => true,
        'labels'            => $taxonomy_labels,
        'show_ui'           => true,
        'show_admin_column' => true,
        'rewrite'           => array(
            'slug'       => $recipe_slug,
            'with_front' => false,
        ),
        'show_in_rest'      => true,
    );

    register_taxonomy('recipe_category', array('recipe'), $taxonomy_args);

    // Rewrite tags and structure
    add_rewrite_tag('%recipe_category%', '([^/]+)', 'recipe_category=');
    add_permastruct('recipe', '/' . $recipe_slug . '/%recipe_category%/%recipe%', false);
}
add_action('init', 'myplugin_register_recipes_cpt_and_taxonomy');

/**
 * Replace %recipe_category% with actual slug in permalinks
 */
function myplugin_recipe_permalink($permalink, $post) {
    if ($post->post_type !== 'recipe') return $permalink;

    $terms = get_the_terms($post->ID, 'recipe_category');
    $slug = (!empty($terms) && !is_wp_error($terms)) ? $terms[0]->slug : 'uncategorized';

    return str_replace('%recipe_category%', $slug, $permalink);
}
add_filter('post_type_link', 'myplugin_recipe_permalink', 10, 2);

/**
 * Register WPML strings and flush permalinks on activation
 */
function myplugin_activate() {
    do_action('wpml_register_single_string', 'Custom Slugs', 'Recipe CPT Slug', 'recipes');
    do_action('wpml_register_single_string', 'Custom Slugs', 'Recipe Archive Slug', 'recipes-archive');
    do_action('wpml_register_single_string', 'Custom Slugs', 'Recipe Category Slug', 'recipe-category');

    myplugin_register_recipes_cpt_and_taxonomy();
    flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'myplugin_activate');

Here is the translated test post linking to the translated book:
hidden link

It works as expected on my test site if you follow this example.

Please use this example as a reference.

June 30, 2025 at 8:15 pm #17186007

nicolasG-15

Okay, several things:

You don't answer my question, which is why it doesn't work anymore since the plugin updates. As a developer, if I knew the exact changes made when post saving, I could look for the issue more faster.

Secondly, on your site the books archive doesn't work /books/%book_category%/ or /books/. In addition, the books slug isn't translated into German either.

On my site I need to be able to use a custom post type “recipe” to have a “recettes” archive in the French plural and then translate it to the english Plural.

I don't want you to debug my custom code because it works with an earlier version of the plugin. I want to know what happens when I save an article in the translation of the post because the link between the href attribute is modified and I don't think that's the expected behavior.

July 1, 2025 at 7:32 am #17186910

Andreas W.
WPML Supporter since 12/2018

Languages: English (English ) Spanish (Español ) German (Deutsch )

Timezone: America/Lima (GMT-05:00)

Thank you very much for the details!

I can confirm now that if we translate the CPT slug, the translation will no longer work, and CPTs become unavailable in second languages while using my earlier provided approach.

Anyhow, I am not able to replicate the issue with the link changing to a different format like ?recipe_category=slug. Instead, I receive a 404 error on translated CPTs when I attempt to translate a slug that includes a tag for CPT taxonomies.

Example of 404:
hidden link

Slug:
EN > books/%book_category%
DE > buecher/%book_category%

This neither works for CPT archive pages on my test, like:
hidden link

The issue with this approach is:

- It is using a placeholder (%book_category%) in the CPT slug, which is replaced dynamically in the permalink.

- WPML doesn’t automatically understand how to translate or resolve these dynamic parts in translated URLs.

Solution:
Instead of hardcoding 'books/%book_category%', make your slugs translatable using WPML Hooks:

$book_slug = apply_filters('wpml_translate_single_string', 'books', 'Custom Slugs', 'Book CPT Slug');
$category_slug = apply_filters('wpml_translate_single_string', 'book-category', 'Custom Slugs', 'Book Category Slug');

Then use:

'rewrite' => array('slug' => $book_slug . '/%book_category%'),

I have updated my earlier provided snippet, which now works as expected.

See post with working links here:
hidden link

This should clarify that what occurs on your site is related to your custom theme and your custom code and this is not a bug that I can confirm on a WPML test site.

It’s more a nuanced edge case where custom rewrite structures (like using %taxonomy% placeholders) interact with WordPress's internal rewrite rules and WPML's translation layer in subtle, sometimes unpredictable ways.

July 2, 2025 at 7:38 am #17191141

nicolasG-15

I'll try your solution, but I don't think it explains the change in behavior since the plugin was updated. As I said, it worked (and still works) with WPML 4.6.13. It's not my code that modifies the link when saving a post (I don't use any save_post hook)

In any case, the link in the href attribute shouldn't be modified, it should be as the user enters it in the editor.

I'll test your solution on my development environment and get back to you.

July 2, 2025 at 7:51 am #17191326

Andreas W.
WPML Supporter since 12/2018

Languages: English (English ) Spanish (Español ) German (Deutsch )

Timezone: America/Lima (GMT-05:00)

Ok, I truly understand your concerns.

I will run another test on a new test site using your example and testing with different WPML versions.

I will follow up with you about this once I am able to run those tests.

July 2, 2025 at 8:29 am #17191439

Andreas W.
WPML Supporter since 12/2018

Languages: English (English ) Spanish (Español ) German (Deutsch )

Timezone: America/Lima (GMT-05:00)

I noticed a potential issue with your current approach: both the CPT archive and the taxonomy base are using the slug "recettes", which isn't recommended in WordPress as it can cause conflicts in rewrite rules and lead to unexpected behaviors.

For context, my own working example uses a different slug for the taxonomy base to avoid this overlap.

Also, when I tested your current registration setup in a sandbox, the default permalink for a recipe post resulted in:

hidden link

...which leads to a 400 Bad Request error, likely due to the %recipe_category% placeholder not being resolved properly.

One-Click-Login:
hidden link

I also noticed custom utilities like translate-archive-recipe-url.php and get-translated-post.php. At this point, it seems we’re diving into deeply theme-specific logic, which is outside the scope of support.

WPML Support Policy:
https://wpml.org/purchase/support-policy/

I’d kindly ask you to take another look at the working solution I provided. It might offer a more maintainable approach moving forward.