Implementing RTL Language Support (Arabic, Hebrew) in Mobile Applications
Adding Arabic or Hebrew to working application — not "change locale and set layoutDirection = rtl". Typical picture after such attempt: back icons look right, but progress bar arrow goes left to right; text in TextField aligned correctly, but placeholder still left; slide-in animations "arrive" from wrong side, custom Canvas draws graphs mirrored only in debug mode on emulator — on real device no.
Where RTL Actually Breaks
iOS — Auto Layout and SwiftUI
Behavior determined by semanticContentAttribute on UIView. By default .unspecified — system decides by locale. Problem starts with custom views: UIView.draw(_:), CALayer, Core Graphics — all ignore semanticContentAttribute and draw in absolute coordinates. Need explicitly apply CGAffineTransform(scaleX: -1, y: 1) or rewrite logic for userInterfaceLayoutDirection.
In SwiftUI HStack and padding(.leading) auto-mirror at layoutDirection == .rightToLeft. But alignment: .leading in text at RTL — already right edge. If explicitly specify .trailing, everything flips. Custom Shape through Path drawn in absolute coordinates — mirror manually through .environment(\.layoutDirection, .rightToLeft) combined with transform.
SF Symbols icons: some have RTL-variant (e.g., arrow.right → auto arrow.left at RTL). But logos and brand icons can't mirror — for them .semanticContentAttribute = .forceLeftToRight.
Android — LayoutDirection and Drawable
android:supportsRtl="true" in manifest — mandatory, but insufficient. start/end instead of left/right in XML attributes (paddingStart, layout_marginEnd) — Lint catches not everything, especially in programmatically created LayoutParams.
VectorDrawable with autoMirrored="true" — correct way to mirror icons. But BitmapDrawable and PNG from mipmap don't mirror auto. Either rotationY = 180f through code or separate resource in drawable-ldrtl-*.
In Jetpack Compose Modifier.padding(start = 16.dp) correctly mirrors at RTL, Modifier.offset(x = 16.dp) — no. Difference unclear until run on Arabic locale. Alignment.Start and Alignment.End work correctly; Alignment.Left — no.
Animations. SlideInHorizontally in Compose takes lambda initialOffsetX: for LTR this -fullWidth, for RTL — +fullWidth. Without checking LocalLayoutDirection.current animation comes from wrong side.
How We Implement This
Audit starts with running app on Arabic locale through adb shell setprop persist.sys.locale ar-AE (Android) and Settings → General → Language & Region (iOS Simulator) — reveals 80% issues before fixes.
On iOS work with UIView.appearance(whenContainedInInstancesOf:) for system semanticContentAttribute inheritance, rewrite custom draw() for conditional flipping, check all NSTextAlignment.natural vs .left in old XIB files (.natural mirrors, .left — no).
On Android conduct lint audit on hardcoded-left/right attributes, replace LayoutParams programmatically through MarginLayoutParamsCompat.setMarginStart/End, add autoMirrored to direction vector icons.
Flutter requires separate attention: TextDirection.rtl passed through Directionality widget. But CustomPainter draws in absolute Canvas coordinates — mirror through canvas.scale(-1, 1) with canvas.translate(-size.width, 0). Row with textDirection: TextDirection.rtl and standard Material widgets work correctly; problems — in custom components and animations through AnimationController with hardcoded offset.
React Native — I18nManager.isRTL for conditional logic, flexDirection: 'row' mirrors at RTL auto on iOS, Android — only React Native 0.63+. For old versions need I18nManager.forceRTL(true) with app restart.
Typical Review-Skipped Errors
- Hardcoded
textAlign: 'left'in custom components instead'auto'or'start' -
transform: [{scaleX: -1}]applied to icon without checkingI18nManager.isRTL— icon mirrors always - Numbers in Arabic text:
١٢٣(eastern arabic numerals) vs123— depends on display context, need explicitly setNSLocale/Locale - Bidirectional text (Arabic + English name):
NSAttributedStringwith explicitNSWritingDirectionAttributeNameotherwise word order breaks
Work Stages
- Audit — run on Arabic/Hebrew locale, screenshots of all screens, compile artifact list
- Classification — layout issues, icon mirroring, text alignment, animation direction
- Fixes — iteratively by components, starting with navigation and main screens
- Testing — on real devices (Samsung with One UI changes some component behavior vs AOSP)
- RTL screenshot tests — add to CI, regressions caught automatically
Full RTL audit and implementation for medium-scale application: 3-5 days. Cost depends on screen count and platforms.







