Skip to content

fix(android): keep textAlign left/right on their physical side in RTL paragraphs - #1078

Draft
jnowakow wants to merge 1 commit into
mainfrom
fix/rn-android-rtl-text-align
Draft

jnowakow wants to merge 1 commit into
mainfrom
fix/rn-android-rtl-text-align

Conversation

@jnowakow

Copy link
Copy Markdown
Collaborator

What/Why?

On Android, markdownStyle textAlign: 'left' / 'right' on paragraph and h1–h6 flipped in right-to-left paragraphs: a Hebrew or Arabic paragraph with textAlign: 'left' rendered right-aligned, and one with 'right' rendered left-aligned.

The cause: TextAlignment.LEFT mapped to Layout.Alignment.ALIGN_NORMAL (and RIGHT to ALIGN_OPPOSITE). Android's layout alignments are relative to the paragraph's direction, so in an RTL paragraph ALIGN_NORMAL is the right side. iOS maps left/right to NSTextAlignmentLeft/Right (physical sides; ios/utils/ParagraphStyleUtils.m textAlignmentFromString), and web passes them to CSS text-align (src/web/styles.ts), so on those platforms they are physical sides too.

The fix is ported from the standalone library packages/enriched-markdown-android (#872):

  • styles/TextAlignment.kt: layoutAlignment(isRtl) and isAbsolute replace the fixed layoutAlignment / needsAlignmentSpan.
  • utils/text/span/TextAlignmentSpans.kt (new, taken from the library as is): applyTextAlignment resolves left/right per text paragraph from its first strong character, falling back to the layout direction when there is none. That is how the TextView resolves each paragraph's direction, so the span always lands on the requested physical side.
  • ParagraphRenderer and HeadingRenderer call applyTextAlignment, as in the library.

The JS-facing values are unchanged (auto, left, right, center, justify). The library's START/END values were not ported, because the JS API has no start/end values. auto, center and justify behave as before: auto and justify add no span (justify is still justificationMode on the TextView), and center adds ALIGN_CENTER. writingDirection is a no-op on Android (each paragraph is already resolved first-strong), so it does not change anything here.

Tables are not touched. Table cells still inherit the paragraph's textAlign when the column has no alignment of its own, as before (and as on iOS). So with paragraph.textAlign: 'left', an RTL cell in a column with no alignment is now left-aligned, like a paragraph would be.

Testing

On the Playground (Android emulator, API 36), with a temporary toggle (not committed) that sets paragraph and h1 textAlign, I set this markdown:

# כותרת בעברית

שלום עולם, זו פסקה קצרה.

# English heading

A short English paragraph.

I checked it in both flavors: "Reference links: Off" renders GitHub and "CommonMark" renders CommonMark.

  • left: before, the Hebrew heading and paragraph stayed on the right. After, all four blocks are on the left.
  • right: before, the Hebrew blocks were on the left. After, all four blocks are on the right.
  • auto: Hebrew is on the right and English on the left, both before and after (unchanged).

Unit tests added:

  • styles/TextAlignmentTest: alignment × direction → Layout.Alignment, isAbsolute, and parsing the JS values.
  • TextAlignmentRendererTest (Robolectric, ported from the library's test): a rendered RTL or LTR paragraph and heading get the expected AlignmentSpan; a paragraph is resolved line by line; text with no strong character follows the layout direction (ar locale).

:react-native-enriched-markdown:testDebugUnitTest passes (54 tests) and :react-native-enriched-markdown:ktlintCheck is clean.

Shots below use the GitHub flavor; preview box at the bottom.

Before After
textAlign: 'left' Before left After left
textAlign: 'right' Before right After right

PR Checklist

  • Code compiles and runs on iOS (iOS is not touched)
  • Code compiles and runs on Android
  • Updated documentation/README if applicable
  • Ran example app to verify changes
  • E2E tests are passing
  • Required E2E tests have been added (if applicable)

… paragraphs

TextAlignment.LEFT mapped to Layout.Alignment.ALIGN_NORMAL and RIGHT to
ALIGN_OPPOSITE, which are relative to the paragraph's direction, so in a
Hebrew or Arabic paragraph or heading `left` rendered on the right and
`right` on the left. iOS (NSTextAlignmentLeft/Right) and web (CSS
text-align) both treat them as physical sides.

Port the per-paragraph resolution from packages/enriched-markdown-android
(#872): TextAlignment.layoutAlignment(isRtl) and applyTextAlignment, which
resolves each paragraph's direction from its first strong character,
falling back to the layout direction. auto, center and justify are
unchanged.

This branch has not been deployed

No deployments
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.

1 participant