Fix layout direction override (PWENG-39)#6723
Merged
Merged
Conversation
5724f7b to
0f85cf1
Compare
alexrepty
commented
Apr 30, 2026
Comment on lines
+176
to
+177
| .environment(\.locale, resolvedLocale) | ||
| .environment(\.layoutDirection, resolvedLocale.swiftUILayoutDirection) |
Contributor
Author
There was a problem hiding this comment.
This is the actual meat of the change. Setting the environment variables will correctly apply the locale and layout direction.
JZDesign
approved these changes
Apr 30, 2026
…om/RevenueCat/purchases-ios into alex/fix-layout-direction-override
📸 Snapshot Test3 added, 264 unchanged
🛸 Powered by Emerge Tools |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b0fa02f. Configure here.
Merged
2 tasks
matteinn
pushed a commit
to matteinn/purchases-android
that referenced
this pull request
May 5, 2026
…enueCat#3425) <!-- Thank you for contributing to Purchases! Before pressing the "Create Pull Request" button, please provide the following: --> ### Checklist - [x] If applicable, unit tests - [x] If applicable, create follow-up issues for `purchases-ios` and hybrids [PR](RevenueCat/purchases-ios#6723) ### Motivation Fixes paywall layout direction when `preferredUILocaleOverride` resolves to an RTL locale while the device/system locale is LTR. Previously, Android paywalls could use the overridden locale for localized strings while still inheriting the system Compose `LocalLayoutDirection`, so RTL languages like Hebrew and Arabic could render translated text but keep LTR layout behavior. Resolves: PWENG-39 ### Changes - Adds a locale-to-Compose-layout-direction helper backed by Android `Configuration.layoutDirection`. - Applies the resolved locale layout direction to legacy paywalls. - Applies the resolved locale layout direction to V2/components paywalls. - Adds unit coverage for RTL and LTR locale direction resolution. [Screen_recording_20260501_134422.webm](https://github.com/user-attachments/assets/b19c2be7-9bdd-4daa-8954-e119e8f8d01d) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes paywall composition locals to force `LocalLayoutDirection` based on the resolved/overridden locale, which could subtly affect layout/alignment across both legacy and components paywalls. Risk is limited to UI rendering (no purchase/auth logic), but may introduce visual regressions in LTR/RTL edge cases. > > **Overview** > Fixes paywall rendering when a `preferredUILocaleOverride` resolves to an RTL language by deriving a Compose `LayoutDirection` from the resolved locale and providing it via `LocalLayoutDirection`. > > Applies this override in both legacy template paywalls (`InternalPaywall`) and V2/components paywalls (`LoadedPaywallComponents`), and adds a new `Locale.toLayoutDirection()` helper with Android instrumentation tests covering RTL (Hebrew/Arabic/Farsi) and LTR locales. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 8fc3352. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Checklist
purchases-androidand hybridsMotivation
When
Purchases.shared.overridePreferredUILocale(_:)is set to an RTL locale (e.g. Hebrew or Arabic) while the device system locale is LTR (e.g. English), the paywall renders translated strings correctly but the layout direction stays LTR. Close buttons, package cards, and text alignment all remain left-anchored, producing a broken RTL experience.Resolves PWENG-39
Description
SwiftUI's layout engine derives text direction and
HStackalignment from the\.localeenvironment value. When that key is not explicitly set, SwiftUI inherits it from the host app's environment, which reflects the system locale — not the locale override. BecauseoverridePreferredUILocalewas only threaded through the content-selection path (string bundle lookup), the environment was never updated and layout stayed LTR.Fix: Inject
\.localeand\.layoutDirectioninto the SwiftUI environment at the point where the resolved locale is already available, in both the V1 (LoadedOfferingPaywallView) and V2 (PaywallsV2View) render paths. ALocale.swiftUILayoutDirectionhelper mapsLocale.characterDirection(forLanguage:)toSwiftUI.LayoutDirection.Testing
Added
LocaleLayoutDirectionTestsunit tests covering RTL languages (Hebrew, Arabic, Farsi, Urdu) and LTR languages (English, French, Japanese), including region-qualified identifiers and the empty-string edge case.Expanded
PaywallViewLocalizationTestswith Hebrew (he-IL) and Arabic (ar) snapshot regression tests. Each test uses a locale-specific offering with native translations (just AI-translated, since it won't actually be facing end users) and renders withlocaleOverrideset, so a future regression (RTL strings but LTR layout) would produce a diff in the reference PNGs.Note
Medium Risk
Changes SwiftUI environment injection for
locale/layoutDirectionin both V1 and V2 paywall render paths, which can affect layout across all paywalls and locales. Risk is mitigated by added unit and snapshot coverage for RTL and locale-resolution edge cases.Overview
Fixes RTL rendering when
overridePreferredUILocaleis set by explicitly injecting the resolved contentLocaleand matchingLayoutDirectioninto the SwiftUI environment for both V1 (LoadedOfferingPaywallView) and V2 (PaywallsV2View) paywalls.Adds
Locale.swiftUILayoutDirectionplus a sharedLocale.selectPreferredLocalehelper (used by V2 locale resolution), and expands test coverage with new RTL/LTR unit tests and Hebrew/Arabic snapshot regressions (plus a small snapshot-recording helper inTestCase).Reviewed by Cursor Bugbot for commit 5e6b9bc. Bugbot is set up for automated code reviews on this repo. Configure here.