Table des matières :
- Périmètre technique : où vivent les traductions dans PrestaShop
- hreflang : implémentation correcte et pièges du cœur
- Slugs multilingues (link_rewrite) : génération, collisions, redirections
- Métadonnées traduites : title/description, duplication, données structurées
- Automatiser la traduction : pipeline robuste (API, jobs, idempotence)
- Validation SEO : crawl, contrôles automatiques, détection des régressions
- Mise en production : garde-fous concrets (staging, rollback, checklist)
Sur PrestaShop 8.1.x et 9.1.x (Symfony 6.4) avec PHP 8.2/8.3, la « traduction » ne se limite pas à remplir name et description. Si vous visez une localisation SEO propre, il faut traiter trois couches en même temps :
- le contenu (champs
_lang), - les URL (slugs
link_rewrite+ routes), - les signaux SEO (hreflang, canonicals, métadonnées, données structurées).
Le cœur PrestaShop fournit des briques, mais l’automatisation « SEO-safe » n’existe pas out-of-the-box : vous devez compléter côté thème et/ou module, et surtout sécuriser la chaîne (unicité des slugs, redirections, réciprocité hreflang, contrôle qualité des métas).
Un bon réflexe avant de coder : poser votre matrice de ciblage (langues × pays × domaines). Par exemple, fr-FR et fr-BE peuvent partager le même contenu (donc pas besoin de fr-FR vs fr-BE), alors que fr-CA implique souvent des différences réelles (termes, devise, taxes, livraison) — et là le marquage régional devient pertinent.
Périmètre technique : où vivent les traductions dans PrestaShop
Sur un shop multi-langue, l’essentiel des contenus traduisibles est persisté dans des tables suffixées _lang, indexées par id_lang et souvent id_shop. Les plus fréquentes : ps_product_lang, ps_category_lang, ps_cms_lang, ps_manufacturer_lang et ps_supplier_lang. Le champ critique pour les URL est quasiment toujours link_rewrite (slug), tandis que les métadonnées SEO se trouvent dans meta_title, meta_description (et parfois meta_keywords, de plus en plus ignoré côté moteurs).
La localisation SEO implique aussi des tables « de contexte » : ps_lang (ISO, locale, active), ps_shop_url (domaines, chemins, SSL), et la configuration SEO (Friendly URL, Accented URL, format des routes). Si vous n’avez pas une cartographie claire de vos domaines par langue (même domaine avec préfixe /fr/ vs domaines dédiés), les hreflang que vous générez peuvent être techniquement valides… et stratégiquement faux.
Pour clarifier, documentez noir sur blanc où doit vivre chaque variante. Exemple de mapping minimal (à adapter à votre multi-boutique) :
| Cible | Langue (BCP47) | Domaine / préfixe | Devise | Remarques SEO |
|---|---|---|---|---|
| France | fr-FR |
même domaine + /fr/ |
EUR | Slugs FR, prix EUR |
| Allemagne | de-DE |
même domaine + /de/ |
EUR | Slugs DE + translittération (ä/ö/ü/ß) |
| Suisse (DE) | de-CH |
domaine CH + /de/ |
CHF | Souvent contenu/prix différents ⇒ variante distincte |
| Canada (FR) | fr-CA |
domaine CA + /fr/ |
CAD | Vocabulaire/mentions légales distinctes ⇒ variante distincte |
Ce tableau vous évite deux erreurs fréquentes : (1) créer des hreflang régionaux « décoratifs » alors que les pages sont identiques, (2) mélanger des signaux (URL FR mais prix CHF, etc.), ce qui brouille la compréhension par Google et par vos utilisateurs.
Enfin, ne sous-estimez pas les effets de bord : une mise à jour massive de *_lang sur un catalogue volumineux déclenche invalidations de cache, reconstructions d’index (recherche, facettes) et écritures InnoDB. Si vous n’avez pas déjà une méthodologie d’audit perf, commencez par cadrer l’exécution et la charge (voir la méthode reproductible de l’Audit performance PrestaShop : méthode en 6 étapes reproductibles).
Petit ajout pragmatique : avant un batch de traduction, mesurez combien de lignes vous allez réellement toucher (et donc combien d’I/O vous imposez à MySQL). Exemple :
-- Estimer le volume de produits à traduire (champ meta_title vide dans une langue cible)
SELECT COUNT(*) AS missing_meta
FROM ps_product_lang
WHERE id_shop = 1
AND id_lang = 3
AND (meta_title IS NULL OR meta_title = '');
hreflang : implémentation correcte et pièges du cœur
Le rôle de hreflang est strictement de déclarer des variantes linguistiques/régionales d’une même intention de page. C’est de l’alignement d’URLs, pas de la traduction automatique. Sur la forme, hreflang s’appuie sur les tags de langue type BCP 47 (langue + région optionnelle). La base normative est RFC 5646 :
“This document describes a language tag for use in cases where it is desirable to indicate the language used in an information object.” — RFC 5646, IETF (https://www.rfc-editor.org/rfc/rfc5646)
Pour éviter les erreurs de formats (iw au lieu de he, sous-tags invalides, etc.), gardez aussi le registre officiel des sous-tags sous la main : https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
Dans PrestaShop, le cœur expose souvent une liste d’URLs alternatives (selon contrôleur, boutique, langue active) via les variables de template, mais ça dépend du thème et du contrôleur. Sur Classic, on retrouve généralement des alternatives dans le header. Si vous avez un thème custom, vérifiez que le <head> rend bien des balises rel= »alternate » pour chaque variante de langue/boutique.
Côté Google, la documentation “versions localisées” explique le principe et les formats acceptés : https://developers.google.com/search/docs/specialty/international/localized-versions
Les pièges récurrents sur PrestaShop sont connus : (1) valeurs hreflang incorrectes (utiliser iso_code seul alors que vous avez des locales fr-FR, fr-CA), (2) non-réciprocité (A pointe vers B mais B n’a pas de retour vers A), (3) mauvais mapping multi-domaine/multi-boutique, (4) canonical contradictoire (canonical sur /fr/ mais hreflang pointe vers /en/ sans cohérence). Le point (4) est particulièrement toxique : les moteurs peuvent ignorer vos signaux si canonical/hreflang se contredisent.
Deux recommandations “terrain” qui évitent beaucoup de dégâts :
- Toujours inclure l’auto-référence : la page FR doit aussi se déclarer en
hreflang="fr-FR"(oufr) vers elle-même, pas uniquement pointer vers les autres langues. - Ajouter
x-defaultseulement si vous avez une vraie page “sélecteur” (ou page globale) ; sinon, abstenez-vous. Unx-defaultmal choisi détourne parfois des impressions vers une variante non désirée.
Mini-scenario courant en Europe : vous avez fr et de sur le même domaine, mais vous avez aussi une boutique CH séparée. Si votre thème génère les alternates “au niveau shop principal” sans tenir compte de ps_shop_url, vous pouvez produire des hreflang qui pointent vers le mauvais domaine, techniquement accessible (200) mais commercialement faux (mauvaise devise / livraison). C’est typiquement un bug discret : les pages “fonctionnent”, mais l’attribution géographique et la conversion se dégradent.
Slugs multilingues (link_rewrite) : génération, collisions, redirections
Dans PrestaShop, le slug est principalement le champ link_rewrite en table _lang. Il est par langue, et il est consommé par le générateur d’URL (Link) et les routes (legacy + Symfony). Quand link_rewrite est vide (cas fréquent après import), vous vous retrouvez avec des URLs dégradées, des routes qui retombent sur des IDs, ou des redirections implicites inconsistantes selon contrôleur. Pour comprendre précisément qui génère quoi (et à quel moment), gardez sous la main l’article interne : Génération d’URL PrestaShop : Link, routes Symfony et legacylink.
La génération automatique “naïve” (ex. Tools::str2url($name)) marche pour 80% des cas, et casse sur le reste : collisions (mêmes noms), caractères interdits, translittération incohérente, ou slugs identiques entre variantes produits. Sur un catalogue réel, la collision n’est pas rare (produits « T-shirt noir » dans plusieurs déclinaisons/collections). Vous devez donc implémenter une stratégie d’unicité déterministe : suffixe -2, suffixe basé sur id_product, ou hash court.
Points à trancher avant la génération :
- Voulez-vous des slugs “lisibles” (marketing) ou “stables” (technique) ? Les deux sont compatibles, mais la stabilité doit primer en migration.
- Acceptez-vous les URLs accentuées (
é,ç) ? PrestaShop propose une option “Accented URL”. Côté SEO, ce n’est pas un problème en soi, mais côté opérationnel (copier-coller, tracking, normalisation), cela peut compliquer. L’essentiel est d’être cohérent et d’éviter les changements ultérieurs. - Comment gérez-vous la translittération locale ? Exemple : en allemand,
ßdevient souventss,ädevientae(selon conventions), mais certains outils fonta. Le but est la prévisibilité (pour vos redirections et vos exports), pas la perfection linguistique.
Exemple simple (suffixe incrémental), à exécuter par langue et par type d’entité :
-- Détecter les slugs vides ou dupliqués pour les produits
SELECT id_lang, link_rewrite, COUNT(*) c
FROM ps_product_lang
WHERE id_shop = 1
GROUP BY id_lang, link_rewrite
HAVING link_rewrite = '' OR c > 1;
Complément utile : détecter les slugs “dangereux” (très courts, ou uniquement numériques), qui finissent souvent par créer des ambiguïtés ou des collisions lors d’une refonte de routes :
SELECT id_product, id_lang, link_rewrite
FROM ps_product_lang
WHERE id_shop = 1
AND (CHAR_LENGTH(link_rewrite) < 3 OR link_rewrite REGEXP '^[0-9-]+$')
ORDER BY id_lang, id_product
LIMIT 200;
La question que beaucoup oublient : que faire quand un slug change ? Si vous poussez une régénération automatique, vous créez des 404 et vous perdez l’historique. Le cœur a des mécanismes de redirection selon les entités (ex. champs de redirection sur certains objets), mais ce n’est pas un « redirect manager » complet. Pour une stratégie propre :
- stockez l’ancien slug (table custom type
ps_slug_historyavecid_object,object_type,id_lang,old_slug,created_at), - mettez en place une redirection 301 au niveau du contrôleur/routing (ou via un module qui intercepte la résolution) avant que PrestaShop ne rende une 404,
- et gardez un garde-fou “anti-chaînes” : une URL ne doit pas faire 301 → 301 → 200 (sinon crawl gaspillé et perte de signals).
À défaut, faites au moins une passe de logs 404 et alimentez des règles — mais cela devient vite ingérable si vous régénérez à grande échelle sans historique.
Métadonnées traduites : title/description, duplication, données structurées
Pour le SEO, traduire meta_title/meta_description n’est pas optionnel : si vous laissez la langue par défaut partout, vous fabriquez de la duplication cross-locale et vous diluez la pertinence. Sur PrestaShop, ces champs existent sur plusieurs objets (ps_product_lang, ps_category_lang, ps_cms_lang) et sont souvent utilisés par le thème pour remplir <title> et <meta name="description">. Le cœur ne “compose” pas magiquement des métas localisées ; si votre import/traduction n’écrit pas ces champs, votre HTML ne sera pas localisé.
Au-delà du “traduire”, visez des métas adaptées au marché :
- unités (cm vs inches), formats (point/virgule), termes usuels (ex. “livraison” vs “shipping”),
- mentions commerciales (ex. “TTC” en FR, plus rare ailleurs),
- et surtout une intention qui reste la même d’une variante à l’autre (sinon hreflang perd son sens).
Un cadre simple (et mesurable) pour la qualité :
| Élément | Recommandation opérationnelle | Test rapide |
|---|---|---|
meta_title |
~45–60 caractères, mot-clé + différenciant | Export + contrôle longueur |
meta_description |
~120–160 caractères, bénéfice + preuve | Déduplication (hash) |
H1 |
cohérent avec name mais pas forcément identique |
Crawl + extraction H1 |
| Données structurées | inLanguage et texte alignés à la locale |
Rich Results + inspection |
L’automatisation doit respecter un minimum de garde-fous : longueur réaliste, absence de “spintax”, et surtout contrôle qualité. Google liste explicitement les traductions automatiques sans relecture comme un exemple de pratique problématique dans ses règles anti-spam : https://developers.google.com/search/docs/essentials/spam-policies
Traduction opérationnelle : vous pouvez générer automatiquement les métas, mais prévoyez un workflow de relecture au moins sur les pages à fort trafic (top catégories, top produits). Techniquement, vous pouvez stocker un état “draft” dans une table custom, ou réutiliser des champs existants (moins propre) ; l’idée est d’éviter de publier des métas absurdes qui vont polluer vos snippets.
Sur les données structurées (JSON-LD), la localisation ne se limite pas aux textes. Vérifiez que vos templates incluent inLanguage, que les champs textuels (nom, description) sont alignés avec la langue courante, et que les prix/devise sont cohérents avec la boutique/zone. Après déploiement, testez via le Rich Results Test de Google et crawl local (voir section validation). Une incohérence classique : pages en de avec inLanguage: "en" parce que le JSON-LD est construit depuis une couche non localisée (cache HTML plein, fragment non varié par id_lang, etc.).
Automatiser la traduction : pipeline robuste (API, jobs, idempotence)
Le pattern qui tient en prod : ne pas traduire “dans le hook de sauvegarde” et ne pas faire des appels réseau depuis un back-office synchrone. À la place, vous industrialisez via (a) une commande CLI (Symfony Console), (b) un batch planifié (cron), (c) éventuellement une queue si votre infra est prête. En PrestaShop 9, construire ça proprement côté module passe par une structure Symfony correcte (services, commandes, configuration) ; si vous partez de zéro, appuyez-vous sur : Module PrestaShop 9 : structure, services et bonnes pratiques Symfony.
Le pipeline minimal :
- Détecter les champs source modifiés (hash du contenu source +
date_upd). - Lister les traductions manquantes par
id_langcible. - Appeler une API de traduction (DeepL, Google Cloud Translation, etc.) en respectant rate limits et coûts.
- Écrire atomiquement dans les tables
_lang(transaction si possible) et journaliser ce qui a été modifié. - Régénérer/valider le
link_rewritesi vous automatisez aussi les slugs.
Deux raffinements qui changent tout sur de gros catalogues :
- Idempotence par champ : un produit peut avoir
descriptionmodifiée mais pasname. Enregistrez un hash par champ (ou un hash global + versioning) pour éviter de retraduire tout le bloc. - Segmentation : traitez d’abord les catégories (structure), puis les produits, puis les CMS. Les catégories influencent souvent les breadcrumbs, menus et maillage interne.
Sur un catalogue à 50k produits × 5 langues, la différence entre un batch idempotent et un batch “brutal” est énorme : l’idempotent ne retraduit que ce qui change, réduit les écritures DB, limite les invalidations de cache, et vous permet de reprendre après interruption. Pour éviter d’exploser la prod, couplez ça à une stratégie de cache serveur (Redis, Varnish) et à une limitation de débit ; cf. Cache PrestaShop : Varnish, Redis, Memcached et OPcache côté serveur.
Un squelette de commande (simplifié) ressemble à ça :
// PrestaShop 9.1 + PHP 8.2
#[AsCommand(name: 'acme:translate:products')]
final class TranslateProductsCommand extends Command
{
public function __construct(
private readonly Connection $db,
private readonly HttpClientInterface $http,
private readonly int $shopId,
private readonly int $sourceLangId,
) { parent::__construct(); }
protected function execute(InputInterface $input, OutputInterface $output): int
{
// 1) Charger une page de travail (LIMIT/OFFSET ou curseur)
$rows = $this->db->fetchAllAssociative(
'SELECT pl.id_product, pl.name, pl.description_short
FROM ps_product_lang pl
WHERE pl.id_shop = :shop AND pl.id_lang = :src
ORDER BY pl.id_product ASC
LIMIT 200',
['shop' => $this->shopId, 'src' => $this->sourceLangId]
);
// 2) Appeler l’API en batch, puis 3) écrire dans ps_product_lang
// (à faire avec vérifs d’unicité sur link_rewrite et contrôle de longueur sur meta_*)
return Command::SUCCESS;
}
}
Les points non négociables : (1) verrouillage (éviter deux batches simultanés), (2) retries avec backoff sur erreurs HTTP, (3) journalisation (quoi/qui/quand), (4) tests sur staging avec snapshot DB.
Et oui, ça implique de toucher à la DB : si vous n’avez pas une routine de maintenance (index, purge, optimisation), vous allez le payer tôt ou tard (voir Base de données PrestaShop : routine de maintenance et nettoyage automatisé).
Validation SEO : crawl, contrôles automatiques, détection des régressions
La validation hreflang ne se fait pas “à l’œil”. Vous devez vérifier : présence, valeurs, réciprocité, et cohérence canonical. Un test simple par page en CI (ou au minimum en préprod) : récupérer le HTML et parser les alternates.
Le test utile n’est pas juste “ça existe”, c’est :
- chaque
hreflangpointe vers une URL 200, - indexable (pas de
noindex, pas bloquée robots, pas d’auth), - avec un canonical cohérent,
- et qui renvoie un retour vers la page source (réciprocité).
Pour industrialiser, vous pouvez construire un crawler interne qui (a) prend une liste d’URLs (sitemap), (b) récupère alternates + canonical, (c) vérifie les codes HTTP et la réciprocité. Les erreurs typiques remontent vite : alternates qui pointent vers des pages désactivées, URLs avec paramètres, pages qui 301 en chaîne.
Conseil pratique : basez la liste d’URLs sur vos sitemaps (module sitemap ou génération custom). Cela évite d’auditer des pages non destinées à l’indexation (facettes, tri, recherche interne).
Sur les contenus traduits, traquez les “pages vides” et les templates qui n’affichent pas les champs traduits (H1, snippets, descriptions). C’est un bug courant lors de refontes thème : un sélecteur de langue fonctionne, mais le rendu HTML reste sur la langue par défaut car une variable n’est pas contextualisée. Pour cadrer l’audit, vous pouvez réutiliser la check-list existante : Page vide : audit SEO des balises H1-H6 et contenus manquants et l’aligner avec votre plan global (voir SEO PrestaShop : roadmap 3 mois audit, contenu, performance et popularité).
Enfin, surveillez l’impact infra : traduire + régénérer slugs/metas, c’est beaucoup d’I/O DB et potentiellement une hausse de latence. Mettez des métriques sur le MySQL (temps de requêtes, locks, buffer pool), sur PHP-FPM (latences, saturation), et sur les erreurs applicatives (500, timeouts). Pour l’observabilité “prête à brancher”, Netdata et Grafana font le boulot, à condition de définir des seuils/alertes : Netdata monitoring et Grafana sur Ubuntu : installation APT et configuration initiale.
Mise en production : garde-fous concrets (staging, rollback, checklist)
Déployer une localisation SEO automatique touche au routage, au HTML <head> et à la DB ; ce n’est pas un “petit module” qu’on active un vendredi soir. Travaillez sur staging avec une copie réaliste du catalogue (ou au minimum un échantillon représentatif) et un environnement proche prod (mêmes versions PHP/MySQL, mêmes settings de cache). Avant le premier batch, prenez un snapshot DB et un export des tables _lang concernées.
Le rollback doit être prévu : si votre génération de slugs crée des collisions ou des URLs incohérentes, vous devez pouvoir revenir à l’état précédent. Concrètement : conservez un dump des champs link_rewrite, meta_*, name/description par id_lang, et versionnez vos règles de redirection si vous en ajoutez. Si vous opérez en multi-boutique, testez aussi la matrice id_shop × id_lang : c’est là que les effets de bord apparaissent (URL qui pointe vers le mauvais domaine, langue active mais shop_url absent, etc.).
Checklist minimale avant bascule :
- Vérifier que chaque page cible a un canonical cohérent + alternates hreflang réciproques (et auto-référence).
- Garantir l’unicité des
link_rewritepar type d’entité et par langue (requêtes SQL + tests applicatifs). - Mettre en place une stratégie de redirection 301 pour changements de slugs (au minimum sur produits et catégories), avec historique.
- Auditer les templates pour s’assurer que H1/title/meta/JSON-LD sont bien contextualisés par langue (attention aux caches non variés).
- Exécuter la traduction en batch (débit limité), avec logs, retries, verrouillage (un job = une responsabilité).
- Monitorer DB/PHP-FPM pendant l’opération (alertes sur locks, latence, 5xx) et garder un plan de pause/relance.
- Après mise en ligne : contrôler les 404 et les redirections via logs (et pas uniquement via un outil SEO), puis corriger les cas réels.
Si vous faites ces points correctement, la “traduction PrestaShop” devient une vraie localisation SEO : stable sur le routage, contrôlable sur la qualité, et observable en prod. Sinon, vous obtenez le combo habituel : hreflang décoratif, slugs cassés, et métadonnées dupliquées — autrement dit, du bruit pour les moteurs et de la dette technique pour vous.
