Summary
When Content Localization is enabled, the affiliate plugin’s Amazon tag (and, more generally, any plugin that rewrites cooked HTML via the post_process_cooked event) is applied to a post’s original cooked HTML but not to its translated PostLocalization cooked HTML. Readers who view a post in a translated locale therefore see the raw, untagged link, which means affiliate commission is lost on every translated impression.
The root cause is that translated content is baked through a second, separate processor (LocalizedCookedPostProcessor) that never triggers the post_process_cooked event.
Environment
- Discourse: core
main@978e9b2e5dc(2026-07-15); also confirmed on the2026.7.0line @7c06c1528(2026-07-10). No relevant change between them. - Plugin:
discourse-affiliate(shipped in core’splugins/directory), withaffiliate_amazon_de(or anyaffiliate_amazon_*) set to a valid tag. - Settings:
content_localization_enabled = true,content_localization_supported_localesincludes at least one locale other than the post’s original. - Scope: server-side (post baking). Independent of device/OS/browser; reproduces in Safe Mode and with no theme, since it happens in the cook pipeline, not the front end.
Steps to reproduce
- Enable
content_localization_enabledand add a second supported locale (e.g. original posts inde, translation toen). - Configure
discourse-affiliate: set the Amazon tag for the relevant marketplace (e.g.affiliate_amazon_de = mytag-21). - Create a post whose raw contains an Amazon product link, e.g.
https://www.amazon.de/-/en/…/dp/B0C3WGSSWC. - View the post in its original locale and inspect the Amazon anchor’s
href. - Generate/serve the translated version of the same post (via the language switcher, or by letting the translation be created) and inspect the same anchor’s
href.
Actual result
- Original cooked:
…/dp/B0C3WGSSWC?tag=mytag-21— affiliate tag present.
PostLocalizationcooked (translated):…/dp/B0C3WGSSWC— notag, no affiliate rewriting at all.
(Confirmed directly in the DB: for an affected post, posts.cooked carries the tagged URL while every post_localizations.cooked row for that post carries the bare URL.)
Expected result
The translated cooked HTML should receive the same link rewriting as the original, so the affiliate tag (and any other post_process_cooked transformation) is present in every locale a reader can view.
Root cause
The affiliate plugin rewrites links only inside the post_process_cooked event:
# 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
That event is triggered from exactly one place, the original-language cook:
# lib/cooked_post_processor.rb:50
DiscourseEvent.trigger(:post_process_cooked, @doc, @post)
Translated content, however, is baked by a different processor, enqueued from PostLocalizationCreator/PostLocalizationUpdater as 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 never triggers post_process_cooked, and there is no DiscourseEvent.trigger anywhere in the localization bake path — so no plugin relying on that event participates in translated content. This is a general gap: it affects affiliate tagging today, but equally any plugin that mutates cooked HTML through post_process_cooked (link decorators, badge/mention rewrites, etc.).
Suggested fix
Have the localized bake participate in the same link/DOM rewriting as the original cook. Either option resolves it:
- Emit a hookable event from the localized processor — trigger
post_process_cooked(or a dedicatedpost_process_localized_cooked) insideLocalizedCookedPostProcessor#post_process, so existing plugins work on translations with no per-plugin change. (If a new event is preferred to avoid double-processing side effects, plugins can opt in.) - Run the affiliate rewrite in the localized path, e.g. reuse
AffiliateProcessor.applyover@doc.css("a[href]")inLocalizedCookedPostProcessor#post_processafter the onebox/image passes.
Option 1 is the more general fix, since the same gap affects every post_process_cooked consumer, not just affiliate.
Additional notes
- A site that already has affected translations will need existing
PostLocalizationrows re-baked after the fix (e.g. re-enqueueJobs::ProcessLocalizedCookedwithrecook: true) to backfill tags into already-translated posts. - Reproduces consistently (not intermittent) whenever a post with an affiliate-eligible link is served in a translated locale.