Skip to content

🐛 Fixed RTL languages rendering left-to-right in newsletter emails#27357

Open
betschki wants to merge 7 commits intoTryGhost:mainfrom
magicpages:fix/newsletter-rtl-support
Open

🐛 Fixed RTL languages rendering left-to-right in newsletter emails#27357
betschki wants to merge 7 commits intoTryGhost:mainfrom
magicpages:fix/newsletter-rtl-support

Conversation

@betschki
Copy link
Copy Markdown
Contributor

Newsletters for sites with an RTL publication language (Persian, Arabic, Hebrew, Urdu) rendered left-to-right because the email template never emitted lang/dir on the root <html> element.

Portal already calls i18next's built-in dir() for the same purpose; the email renderer just never wired it through. The renderer now passes the resolved locale and direction into the template context, the template sets html lang/dir, the inline body style picks up direction, and the feedback button row mirrors with the document.

Defaults to ltr when no dir helper is provided so existing renders are byte-identical for LTR locales.

Before:
Screenshot 2026-04-12 at 18-04-31 Ghost Admin - Test

After:
Screenshot 2026-04-12 at 18-03-15 Ghost Admin - Test

Got some code for us? Awesome 🎊!

Please take a minute to explain the change you're making:

  • Why are you making it?
  • What does it do?
  • Why is this something Ghost users or developers need?

Please check your PR against these items:

  • I've read and followed the Contributor Guide
  • I've explained my change
  • I've written an automated test to prove my change works

We appreciate your contribution! 🙏

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e52b8f85-d750-4d80-81bb-341f277d893e

📥 Commits

Reviewing files that changed from the base of the PR and between b9ec001 and 5dd2763.

⛔ Files ignored due to path filters (1)
  • ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • ghost/core/core/server/services/email-service/email-renderer.js
  • ghost/core/core/server/services/email-service/email-service-wrapper.js
  • ghost/core/core/server/services/email-service/email-templates/partials/styles.hbs
  • ghost/core/core/server/services/email-service/email-templates/template.hbs
  • ghost/core/test/unit/server/services/email-service/email-renderer.test.js

Walkthrough

The changes introduce bi-directional text direction support to email rendering. The EmailRenderer class now accepts an optional dir dependency that computes text direction based on locale. During template data generation, the renderer determines and passes both the current locale and computed direction (rtl or ltr) to email templates via the site object. The i18n.dir method is wired into the email service wrapper and used to resolve direction values. Email template markup and styles are updated to apply these dynamic values to the <html> element's lang and dir attributes, the body CSS rule, and feedback component directionality. Test coverage validates correct direction assignment for various locales and fallback behavior when the dir helper is unavailable.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing RTL language rendering in newsletter emails, which is the core objective of the PR.
Description check ✅ Passed The description comprehensively explains the problem, solution, implementation details, and includes before/after screenshots demonstrating the fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/newsletter-rtl-support

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.42.1)
ghost/core/test/unit/server/services/email-service/email-renderer.test.js

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Newsletters for sites with an RTL publication language (Persian, Arabic,
Hebrew, Urdu) rendered left-to-right because the email template never
emitted lang/dir on the root <html> element. Portal already calls
i18next's built-in dir() for the same purpose; the email renderer just
never wired it through. The renderer now passes the resolved locale and
direction into the template context, the template sets html lang/dir,
the inline body style picks up direction, and the feedback button row
mirrors with the document. Defaults to ltr when no dir helper is
provided so existing renders are byte-identical for LTR locales.
@betschki betschki force-pushed the fix/newsletter-rtl-support branch from 5dd2763 to 7afd15c Compare April 12, 2026 18:14
The email preview API endpoint snapshot still expected the old <html>
shape; the renderer now emits lang/dir and a body direction style.
Regenerating the snapshot here so the new wrapper output matches.
The integration snapshot test calls MemberWelcomeEmailRenderer directly
with a fixture siteSettings that doesn't include locale, which made the
new wrapper render <html lang dir="ltr"> (empty lang attribute).
Production goes through service.js#getSiteSettings which already falls
back to en, so this only affected the test path, but the renderer now
defends against any direct caller missing locale by defaulting to en.
Snapshot regenerated with the corrected output.
@marceloomens
Copy link
Copy Markdown

I'd love to see this PR merged soonest, as I currently have an issue with Ghost newsletter RTL support in production. Please let me know if there's anything I can do in support of moving this PR along.

The "Manage subscription" cell in the subscription details box was
hardcoded to align right via both an HTML attribute and CSS. Combined
with the table column flip that comes from <html dir="rtl">, that
pushed the link inwards against the subscription details block instead
of out to the email margin. The HTML align attribute is now conditional
on site.direction (Outlook still wants the literal value), the CSS
uses text-align: end so it flips automatically in modern clients, and
the mobile-stacking media rule that forced text-align: left switches
to start so stacked content reads in the document direction.
Copy link
Copy Markdown
Contributor

@EvanHahn EvanHahn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good but I have a few comments. Thanks for doing this!

Comment thread ghost/core/core/server/services/email-service/email-templates/partials/styles.hbs Outdated
Comment thread ghost/core/core/server/services/email-service/email-renderer.js Outdated
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
color: #15212A;
direction: {{site.direction}};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know much about this property, but MDN discourages it in favor of the dir HTML attribute (which you've set). Is this necessary?

Copy link
Copy Markdown

@marceloomens marceloomens Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my experience duplicating this (so both the dir="rtl" HTML attribute and direction: rtl; style) gives the most consistent result across clients.

I'm personally also in favor of "just the HTML attribute". Or better still, modern clients implying "dir=rtl" based on the lang attribute.

I would prefer to be pragmatic, aim for cross-client consistency, and duplicate this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like @marceloomens's approach. Any objection to that @EvanHahn?

Comment thread ghost/core/test/unit/server/services/email-service/email-renderer.test.js Outdated
Comment thread ghost/core/test/unit/server/services/email-service/email-renderer.test.js Outdated
Made the dir parameter required with a proper type annotation instead
of an optional Function. Replaced CSS logical properties (text-align:
end/start) with Handlebars-interpolated values for Outlook and Yahoo
Mail compatibility. Consolidated duplicate RTL locale tests.
@sonarqubecloud
Copy link
Copy Markdown

@EvanHahn
Copy link
Copy Markdown
Contributor

EvanHahn commented Apr 16, 2026 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants