I’d like to be able to have multiple permalinks for the same underlying post (for i18n reasons) – eg
http://www.example.com/my-custom-post-type/this-is-a-cool-article
http://www.example.com/mon-type-de-poste-personnalise/cest-un-article-sympa
Both pointing to the same post page. Built in WP i18n functions would deal with the language switching on the post page itself. I’m more concerned with the URL display for both SEO and end user aesthetics.
I can’t use a 301 redirect as I don’t want the browser URL changing. I’m understand how to internationalize the custom post type slug, but not the actual post slug.
I’m thinking I can save the secondary post slug in a post meta field, but I’d have to hook on somewhere in the routing to enable it – I haven’t had much luck in finding the correct place to hook? Is there a place to hook into or an easier way to deal with this?
1 Answer
The request parsing is handled in WP::parse_request()
. After that, there’s a action hook parse_request
which gives you the instance of the wp
object.
We assume that http://www.example.com/my-custom-post-type/this-is-a-cool-article
is your permalink and http://www.example.com/mon-type-de-poste-personnalise/cest-un-article-sympa
ends up in a 404. So the first thing to check in your callback should be, if the wp
object is in a error/404 state:
if ( ! empty( $wp->query_vars[ 'error' ] && 404 == $wp->query_vars[ 'error' ] ) {
//…
}
Edit: i made a mistake: it’s not sure, that $wp
status is always 404 on your alias URL. It strongly depends on the rewrite rules on your system. So you need to check for your alias cpt slug directly.
Now you have to parse the request on your own and lookup for your meta value to find the corresponding post:
if ( ! empty( $wp->query_vars[ 'error' ] && '404' == $wp->query_vars[ 'error' ] ) {
//look up for your post, this var should looks like
// 'mon-type-de-poste-personnalise/cest-un-article-sympa' in your case:
$wp->request;
//e.g.
$parts = explode( "https://wordpress.stackexchange.com/", $wp->request );
// your language slug
$slug = ( isset( $parts[ 1 ] ) ) ? $parts[ 1 ] : '';
// do your query with your meta value $slug here
}
Next thing is, to setup the $wp->query_vars
as they would look like when we would facing the original request:
if ( ! empty( $wp->query_vars[ 'error' ] && '404' == $wp->query_vars[ 'error' ] ) {
//look up for your post, this var should looks like
// 'mon-type-de-poste-personnalise/cest-un-article-sympa' in your case:
$wp->request;
//e.g.
$parts = explode( "https://wordpress.stackexchange.com/", $wp->request );
// your i18n post slug
$slug = ( isset( $parts[ 1 ] ) ) ? $parts[ 1 ] : '';
// do your query with your meta value $slug here
// and setup the WP_Post object for the matching post
$matching_post; // WP_Post
// if you don't find any post, return here!
// unset the error flag
unset( $wp->query_vars[ 'error' ] );
// your cpt slug
$cpt_slug = 'my-custom-post-type';
$wp->query_vars[ $cpt_slug ] = $matching_post->post_name;
$wp->query_vars[ 'post_type' ] = $cpt_slug;
$wp->query_vars[ 'name' ] = $matching_post->post_name;
}
The last thing you have to do is to prevent WordPress to redirect to the canonical URL automatically by removing the function redirect_canonical
from the template_redirect
action:
if ( ! empty( $wp->query_vars[ 'error' ] && '404' == $wp->query_vars[ 'error' ] ) {
//look up for your post, this var should looks like
// 'mon-type-de-poste-personnalise/cest-un-article-sympa' in your case:
$wp->request;
//e.g.
$parts = explode( "https://wordpress.stackexchange.com/", $wp->request );
// your language slug
$slug = ( isset( $parts[ 1 ] ) ) ? $parts[ 1 ] : '';
// do your query with your meta value $slug here
// and setup the WP_Post object for the matching post
$matching_post; // WP_Post
# unset the error flag
unset( $wp->query_vars[ 'error' ] );
# your cpt slug
$cpt_slug = 'my-custom-post-type';
$wp->query_vars[ $cpt_slug ] = $matching_post->post_name;
$wp->query_vars[ 'post_type' ] = $cpt_slug;
$wp->query_vars[ 'name' ] = $matching_post->post_name;
# don't redirect to the canonical url
remove_action( 'template_redirect', 'redirect_canonical' );
}
Put the last codeblock in a function and apply it to the parse_request
action:
add_action( 'parse_request', 'wpse_126309_parse_request' );
/**
* @wp-hook parse_request
* @param WP $wp
* @return void
*/
function wpse_126309_parse_request( $wp ) {
// the code goes here
}
You should test this example (it’s not more than a proof of concept) for side effects.