Content Localization uses raw R2 bucket URL instead of S3 CDN URL in translated posts

AI-generated summary

David_Ghost reports a bug where Content Localization with Discourse AI translations and Cloudflare R2 storage uses raw R2 bucket URLs instead of the configured s3_cdn_url, causing images to break in translated posts. Despite having s3_use_cdn_url_for_all_uploads enabled, translated content references the raw endpoint rather than the custom domain. David_Ghost provides a temporary theme component workaround that remaps the image sources. Lilly responds that they use similar settings (Cloudflare R2, multiple languages, AI translation) but experience no issues, providing screenshots of working images in English, German, Russian, and Portuguese. In reply, David_Ghost notes that in their tests, only 1 or 2 out of 3 to 5 uploaded images render correctly.

Hello,

When using content Localization with Discourse AI translations and Cloudflare R2 as S3-compatible storage, the translated post content stores the raw R2 bucket endpoint URL instead of the configured s3_cdn_url (custom domain). Images render broken for all users viewing translations.

Environment

  • Discourse: latest
  • Cloudflare R2 configured via S3 compatible storage
  • s3_cdn_url: https://files.mydomain.com
  • s3_use_cdn_url_for_all_uploads: enabled
  • Content Localization + Discourse AI translation: enabled

Steps to Reproduce

  1. Configure Cloudflare R2 with a custom domain (s3_cdn_url)
  2. Enable Content Localization and AI translation
  3. Create a post with an image
  4. Wait for the AI translation to generate the localized version
  5. Switch language — images in the translated version are broken

Observed behavior

The translated content contains URLs like:

//files.myendpoint.r2.cloudflarestorage.com/original/1X/ instead of https://files.mydomain.com/original/1X/

Related bug: Chat thumbnails bypass s3_cdn_url & use raw S3 bucket URLs

a temporary fix is to do a remap or use this tc:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer("1.8.0", (api) => {
  const badDomain = "files.myendpoint.r2.cloudflarestorage.com";
  const goodDomain = "files.mydomain.com";

  function fixImages() {
    document.querySelectorAll(`img[src*="${badDomain}"]`).forEach(img => {
      img.src = img.src.replace(badDomain, goodDomain);
    });
    document.querySelectorAll(`img[data-src*="${badDomain}"]`).forEach(img => {
      img.dataset.src = img.dataset.src.replace(badDomain, goodDomain);
    });
    document.querySelectorAll(`img[data-large-src*="${badDomain}"]`).forEach(img => {
      img.dataset.largeSrc = img.dataset.largeSrc.replace(badDomain, goodDomain);
    });
  }

  api.decorateCooked(() => fixImages(), { id: "fix-r2-translated-images" });
  api.onPageChange(() => fixImages());
});

i’m not so sure about this. i have cloudflare R2 buckets for object storage and backups on our site and we have multiple languages with AI translate - images are loading just fine in all of them.

for example:

English

German

Russian

Portuguese

Yes, on my tests, i’m uploading 3 to 5 images. But only 1 or 2 works as expected.