This is because WordPress does not filter posts and pages by language when checking if the slug is unique.
I had same problem and just finished code as a workaround for this. Until Chouby includes this in Polylang core you can use following code in your theme functions.php or create a plugin out of it:
global $polylang;
// Check if Polylang_Base exists and if $polylang is the right object
if(is_admin() && class_exists('Polylang_Base') && $polylang instanceof Polylang_Base){
// The wp_unique_post_slug filter is applied only after WordPress checks if the slug is unique and adds suffix otherwise
// the function returns unique post slug within language
add_filter('wp_unique_post_slug', 'unique_slug_in_language', 10, 5);
function unique_slug_in_language($slug, $post_ID, $post_status, $post_type, $post_parent = 0){
global $polylang, $wpdb;
// Get language of a post
$lang = $polylang->get_post_language($post_ID);
// return the slug if Polylang does not return post language
if(empty($lang))
return $slug;
// Remove suffix if it was added
// this might remove any dash and number at the end of a slug
$new_slug = isset($_POST['new_slug'])? sanitize_title($_POST['new_slug']) : preg_replace('@-\d$@', '', $slug);
// Return slug if it was not changed
if($new_slug == $slug)
return $slug;
// prepare statements to filter by language
$join = $wpdb->prepare(" INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = ID");
$where_lang = $wpdb->prepare(" AND pll_tr.term_taxonomy_id = %d", $lang->term_id);
$hierarchical_post_types = get_post_types( array('hierarchical' => true) );
// Polylang does not translate attachements - skip if it is one
if ( 'attachment' == $post_type ) {
return $slug;
} elseif ( in_array( $post_type, $hierarchical_post_types ) ) {
// Page slugs must be unique within their own trees. Pages are in a separate
// namespace than posts so page slugs are allowed to overlap post slugs.
$check_sql = "SELECT post_name FROM $wpdb->posts
$join
WHERE post_name = %s AND post_type IN ( '" . implode( "', '", esc_sql( $hierarchical_post_types ) ) . "' ) AND ID != %d AND post_parent = %d
$where_lang
LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $new_slug, $post_ID, $post_parent ) );
if(!$post_name_check)
return $new_slug;
} else {
// Post slugs must be unique across all posts.
$check_sql = "SELECT post_name FROM $wpdb->posts
$join
WHERE post_name = %s AND post_type = %s AND ID != %d
$where_lang
LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $new_slug, $post_type, $post_ID ) );
if(!$post_name_check)
return $new_slug;
}
return $slug;
}
}
It is smart enough not to just strip the suffix added by WordPress. It really checks if there are no duplicates in post/page language and under same parent. Code is partially taken from wp-includes/post.php wp_unique_post_slug
function with some stuff from Polylang core.php.
Not pretty, but it works and I don’t know any better way to do this right now. Additions or suggestion are welcome.