Refactoring : sécurité (XSS), découpage en modules inc/* et js/admin/*, IDs résolus par slug, perf (caches, cron Gravatar, assets auto-hébergés), tests

This commit is contained in:
2026-06-10 21:30:25 +02:00
parent e6b73df516
commit 9280c3b9ce
44 changed files with 3209 additions and 2907 deletions

120
inc/i18n.php Normal file
View File

@@ -0,0 +1,120 @@
<?php
/**
* Multilingue maison (remplace Polylang).
*
* Détection de langue par préfixe /en/, champs bilingues "FR // EN",
* préfixage des URLs internes, traduction des noms de catégories,
* filtres Twig (|bilingual, |en_url, |cat_name) et sélecteur de langue.
*/
// ── Language detection (replaces Polylang) ───────────────────────
define( 'THALIM_ORIGINAL_URI', $_SERVER['REQUEST_URI'] ?? '/' );
function thalim_current_language(): string {
if ( isset( $GLOBALS['thalim_lang_override'] ) ) return $GLOBALS['thalim_lang_override'];
$uri = THALIM_ORIGINAL_URI;
return ( strpos( $uri, '/en/' ) === 0 || rtrim( $uri, '/' ) === '/en' ) ? 'en' : 'fr';
}
// Strip /en/ prefix before WordPress resolves the URL
add_filter( 'do_parse_request', function ( $do_parse ) {
$uri = THALIM_ORIGINAL_URI;
if ( strpos( $uri, '/en/' ) === 0 ) {
$_SERVER['REQUEST_URI'] = substr( $uri, 3 );
} elseif ( rtrim( $uri, '/' ) === '/en' ) {
$_SERVER['REQUEST_URI'] = '/';
}
return $do_parse;
}, 1 );
// Prevent WP canonical redirect from overriding /en/ URLs
add_filter( 'redirect_canonical', function ( $redirect ) {
if ( strpos( THALIM_ORIGINAL_URI, '/en/' ) === 0 ) return false;
return $redirect;
}, 10, 2 );
// Split "FR // EN" bilingual fields — returns the right part for the given lang
function thalim_bilingual( string $value, string $lang = null ): string {
if ( $lang === null ) $lang = thalim_current_language();
if ( strpos( $value, ' // ' ) === false ) return $value;
$parts = explode( ' // ', $value, 2 );
return ( $lang === 'en' && trim( $parts[1] ?? '' ) !== '' ) ? trim( $parts[1] ) : trim( $parts[0] );
}
// Prepend /en to any internal URL when current language is EN.
// Idempotent: safe to call multiple times on the same URL.
function thalim_en_url( string $url ): string {
if ( thalim_current_language() !== 'en' ) return $url;
$home = rtrim( home_url(), '/' );
$path = substr( $url, strlen( $home ) );
if ( str_starts_with( $path, '/en/' ) || $path === '/en' ) return $url;
return $home . '/en' . $path;
}
// Auto-prefix all WP-generated internal URLs in EN mode.
// Safe on admin: THALIM_ORIGINAL_URI starts with /wp-admin/ there,
// so thalim_current_language() returns 'fr' and thalim_en_url() is a no-op.
add_filter( 'term_link', 'thalim_en_url' ); // get_term_link(), get_category_link(), get_tag_link()
add_filter( 'post_link', 'thalim_en_url' ); // get_permalink() on regular posts
add_filter( 'page_link', 'thalim_en_url' ); // get_permalink() on pages
add_filter( 'post_type_link', 'thalim_en_url' ); // get_permalink() on CPTs
add_filter( 'author_link', 'thalim_en_url' ); // get_author_posts_url()
// Return the translated category name if viewing in EN and titre_anglais is set.
// Accepts a WP_Term, Timber\Term, term_id (int), or a name string + term_id.
function thalim_cat_name( $cat, string $lang = null ): string {
if ( $lang === null ) $lang = thalim_current_language();
if ( is_object( $cat ) ) {
$term_id = $cat->term_id ?? ( $cat->id ?? 0 );
$fallback = $cat->name;
} elseif ( is_numeric( $cat ) ) {
$term_id = (int) $cat;
$term = get_term( $term_id, 'category' );
$fallback = $term && ! is_wp_error( $term ) ? $term->name : (string) $cat;
} else {
return (string) $cat;
}
if ( $lang !== 'en' ) return $fallback;
$en = get_term_meta( $term_id, 'titre_anglais', true );
return ( $en !== '' && $en !== false ) ? $en : $fallback;
}
// Register bilingual and en_url as Twig filters
add_filter( 'timber/twig', function ( $twig ) {
$twig->addFilter( new \Twig\TwigFilter( 'bilingual', 'thalim_bilingual' ) );
$twig->addFilter( new \Twig\TwigFilter( 'en_url', 'thalim_en_url' ) );
$twig->addFilter( new \Twig\TwigFilter( 'cat_name', 'thalim_cat_name' ) );
return $twig;
} );
// Language switcher data (replaces pll_the_languages)
// Output matches the structure header.twig expects: slug, url, current_lang
function thalim_language_switcher(): array {
$uri = THALIM_ORIGINAL_URI;
$path = parse_url( $uri, PHP_URL_PATH ) ?? '/';
$query = ( $q = parse_url( $uri, PHP_URL_QUERY ) ) ? '?' . $q : '';
$is_en = thalim_current_language() === 'en';
$fr_path = $is_en ? ( substr( $path, 3 ) ?: '/' ) : $path;
$en_path = $is_en ? $path : '/en' . $path;
return [
'fr' => [ 'slug' => 'fr', 'url' => home_url( $fr_path ) . $query, 'current_lang' => ! $is_en ],
'en' => [ 'slug' => 'en', 'url' => home_url( $en_path ) . $query, 'current_lang' => $is_en ],
];
}
// Apply bilingual split to the browser tab title + translate category names
add_filter('document_title_parts', function ($title_parts) {
if (!empty($title_parts['title'])) {
$title_parts['title'] = thalim_bilingual($title_parts['title']);
// On category archives, replace the title with the translated category name
if ( is_category() ) {
$cat = get_queried_object();
if ( $cat ) {
$title_parts['title'] = thalim_cat_name( $cat );
}
}
}
return $title_parts;
});