Salut à tou-te-s !!!

Après plus de trois ans d’absence, me voici de retour pour partager avec vous quelques tutos et bouts de code bien pratiques pour la création de site web 🙂

Et on démarre avec du PHP permettant de rendre du texte plus conforme aux règles de typographie françaises (consulter aussi cette page plus digeste et bien sympa sur la ponctuation et les espaces).

On pourra appliquer ces fonctions aux éléments WordPress comme les titres, extraits, contenus principaux (Gutenberg ou éditeur classique), champs personnalisés, ou encore des éléments tiers provenant de plugins (exemple avec le Rich Text d’Oxygen builder), à l’aide des crochets adéquats.

Pour des raisons de performance de code, je propose pour les deux fonctions présentées :

  • une version pour texte brut (une chaine de caractères simple, un extrait d’article WordPress ou un champ personnalisé de type texte simple) ;
  • une version pour contenu HTML (comme les contenus Gutenberg ou éditeur classique ou champ custom HTML ou Rich Text Oxygen) un peu plus gourmande.

1. Ponctuation et symboles monétaires

Pour cette première fonction, le but est de remplacer les espaces précédant certains signes de ponctuation et symboles monétaires par des espaces insécables (l’entité HTML «   ») afin que le caractère concerné ne se retrouve jamais en début de ligne (donc séparé du mot auquel il est lié).

On traitera également le cas particulier des guillemets français, puisque dans ce cas l’élément ouvrant nécessite une espace insécable APRÈS pour le lier au mot suivant.

Note : Pour les guillemets, le script ajoute l’espace s’il est manquant.

Voici le code pour la version brute (chaine de caractère sans balise HTML) :

function link_punctuation_raw( $string ) {

    return preg_replace( [ 
		'/(«|«)\s*([^\s])/u',
		'/([^\s])\s*(»|»)/u',
		'/([^\s])\s+([:;%!\?])/u',
		'/(\d)\s+(¤|¤|\$|¢|¢|£|£|¥|¥|₣|₣|€|€)/u',
	], '$1 $2', $string );

}

Et le code pour le contenu HTML, basé sur la fonction précédente :

function link_punctuation_html( $original_text ) {

	$fixed_text = '';
	$active = true;

	$original_text = preg_replace(
	    '/\r?\n?<(!--|pre|script|style)/i',
	    "\n" . '<$1',
	    $original_text );
	$original_text = preg_replace(
	    '/(--|\/pre|\/script|\/style)>\r?\n?/i',
	    '$1>' . "\n",
	    $original_text );
		
	foreach ( preg_split( '/((\r?\n)|(\r\n?))/', $original_text ) as $line ) {

		if ( preg_match( '/(--|\/pre|\/script|\/style)>/i', $line ) ) {
			$active = true;
			$fixed_text .= $line . "\n";
			continue;
		} elseif ( preg_match( '/<(!--|pre|script|style)/i', $line ) ) {
			$active = false;
			$fixed_text .= $line . "\n";
			continue;
		}
		if ( $active )
			$line = link_punctuation_raw( $line );

		$fixed_text .= $line . "\n";
	}
	
	return rtrim( $fixed_text );

}

Sans rentrer dans les détails, la première fonction effectue une substitution de chaine avec différents motifs contenant les caractères concernés, et la seconde découpe le contenu HTML multi-lignes en chaines simples, repère les blocs <script>, <pre> et <style> afin d’ignorer leur contenu, et appelle la première fonction pour la substitution des lignes restantes.

2. Mots orphelins

Cette fois on s’attache à éviter les mots qui se retrouvent seuls sur la dernière ligne d’un paragraphe, ce qui est souvent inesthétique, en liant les deux derniers mots du paragraphe avec l’espace insécable.

À utiliser judicieusement, toutefois, car dans certaines circonstances, le résultat est encore plus moche que l’original, notamment avec des fontes de grande taille et/ou des textes de faible largeur (attention aux mobiles !) :

1. Forcer l’avant dernier mot du paragraphe à la ligne peut laisser un gros blanc à la fin de l’avant-dernière ligne, voire aboutir à une dernière ligne plus longue que l’avant-dernière !

2. Lier les deux derniers mots d’un paragraphe peut parfois mener à un débordement du conteneur et parfois casser le design du site.

Vous le constaterez sur cette page en réduisant la fenêtre de votre navigateur à 200 px 😉 Mais mon site étant consulté à 90% sur desktop, et les 10% de mobiles restant ayant des écrans de plus en plus grands, je considère que c’est un compromis satisfaisant.

Et voici les deux fonctions :

function remove_orphans_raw( $string ) {
    return preg_replace(
        '@\s+([^\s]+)\s+([^\s]+)$@',
        ' $1&nbsp;$2',
        $string);
}

function remove_orphans_html( $string ) {
    return preg_replace(
        '@\s+([^\s<>\"\'\[\]]+)\s+([^\s<>\"\'\[\]]+)</(p|h1|h2|h3|h4|h5|h6|li)>@m',
        ' $1&nbsp;$2</$3>',
        $string);
}

Application dans WordPress avec les hooks

Contenus propres à WordPress

On peut tout d’abord utiliser ces fonctions pour filtrer les éléments classiques de contenu Wordpress :

$filters = [
	'the_title' => 10,
	'the_excerpt' => 10,
	// etc.
];
foreach ( $filters as $filter_name => $filter_prio ) {
	add_filter( $filter_name, 'link_punctuation_raw', $filter_prio );
	//add_filter( $filter_name, 'remove_orphans_raw', $filter_prio );
};

$filters = [
	'the_content' => 10,
	// etc.
];
foreach ( $filters as $filter_name => $filter_prio ) {
	add_filter( $filter_name, 'link_punctuation_html', $filter_prio );
	add_filter( $filter_name, 'remove_orphans_html', $filter_prio );
};

Notes :

1. On peut personnaliser dans ce code la liste des filtres à utiliser pour chaque fonction ainsi que les priorités (en cas de conflit dans l’ordre d’exécution au sein des crochets).

2. Je n’applique pas toujours la suppression des mots orphelins aux titres et extraits WP car selon les cas le résultat peut être contre productif (Cf. remarque plus haut).

Contenus provenant d’autres sources

Mais on peut également hacker d’autres éléments provenant par exemple de plugins tiers, comme ici l’élément Rich Text du constructeur de page Oxygen :

add_filter( 'do_shortcode_tag', function( $output, $tag, $attr ) {
	if( 'oxy_rich_text' != $tag )
	    return $output;
	return link_punctuation_html($output);
}, 10, 3 );

Ou encore des champs texte du plugin de formulaire FluentForm :

$filters = [
	'fluentform_rendering_field_html_input_text',
	'fluentform_rendering_field_html_textarea',
	// etc.
];
foreach ( $filters as $filter ) {
	add_filter( $filter, 'link_punctuation_raw' );
};

Limitations vs optimisation

Cette méthode « serveur » (traitements exécutés en PHP au moment de la génération de la page) a le mérite de ne pas surcharger le frontend du site avec du javascript (le résultat peut être mis en cache, la page est plus simple à optimiser).

Cependant, elle nécessite d’injecter le code dans les modules nécessaires. Or, même si les hooks WordPress permettent de toucher la plupart des éléments d’un site, tous les plugins ne fournissent pas cette possibilité. Typiquement, il ne sera pas toujours possible de filtrer toute une page construite avec un page builder.

Dans la pratique, je trouve finalement que ce n’est pas si limitant, car j’utilise au maximum les Custom Post Type pour mes contenus, donc je peux utiliser le filtre the_content. A priori, entête et pied de page ne nécessitent pas ce genre de traitement, et pour les contenus statiques restants ne fournissant pas de hooks, on pourra toujours insérer les espaces insécables à la main.

Aussi, les codes précédents ne sont pas 100% « bullet proof » : toutes les combinaisons ne peuvent pas être traitées correctement.

1. Pour le premier, plusieurs signes de ponctuation successifs ne permettent pas de traiter toutes les espaces. Exemple avec un guillemet fermant et les deux points derrière « bullet proof » ci-dessus. Seul l’espace avant le guillemet est traité.

2. Pour le second, la présence de balises HTML entre les deux derniers mots d’un paragraphe (par exemple </strong>) empêchera de lier les deux mots. C’est le cas de « traitées correctement » ci-dessus.

Il doit y avoir de nombreux autres cas similaires (n’hésitez pas à me les signaler et on verra si on peut les traiter simplement ou non), mais vouloir rendre ces codes imparables serait complètement illusoire en raison d’une combinatoire énorme qui engendrerait des expressions régulières d’une complexité décourageante !

Crédits

Ces codes sont largement inspirés de scripts trouvés sur le net, notamment pour les symboles monétaires et le découpage des contenus HTML, mais les utilisant depuis des années déjà, je n’ai pas pu retrouver les articles ou snippets originaux. Je m’en excuse et remercie chaleureusement leurs auteurs ; je les cite dès que je remets la main sur les articles 😉

Laissez le premier commentaire