From 00fef63b4bd16d7c89555d40302a796df4fe92e5 Mon Sep 17 00:00:00 2001 From: Yevhenii Date: Mon, 12 Oct 2026 08:04:40 +0300 Subject: [PATCH] Align text to the left by default in LTR on iOS A paragraph that declares no textAlign gets an explicit alignment on iOS only when its layout direction is right to left (#57634). In a left-to-right layout the paragraph style keeps NSTextAlignmentNatural, which UIKit resolves against the app's localization rather than the paragraph, so in an app running in a right-to-left language a left-to-right subtree is drawn flush right while Android draws it flush left. Resolve the alignment whenever the paragraph's layout direction is known, which is what RCTIsParagraphStyleEffectivelySame already assumes when it compares paragraph styles. --- .../Tests/Text/RCTAttributedTextUtilsTest.mm | 25 +++++++++++++++++++ .../RCTAttributedTextUtils.mm | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm b/packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm index a02622d5d7d7..242e40e644ae 100644 --- a/packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm +++ b/packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm @@ -43,6 +43,15 @@ static NSTextAlignment NSTextAlignmentFromTextAlign(NSString *textAlign, LayoutD return paragraphStyle.alignment; } +static NSParagraphStyle *ParagraphStyleFromLayoutDirection(std::optional layoutDirection) +{ + TextAttributes textAttributes; + textAttributes.layoutDirection = layoutDirection; + + NSDictionary *attributes = RCTNSTextAttributesFromTextAttributes(textAttributes); + return attributes[NSParagraphStyleAttributeName]; +} + static UIFont *FontFromSettings(std::optional settings, std::optional weight = std::nullopt) { TextAttributes textAttributes; @@ -133,6 +142,22 @@ - (void)testTextAlignmentStartAndEndResolveWithLayoutDirection XCTAssertEqual(NSTextAlignmentFromTextAlign(@"end", LayoutDirection::RightToLeft), NSTextAlignmentLeft); } +- (void)testUndeclaredTextAlignmentResolvesWithLayoutDirection +{ + NSParagraphStyle *leftToRight = ParagraphStyleFromLayoutDirection(LayoutDirection::LeftToRight); + XCTAssertNotNil(leftToRight); + XCTAssertEqual(leftToRight.alignment, NSTextAlignmentLeft); + + NSParagraphStyle *rightToLeft = ParagraphStyleFromLayoutDirection(LayoutDirection::RightToLeft); + XCTAssertNotNil(rightToLeft); + XCTAssertEqual(rightToLeft.alignment, NSTextAlignmentRight); +} + +- (void)testUndeclaredTextAlignmentWithoutLayoutDirectionStaysNatural +{ + XCTAssertNil(ParagraphStyleFromLayoutDirection(std::nullopt)); +} + - (void)testSamenessOfEmptyAttributedStrings { NSAttributedString *attributedString1 = [[NSAttributedString alloc] initWithString:@""]; diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm index 95ff5a0d180e..ebebb33ff749 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm @@ -218,7 +218,7 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; BOOL isParagraphStyleUsed = NO; const bool isRTL = textAttributes.layoutDirection == LayoutDirection::RightToLeft; - if (textAttributes.alignment.has_value() || isRTL) { + if (textAttributes.alignment.has_value() || textAttributes.layoutDirection.has_value()) { TextAlignment textAlignment = RCTResolveTextAlignment(textAttributes.alignment.value_or(TextAlignment::Natural), isRTL);