Aller au contenu

Spécifications Techniques - Architecture Multilingue Meristheme

Date : 21 Octobre 2025 Responsable technique : Fenohery Développeur : Fenohery Validateur : Andrianina


🏛️ Architecture Technique Globale

Configuration CMS

  • Plateforme : [WordPress/Drupal/Custom] - À définir
  • Version PHP : 8.1 minimum
  • Base de données : MySQL 8.0+ / MariaDB 10.6+
  • Hébergement : [À spécifier selon client]

Plugin Multilingue

  • Solution sélectionnée : WPML Pro (WordPress Multilingual Plugin)
  • Version requise : WPML CMS Nav + String Translation + Media Translation
  • Coût estimé : ~\(79-\)299 (selon licence choisie)
  • Configuration : Mode de traduction manuelle avec gestion complète
  • Support : Documentation officielle WPML + support technique inclus

🗺️ Configuration URLs Multilingues

Structure URLs Recommandée

Structure par sous-répertoires (SEO optimal) :
- Français : https://meristheme.com/fr/
- Anglais International : https://meristheme.com/en/

Configuration Nginx/Apache pour WPML

# Nginx - Redirections WPML automatiques
location ~ ^/fr|en {
    try_files $uri $uri/ /index.php?$args;
}

# Redirection langue par défaut WPML (détection navigateur)
if ($http_accept_language ~* ^fr) {
    return 301 /fr$request_uri;
}
if ($http_accept_language ~* ^en) {
    return 301 /en$request_uri;
}

Sitemap Multilingue

  • Format : XML sitemap hreflang
  • Génération : Automatique via plugin
  • Soumission : Google Search Console
  • Fréquence : Mise à jour automatique

⚙️ Configuration WPML Pro

Installation et Activation WPML

# Téléchargement WPML Pro depuis le site officiel
# Upload via WordPress admin ou FTP
# Installation automatique recommandée
wp plugin install wpml-multilingual-cms
wp plugin activate wpml-multilingual-cms

# Modules complémentaires WPML
wp plugin install wpml-string-translation
wp plugin install wpml-translation-management
wp plugin install wpml-media-translation
wp plugin activate wpml-string-translation
wp plugin activate wpml-translation-management
wp plugin activate wpml-media-translation

Configuration Initiale WPML

  1. Langues actives : FR (par défaut), EN (Anglais International)
  2. URL mode : Sous-répertoires (/fr/, /en/)
  3. Redirection automatique : Activée (basée sur navigateur)
  4. Sélecteur langue : Menu navigation + Footer (widget WPML)
  5. Mode traduction : Traduction manuelle avec Translation Editor

Paramètres Avancés WPML

// wp-config.php - Configuration WPML
define('WPML_LANGUAGE_NEGOTIATION_TYPE', '3'); // URL-based
define('WPML_STICKY_LANGUAGES', true);
define('WPML_SHOW_TRANSLOADED', true);
define('WPML_TM_EDITOR_TEAM', '1'); // Mode traduction manuelle
define('WPML_MEDIA_VERSION', '2'); // Support médias multilingues

Modules WPML Requis

  1. WPML Multilingual CMS - Core plugin
  2. WPML String Translation - Traduction des chaînes de texte
  3. WPML Translation Management - Gestion des traductions
  4. WPML Media Translation - Support médias multilingues

Sélecteur Principal (Header)

<nav class="main-navigation">
    <div class="language-selector">
        <button class="current-lang" aria-expanded="false">
            <span class="flag-icon flag-fr"></span>
            <span>Français</span>
            <svg class="chevron-down">...</svg>
        </button>
        <ul class="language-dropdown">
            <li><a href="/fr/" hreflang="fr" class="active">
                <span class="flag-icon flag-fr"></span>Français
            </a></li>
            <li><a href="/en/" hreflang="en">
                <span class="flag-icon flag-en"></span>English
            </a></li>
            <li><a href="/es/" hreflang="es">
                <span class="flag-icon flag-es"></span>Español
            </a></li>
            <li><a href="/de/" hreflang="de">
                <span class="flag-icon flag-de"></span>Deutsch
            </a></li>
            <li><a href="/it/" hreflang="it">
                <span class="flag-icon flag-it"></span>Italiano
            </a></li>
        </ul>
    </div>
</nav>
<footer class="site-footer">
    <div class="language-switcher">
        <span>Disponible en :</span>
        <a href="/fr/" hreflang="fr">Français</a>
        <a href="/en/" hreflang="en">English</a>
        <a href="/es/" hreflang="es">Español</a>
        <a href="/de/" hreflang="de">Deutsch</a>
        <a href="/it/" hreflang="it">Italiano</a>
    </div>
</footer>

CSS Sélecteur Langue

.language-selector {
    position: relative;
    margin: 0 20px;
}

.language-dropdown {
    position: absolute;
    top: 100%;
    right: 0;
    background: white;
    border: 1px solid #ddd;
    border-radius: 4px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    z-index: 1000;
    min-width: 150px;
}

.language-dropdown a {
    display: flex;
    align-items: center;
    padding: 8px 12px;
    text-decoration: none;
    color: #333;
    transition: background-color 0.2s;
}

.language-dropdown a:hover,
.language-dropdown a.active {
    background-color: #f5f5f5;
    color: #0073aa;
}

.flag-icon {
    width: 18px;
    height: 12px;
    margin-right: 8px;
    background-size: cover;
}

🔄 Redirections Automatiques

Logique de Redirection

// Fonction détection langue navigateur
function detect_user_language() {
    $accept_lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
    $languages = ['fr', 'en', 'es', 'de', 'it'];

    foreach ($languages as $lang) {
        if (strpos($accept_lang, $lang) === 0) {
            return $lang;
        }
    }

    return 'fr'; // Langue par défaut
}

// Redirection automatique première visite
function redirect_to_language() {
    if (!isset($_COOKIE['visited_language'])) {
        $detected_lang = detect_user_language();
        $current_url = $_SERVER['REQUEST_URI'];

        if ($detected_lang !== 'fr' && strpos($current_url, '/') === 0) {
            wp_redirect("/$detected_lang$current_url", 302);
            exit;
        }

        setcookie('visited_language', 'true', time() + (86400 * 30), '/');
    }
}
add_action('template_redirect', 'redirect_to_language');

Gestion Cookies Langue

// JavaScript - Gestion préférence langue
class LanguageManager {
    constructor() {
        this.currentLang = document.documentElement.lang;
        this.cookieName = 'meristheme_language';
        this.init();
    }

    init() {
        this.setupLanguageSwitcher();
        this.detectLanguageChange();
        this.setLanguageCookie();
    }

    setupLanguageSwitcher() {
        document.querySelectorAll('.language-dropdown a').forEach(link => {
            link.addEventListener('click', (e) => {
                e.preventDefault();
                const newLang = this.extractLanguageFromURL(link.href);
                this.switchLanguage(newLang);
            });
        });
    }

    switchLanguage(lang) {
        // Sauvegarder préférence
        document.cookie = `${this.cookieName}=${lang}; path=/; max-age=31536000`;

        // Rediriger vers nouvelle langue
        const currentPath = window.location.pathname;
        const newPath = this.buildNewPath(currentPath, lang);

        window.location.href = newPath;
    }

    buildNewPath(currentPath, newLang) {
        // Remplacer ou ajouter langue dans l'URL
        const langRegex = /^\/(fr|en|es|de|it)\//;

        if (langRegex.test(currentPath)) {
            return currentPath.replace(langRegex, `/${newLang}/`);
        } else {
            return `/${newLang}${currentPath}`;
        }
    }

    setLanguageCookie() {
        if (!document.cookie.includes(this.cookieName)) {
            document.cookie = `${this.cookieName}=${this.currentLang}; path=/; max-age=31536000`;
        }
    }
}

// Initialisation
document.addEventListener('DOMContentLoaded', () => {
    new LanguageManager();
});

📝 Intégration Contenu Traduit

Structure Base de Données Multilingue

-- Table wp_posts multilingue
ALTER TABLE wp_posts
ADD COLUMN post_language VARCHAR(10) DEFAULT 'fr',
ADD COLUMN post_translation_parent INT(11) DEFAULT NULL,
ADD INDEX idx_language (post_language),
ADD INDEX idx_translation_parent (post_translation_parent);

-- Table wp_postmeta multilingue
ALTER TABLE wp_postmeta
ADD COLUMN meta_language VARCHAR(10) DEFAULT 'fr',
ADD INDEX idx_meta_language (meta_language);

-- Table des traductions
CREATE TABLE wp_icl_translations (
    translation_id INT(11) AUTO_INCREMENT PRIMARY KEY,
    element_type VARCHAR(36) NOT NULL,
    element_id INT(11) NOT NULL,
    language_code VARCHAR(7) NOT NULL,
    source_language_code VARCHAR(7),
    trid INT(11) NOT NULL,
    UNIQUE KEY unique_translation (element_type, element_id, language_code),
    INDEX trid_lang (trid, language_code),
    INDEX element_type_id (element_type, element_id)
);

Fonctions d'Extraction Contenu

class MultilingualContent {
    private $current_lang;

    public function __construct() {
        $this->current_lang = $this->getCurrentLanguage();
    }

    public function getTranslatedPost($post_id) {
        global $wpdb;

        $translated_id = $wpdb->get_var($wpdb->prepare(
            "SELECT element_id
             FROM wp_icl_translations
             WHERE element_type = 'post_post'
             AND trid = (
                 SELECT trid FROM wp_icl_translations
                 WHERE element_id = %d AND element_type = 'post_post'
             )
             AND language_code = %s",
            $post_id, $this->current_lang
        ));

        return $translated_id ? get_post($translated_id) : get_post($post_id);
    }

    public function getTranslatedMeta($post_id, $meta_key) {
        $translated_post = $this->getTranslatedPost($post_id);
        return get_post_meta($translated_post->ID, $meta_key, true);
    }

    public function getCurrentLanguage() {
        // Détection depuis URL
        $path = $_SERVER['REQUEST_URI'];
        if (preg_match('/^\/(fr|en|es|de|it)\//', $path, $matches)) {
            return $matches[1];
        }

        // Détection depuis cookie
        if (isset($_COOKIE['meristheme_language'])) {
            return $_COOKIE['meristheme_language'];
        }

        // Détection depuis navigateur
        return $this->detectBrowserLanguage();
    }
}

🎨 Templates Multilingues

Structure Thème Multilingue

/wp-content/themes/meristheme/
├── languages/
│   ├── meristheme-fr_FR.po
│   ├── meristheme-en_US.po
│   ├── meristheme-es_ES.po
│   ├── meristheme-de_DE.po
│   └── meristheme-it_IT.po
├── template-parts/
│   ├── header-multilingual.php
│   ├── navigation-multilingual.php
│   └── footer-multilingual.php
├── single-multilingual.php
├── page-multilingual.php
└── functions-multilingual.php

Template Header Multilingue

<?php // header-multilingual.php ?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
    <meta charset="<?php bloginfo('charset'); ?>">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="profile" href="https://gmpg.org/xfn/11">

    <?php wp_head(); ?>

    <!-- Hreflang tags pour SEO -->
    <?php $this_page_id = get_the_ID(); ?>
    <link rel="alternate" hreflang="fr" href="<?php echo get_permalink_in_language($this_page_id, 'fr'); ?>">
    <link rel="alternate" hreflang="en" href="<?php echo get_permalink_in_language($this_page_id, 'en'); ?>">
    <link rel="alternate" hreflang="es" href="<?php echo get_permalink_in_language($this_page_id, 'es'); ?>">
    <link rel="alternate" hreflang="de" href="<?php echo get_permalink_in_language($this_page_id, 'de'); ?>">
    <link rel="alternate" hreflang="it" href="<?php echo get_permalink_in_language($this_page_id, 'it'); ?>">
</head>

<body <?php body_class(); ?>>
<?php wp_body_open(); ?>

<header id="masthead" class="site-header">
    <?php get_template_part('template-parts/navigation-multilingual'); ?>
</header>

<main id="primary" class="site-main">

Fonctions de Support Multilingue

// functions-multilingual.php
function get_permalink_in_language($post_id, $language) {
    global $wpdb;

    $translated_id = $wpdb->get_var($wpdb->prepare(
        "SELECT element_id
         FROM wp_icl_translations
         WHERE element_type = 'post_post'
         AND trid = (
             SELECT trid FROM wp_icl_translations
             WHERE element_id = %d AND element_type = 'post_post'
         )
         AND language_code = %s",
        $post_id, $language
    ));

    return get_permalink($translated_id);
}

function get_the_title_multilingual($post_id = null) {
    $post = get_post($post_id);
    $multilingual = new MultilingualContent();
    $translated_post = $multilingual->getTranslatedPost($post->ID);
    return get_the_title($translated_post);
}

function get_the_content_multilingual($post_id = null) {
    $post = get_post($post_id);
    $multilingual = new MultilingualContent();
    $translated_post = $multilingual->getTranslatedPost($post->ID);
    return apply_filters('the_content', $translated_post->post_content);
}

// Fonction traduction chaînes de caractères
function __meristheme($string, $domain = 'meristheme') {
    return __($string, $domain);
}

function _e_meristheme($string, $domain = 'meristheme') {
    _e($string, $domain);
}

🔍 Optimisation SEO Multilingue

Configuration hreflang Automatique

function add_hreflang_tags() {
    if (is_singular()) {
        $languages = ['fr', 'en', 'es', 'de', 'it'];
        $post_id = get_the_ID();

        foreach ($languages as $lang) {
            $permalink = get_permalink_in_language($post_id, $lang);
            echo '<link rel="alternate" hreflang="' . esc_attr($lang) . '" href="' . esc_url($permalink) . '">' . "\n";
        }

        // URL canonique
        echo '<link rel="canonical" href="' . esc_url(get_permalink()) . '">' . "\n";
    }
}
add_action('wp_head', 'add_hreflang_tags');

Sitemap Multilingue XML

function generate_multilingual_sitemap() {
    $languages = ['fr', 'en', 'es', 'de', 'it'];
    $posts = get_posts(['post_type' => 'page', 'numberposts' => -1]);

    $xml = '<?xml version="1.0" encoding="UTF-8"?>';
    $xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';

    foreach ($posts as $post) {
        foreach ($languages as $lang) {
            $permalink = get_permalink_in_language($post->ID, $lang);
            $mod_date = get_the_modified_date('Y-m-d', $post->ID);

            $xml .= '<url>';
            $xml .= '<loc>' . esc_url($permalink) . '</loc>';
            $xml .= '<lastmod>' . esc_html($mod_date) . '</lastmod>';
            $xml .= '<changefreq>weekly</changefreq>';
            $xml .= '<priority>0.8</priority>';
            $xml .= '</url>';
        }
    }

    $xml .= '</urlset>';

    return $xml;
}

📱 Tests et Validation Technique

Checklist Tests Multilingue

  • Installation plugin réussie sans erreur
  • Configuration langues activées correctement
  • URLs multilinges accessibles et formatées
  • Redirections automatiques fonctionnelles
  • Sélecteurs langue opérationnels (header + footer)
  • Navigation cohérente entre langues
  • Contenu traduit s'affiche correctement
  • SEO balises hreflang générées
  • Performance maintenue (< 3s)
  • Mobile responsive préservé
  • Compatibilité navigateurs (Chrome, Firefox, Safari, Edge)
  • Tests formulaires multilinges fonctionnels
  • Sitemap multilingue généré
  • Cookies langue persistants

Scripts Tests Automatisés

#!/bin/bash
# tests_multilingue.sh

echo "🧪 Tests Architecture Multilingue Meristheme"

# Test URLs accessibilité
echo "📍 Test URLs multilingues..."
languages=("fr" "en" "es" "de" "it")
base_url="https://meristheme.com"

for lang in "${languages[@]}"; do
    url="${base_url}/${lang}/"
    status=$(curl -s -o /dev/null -w "%{http_code}" "$url")
    echo "  $url: $status"

    if [ "$status" -eq 200 ]; then
        echo "  ✅ OK"
    else
        echo "  ❌ ERREUR"
    fi
done

# Test redirections
echo "🔄 Test redirections automatiques..."
for lang in "${languages[@]}"; do
    # Simuler différentes langues navigateur
    header="Accept-Language: $lang"
    redirect=$(curl -s -I -H "$header" "$base_url" | grep -i location)
    echo "  Navigateur $lang: $redirect"
done

# Performance tests
echo "⚡ Tests performance..."
for lang in "${languages[@]}"; do
    url="${base_url}/${lang}/"
    time=$(curl -s -o /dev/null -w "%{time_total}" "$url")
    echo "  $url: ${time}s"

    if (( $(echo "$time < 3.0" | bc -l) )); then
        echo "  ✅ Performance OK"
    else
        echo "  ⚠️ Performance lente"
    fi
done

echo "✅ Tests terminés"

🔧 Maintenance et Support

Tâches Maintenance Mensuelle

  • Mises à jour plugin multilinge
  • Vérification sitemap multilingue
  • Tests performance toutes langues
  • Validation redirections automatiques
  • Backup configuration multilingue
  • Monitoring erreurs 404 multilingues
  • Analyse trafic par langue

Support Post-Livraison

// Script monitoring erreurs multilingues
function log_multilingual_errors() {
    if (is_404()) {
        $requested_url = $_SERVER['REQUEST_URI'];
        $referer = $_SERVER['HTTP_REFERER'] ?? 'Direct';

        error_log("Multilingual 404: $requested_url from $referer");

        // Notification email si erreur répétée
        if (get_transient('404_notification_sent') !== 'yes') {
            wp_mail(
                'support@meristheme.com',
                'Erreur 404 Multilingue Détectée',
                "URL: $requested_url\nReferer: $referer"
            );
            set_transient('404_notification_sent', 'yes', 3600);
        }
    }
}
add_action('template_redirect', 'log_multilingual_errors');

📊 Documentation API

Endpoints pour Gestion Contenu

// API REST pour récupérer contenu multilingue
add_action('rest_api_init', function () {
    register_rest_route('meristheme/v1', '/multilingual/(?P<id>\d+)', [
        'methods' => 'GET',
        'callback' => function($request) {
            $post_id = $request->get_param('id');
            $language = $request->get_param('lang') ?? 'fr';

            $multilingual = new MultilingualContent();
            $translated_post = $multilingual->getTranslatedPost($post_id);

            return [
                'id' => $translated_post->ID,
                'title' => get_the_title($translated_post),
                'content' => apply_filters('the_content', $translated_post->post_content),
                'language' => $language,
                'permalink' => get_permalink_in_language($post_id, $language)
            ];
        }
    ]);
});

Document technique créé le 21 Octobre 2025 - Spécifications complètes architecture multilingue