When you register custom post types manually, WordPress allows you to set a distinct archive slug. How you configure this determines if the slug will be translated into your secondary language or not.
When registering custom post types manually, you can set a set a distinct archive slug using the has_archive function.
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 custom post type name slug is not set, the archive slug will not be translatable.
Setting a translatable slug
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')	 	 
      )	 	 
   );	 	 
}	 	 
Setting the same slug in default and secondary languages
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_slugshould 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-textsin thewpml-config.xmlfile. - After updating the value of a custom slug, the theme/plugin should also flush the rewrite rules (by calling 
flush_rewrite_rules();).