Contenu principal

Des slugs multi-langues pour vos types de contenu personnalisés

Les slugs des types de contenu personnalisés ? Un slug est une partie d’une url. Par exemple dans https://www.screenfeed.fr/projets/, projets est le slug de la page d’archive de mon CPT « Portfolio ». Ce slug est normalement unique pour chaque CPT.

Lorsque l’on a un site multilingue, on aimerait peut-être que ces slugs soient traduits selon la langue affichée. Par exemple, projets en français deviendrait projects en anglais. WordPress ne propose pas cette mécanique, ou du moins, on peut faire la moitié du chemin avec une petite astuce mais ce slug ne changera pas selon la langue. Ici nous allons voir comment internationaliser le slug de la page d’archive de notre CPT, ainsi que celui des pages single.

Nota : j’ai testé cette technique seulement sur un site, utilisant le plugin WPML, elle est donc peut-être imparfaite et pourrait demander des corrections en cas de bug. De plus, le code est spécifique à WPML, puisque celui-ci ajoute ses règles de réécriture. Dans tous les cas, elle représente un bon point de départ.

Alors comment faire ?
D’abord il nous faut connaitre les slugs d’origine, actuellement utilisés par le CPT. Ça, c’est simple, il suffit de se rendre sur l’archive de notre CPT, ainsi que dans une single et relever leur url.

Je vais garder l’exemple d’un CPT portfolio et je vais partir de l’hypothèse que nos modifications seront au niveau du thème courant. On aurait donc ceci : projects pour l’archive, et project pour la single.
Ensuite, il nous faut internationaliser ces slugs grâce aux fichiers .po et .mo du thème.
Pour la traduction française ça donnerait ça :

#: Fichier fr_FR.po
msgid  "projects"
msgstr "projets"

msgid  "project"
msgstr "projet"

Ensuite ? Il ne nous reste qu’à ajouter une fonction dans le fichier functions.php du thème.
Le hook que nous allons utiliser est parse_request car nous devons intervenir avant que WordPress n’aille chercher les posts en base de donnée ou ne renvoie une 404.
D’abord nous allons ranger nos slugs dans des tableaux. Le bon point ici c’est que l’on pourra y ranger les slugs de plusieurs CPT.

01020304050607080910111213

add_filter('parse_request', 'sf_cpts_multilang_slugs');
function sf_cpts_multilang_slugs( $wp ) {
	// Slugs
	$archive_slugs = array(
		'portfolio'	=> __('projects', 'mon-domaine'),
		// En ajouter d'autres...
	);
	$single_slugs = array(
		'portfolio'	=> __('project', 'mon-domaine'),
		// En ajouter d'autres...
	);
	// ...
}

Occupons-nous d’abord de la page d’archive.
Nous allons modifier le paramètre $wp. $wp est un object de la classe WP, comportant plusieurs propriétés (Captain Obvious approuve). Si l’on se rend sur la page d’archive en anglais, $wp a ceci comme valeur (ce résultat est dépendant de WPML, nous aurions autre-chose avec un autre plugin) :

WP Object
(
    [public_query_vars] => Array
        (
            [0] => m
            [1] => p
            // etc.
        )

    [private_query_vars] => Array
        (
            [0] => offset
            [1] => posts_per_page
            // etc.
        )

    [extra_query_vars] => Array
        (
        )

    [query_vars] => Array
        (
            [post_type] => portfolio
        )

    [query_string] => 
    [request] => en/projects
    [matched_rule] => en/projects/?$
    [matched_query] => post_type=portfolio
    [did_permalink] => 1
)

C’est principalement $wp->query_vars qui va nous intéresser.
Si l’on se rend sur l’archive française, on a alors ceci :

WP Object
(
    [public_query_vars] => Array
        (
            // etc.
        )

    [private_query_vars] => Array
        (
            // etc.
        )

    [extra_query_vars] => Array
        (
        )

    [query_vars] => Array
        (
            [page] =>
            [name] => projets
        )

    [query_string] => 
    [request] => projets
    [matched_rule] => ([^/]+)(/[0-9]+)?/?$
    [matched_query] => name=projets&page=
    [did_permalink] => 1
)

Sur une installation sans WPML nous aurions une 404. Ici, il semblerait que ce soit à cause de WPML, on nous retourne une page (qui n’existe pas o_O).
Bref, il nous faut la même chose que pour la page anglaise. En gros, on récupère les infos qu’on nous donne et on les modifie de telle façon à retourner ce qu’il faut.

01020304050607080910111213141516171819202122

add_filter('parse_request', 'sf_cpts_multilang_slugs');
function sf_cpts_multilang_slugs( $wp ) {
	// Slugs
	$archive_slugs = array(
		'portfolio'	=> __('projects', 'mon-domaine'),
		// En ajouter d'autres...
	);
	$single_slugs = array(
		'portfolio'	=> __('project', 'mon-domaine'),
		// En ajouter d'autres...
	);

	// Archives slugs
	if ( isset($wp->query_vars['page'], $wp->query_vars['name']) && in_array( $wp->query_vars['name'], $archive_slugs ) ) {
		$wp->query_vars['post_type']	= array_search($wp->query_vars['name'], $archive_slugs);
		$wp->matched_rule		= str_replace('([^/]+)(/[0-9]+)?', $wp->query_vars['name'], $wp->matched_rule);
		$wp->matched_query		= 'post_type=' . $wp->query_vars['post_type'];
		unset($wp->query_vars['page'], $wp->query_vars['name']);
		return;
	}
	// ...
}

Passons maintenant aux pages single. Alors là on part un peu plus dans le n’importe quoi puisque cette fois, $wp->query_vars va nous retourner… un attachement o_O.
Donc au lieu d’avoir ceci (oui, on retrouve bizarrement des paramètres correspondant à une page) :

WP Object
(
    // etc
    [query_vars] => Array
        (
            [page] =>
            [portfolio] => mon-dernier-projet
            [post_type] => portfolio
            [name] => mon-dernier-projet
        )

    [query_string] => 
    [request] => projet/mon-dernier-projet
    [matched_rule] => projet/([^/]+)(/[0-9]+)?/?$
    [matched_query] => portfolio=mon-dernier-projet&page=
    [did_permalink] => 1
)

On aura :

WP Object
(
    // etc
    [query_vars] => Array
        (
            [attachment] => mon-dernier-projet
        )

    [query_string] => 
    [request] => projet/mon-dernier-projet
    [matched_rule] => peut importe, on va le virer
    [matched_query] => attachment=mon-dernier-projet
    [did_permalink] => 1
)

Donc là ça va être un peu plus compliqué car on n’a ni le post_type ni ‘projet’ dans $wp->query_vars, on va donc s’aider de $wp->request. Le code sera à peine un peu plus long mais l’idée reste la même.

01020304050607080910111213141516171819202122232425262728293031323334353637383940414243

add_filter('parse_request', 'sf_cpts_multilang_slugs');
function sf_cpts_multilang_slugs( $wp ) {
	// Slugs
	$archive_slugs = array(
		'portfolio'	=> __('projects', 'mon-domaine'),
		// En ajouter d'autres...
	);
	$single_slugs = array(
		'portfolio'	=> __('project', 'mon-domaine'),
		// En ajouter d'autres...
	);

	// Archives slugs
	if ( isset($wp->query_vars['page'], $wp->query_vars['name']) && in_array( $wp->query_vars['name'], $archive_slugs ) ) {
		$wp->query_vars['post_type']	= array_search($wp->query_vars['name'], $archive_slugs);
		$wp->matched_rule		= str_replace('([^/]+)(/[0-9]+)?', $wp->query_vars['name'], $wp->matched_rule);
		$wp->matched_query		= 'post_type=' . $wp->query_vars['post_type'];
		unset($wp->query_vars['page'], $wp->query_vars['name']);
		return;
	}

	// Single pages slugs
	if ( !empty($wp->query_vars['attachment']) ) {
		$single_slug = false;
		// On va récupérer $post_type (le post_type) et $single_slug (le slug de la page single)
		foreach ( $single_slugs as $post_type => $slug ) {
			if ( strpos($wp->request, '/'.$slug.'/') !== false || strpos($wp->request, $slug.'/') === 0 ) {
				$single_slug = $slug;
				break;
			}
		}
		if ( $single_slug ) {
			$wp->query_vars[$post_type]	= $wp->query_vars['attachment'];
			$wp->query_vars['post_type']	= $post_type;
			$wp->query_vars['page']		= '';
			$wp->query_vars['name']		= $wp->query_vars[$post_type];
			$wp->matched_rule		= str_replace($wp->query_vars[$post_type], '([^/]+)(/[0-9]+)?/?$', $wp->request);
			$wp->matched_query		= $post_type . '=' . $wp->query_vars[$post_type] . '&page=';
			unset($wp->query_vars['attachment']);
			return;
		}
	}
}

Terminé o/

Donc je le répète une dernière fois, ce code diffèrera selon votre installation et les plugins que vous utilisez, le but de cet article étant principalement de vous donner une méthode qu’il faudra peut-être adapter.
En ce qui me concerne, j’ai pu avoir les url example.com/projets/ + example.com/projet/mon-dernier-projet/ et example.com/en/projects/ + example.com/en/project/my-latest-project/ fonctionnelles.

Les points positifs que j’ai trouvé à cette technique :
– ne nécessite pas de savoir combien de langues sont utilisées sur le site. Tant qu’il y a les fichiers .mo correspondants, ça marche.
– que la langue de base du site soit anglais, français, ou autre, n’a aucune importance (lors d’un flush_rewrite_rules() par exemple), ce code fonctionnant dans tous les sens.
– l’impact sur les performances est minime.

Le point noir étant ce que j’ai déjà mentionné : les propriétés de l’objet $wp vont différer selon votre configuration, n’ayant pas de solution universelle pour le moment.

See ya!