Skip Navigation
Originally written
June 23, 2020
Updated
June 24, 2020

When you register custom post types manually, WordPress allows you to set a distinct archive slug using:

has_archive

As of version 3.2, WPML allows has_archive to be set to a string. However, if the string value is set to something other than rewrite['slug'], or the CPT name slug is not set, the archive slug will not be translatable.

Example code 1:

In this code example, we set 'has_archive' => true. Assuming that the slug book has been translated to libro in Spanish, the archive links are in the following:

  • English (default language): http://mydomain.tld/book/
  • Spanish (second language): http://mydomain.tld/es/libro/

Translating Custom Post Type Archives

When you register custom post types manually, WordPress allows you to set a distinct archive slug using:

has_archive

As of version 3.2, WPML allows has_archive to be set to a string. However, if the string value is set to something other than rewrite['slug'], or the CPT name slug is not set, the archive slug will not be translatable.

Example code 1:

In this code example, we set 'has_archive' => true. Assuming that the slug book has been translated to libro in Spanish, the archive links are in the following:

  • English (default language): http://mydomain.tld/book/
  • Spanish (second language): http://mydomain.tld/es/libro/
	 	 
add_action( 'init', 'create_post_type' );	 	 
function create_post_type() {	 	 
   register_post_type( 'book',	 	 
      array(	 	 
         'labels' => array(	 	 
            'name' => __( 'Books', 'textdomain' ),	 	 
            'singular_name' => __( 'Book', 'textdomain' )	 	 
         ),	 	 
         'public' => true,	 	 
         'has_archive' => true,	 	 
         'publicly_queryable' => true,	 	 
         'exclude_from_search' => false,	 	 
         'show_ui' => true,	 	 
         'show_in_menu' => true,	 	 
         'query_var' => true,	 	 
         'rewrite' => array('slug' => 'book'),	 	 
         'supports' => array('title','editor', 'custom-fields','thumbnail')	 	 
      )	 	 
   );	 	 
}	 	 

Example code 2:

In this code example, we set a specific string for the parameter 'has_archive' => 'my-books'. Now the archive links are as follows:

  • English (default language): http://mydomain.tld/my-books/
  • Spanish (second language): http://mydomain.tld/es/my-books/
	 	 
add_action( 'init', 'create_post_type' );	 	 
function create_post_type() {	 	 
   register_post_type( 'book',	 	 
      array(	 	 
         'labels' => array(	 	 
            'name' => __( 'Books', 'textdomain' ),	 	 
            'singular_name' => __( 'Book', 'textdomain' )	 	 
         ),	 	 
         'public' => true,	 	 
         'has_archive' => 'my-books', // Do not use the gettext functions for this parameter	 	 
         'publicly_queryable' => true,	 	 
         'exclude_from_search' => false,	 	 
         'show_ui' => true,	 	 
         'show_in_menu' => true,	 	 
         'query_var' => true,	 	 
         'rewrite' => array('slug' => 'book'),	 	 
         'supports' => array('title','editor', 'custom-fields','thumbnail')	 	 
      )	 	 
   );	 	 
}	 	 

Troubleshooting customizable slugs

Some themes and plugins allow the site administrator to set custom slugs for their custom post types and custom taxonomies.

If your translated custom slugs are returning a 404 error, please check the following:

  • The custom type should be always registered with the same slug across all requests: per language, frontend, backend, AJAX, heartbeat, etc. So in 'rewrite' => [ 'slug' => $my_custom_slug ], the value of $my_custom_slug should not change from one request to another even if it comes from an option (or any custom setting).
  • WPML supports only custom slug translation (when the custom type is registered) but not custom permalink structure.
  • If the custom slug is stored as an option, it should not be registered as an admin-texts in the wpml-config.xml file.
  • After updating the value of a custom slug, the theme/plugin should also flush the rewrite rules (by calling flush_rewrite_rules();).