La réécriture des liens d'affiliation (et tout hook post_process_cooked) est ignorée pour le contenu des messages localisé/traduit

Résumé

Lorsque la localisation du contenu est activée, le tag Amazon du plugin d’affiliation (et, plus généralement, tout plugin qui réécrit le HTML cuit via l’événement post_process_cooked) est appliqué au HTML cuit original d’un message, mais pas à son HTML cuit PostLocalization traduit. Les lecteurs qui consultent un message dans une langue traduite voient donc le lien brut non étiqueté, ce qui signifie que la commission d’affiliation est perdue à chaque impression traduite.

La cause racine est que le contenu traduit est généré par un second processeur séparé (LocalizedCookedPostProcessor) qui ne déclenche jamais l’événement post_process_cooked.

Environnement

  • Discourse : core main @ 978e9b2e5dc (2026-07-15) ; confirmé également sur la branche 2026.7.0 @ 7c06c1528 (2026-07-10). Aucune modification pertinente entre les deux.
  • Plugin : discourse-affiliate (fourni dans le répertoire plugins/ du core), avec affiliate_amazon_de (ou tout autre affiliate_amazon_*) défini sur un tag valide.
  • Paramètres : content_localization_enabled = true, content_localization_supported_locales inclut au moins une langue autre que celle d’origine du message.
  • Portée : côté serveur (cuisson des messages). Indépendant de l’appareil/Système d’exploitation/navigateur ; reproductible en mode sans échec et sans thème, car cela se produit dans le pipeline de cuisson, et non dans l’interface utilisateur.

Étapes pour reproduire

  1. Activez content_localization_enabled et ajoutez une seconde langue prise en charge (par exemple, messages originaux en de, traduction en en).
  2. Configurez discourse-affiliate : définissez le tag Amazon pour le marketplace concerné (par exemple affiliate_amazon_de = mytag-21).
  3. Créez un message dont le contenu brut contient un lien vers un produit Amazon, par exemple https://www.amazon.de/-/en/…/dp/B0C3WGSSWC.
  4. Consultez le message dans sa langue originale et inspectez l’attribut href de l’ancre Amazon.
  5. Générez/servez la version traduite du même message (via le sélecteur de langue, ou en laissant la traduction être créée) et inspectez l’attribut href de la même ancre.

Résultat actuel

  • HTML cuit original : …/dp/B0C3WGSSWC?tag=mytag-21 — tag d’affiliation présent. :white_check_mark:
  • HTML cuit PostLocalization (traduit) : …/dp/B0C3WGSSWC — pas de tag, aucune réécriture d’affiliation. :cross_mark:

(Confirmé directement dans la base de données : pour un message concerné, posts.cooked contient l’URL avec tag, tandis que chaque ligne post_localizations.cooked pour ce message contient l’URL brute.)

Résultat attendu

Le HTML cuit traduit devrait recevoir la même réécriture de liens que l’original, de sorte que le tag d’affiliation (et toute autre transformation post_process_cooked) soit présent dans chaque langue consultée par le lecteur.

Cause racine

Le plugin d’affiliation réécrit les liens uniquement à l’intérieur de l’événement post_process_cooked :

# plugins/discourse-affiliate/plugin.rb
on(:post_process_cooked) do |doc, post|
  doc.css("a[href]").each { |a| a["href"] = AffiliateProcessor.apply(a["href"]) }
  true
end

Cet événement est déclenché depuis un seul endroit, la cuisson de la langue originale :

# lib/cooked_post_processor.rb:50
DiscourseEvent.trigger(:post_process_cooked, @doc, @post)

Le contenu traduit, cependant, est cuit par un processeur différent, mis en file d’attente depuis PostLocalizationCreator/PostLocalizationUpdater en tant que Jobs::ProcessLocalizedCooked :

# lib/localized_cooked_post_processor.rb
def post_process
  post_process_oneboxes
  post_process_images
  @post_localization.link_post_uploads(fragments: @doc)
end

LocalizedCookedPostProcessor#post_process ne déclenche jamais post_process_cooked, et il n’y a aucun DiscourseEvent.trigger nulle part dans le chemin de cuisson de la localisation — aucun plugin dépendant de cet événement ne participe donc au contenu traduit. C’est une lacune générale : elle affecte le tag d’affiliation aujourd’hui, mais également tout plugin qui modifie le HTML cuit via post_process_cooked (décorateurs de liens, réécritures de badges/mentions, etc.).

Correction suggérée

Permettre à la cuisson localisée de participer à la même réécriture de liens/DOM que la cuisson originale. L’une ou l’autre option résout le problème :

  1. Émettre un événement hookable depuis le processeur localisé — déclencher post_process_cooked (ou un post_process_localized_cooked dédié) à l’intérieur de LocalizedCookedPostProcessor#post_process, de sorte que les plugins existants fonctionnent sur les traductions sans modification par plugin. (Si un nouvel événement est préféré pour éviter les effets secondaires d’un double traitement, les plugins peuvent opter pour celui-ci.)
  2. Exécuter la réécriture d’affiliation dans le chemin localisé, par exemple réutiliser AffiliateProcessor.apply sur @doc.css("a[href]") dans LocalizedCookedPostProcessor#post_process après les passages onebox/image.

L’option 1 est la correction la plus générale, car la même lacune affecte chaque consommateur de post_process_cooked, pas seulement l’affiliation.

Notes supplémentaires

  • Un site disposant déjà de traductions concernées devra recuire les lignes PostLocalization existantes après la correction (par exemple, remettre en file d’attente Jobs::ProcessLocalizedCooked avec recook: true) pour rétroinjecter les tags dans les messages déjà traduits.
  • Se reproduit de manière constante (pas intermittente) chaque fois qu’un message contenant un lien éligible à l’affiliation est servi dans une langue traduite.