Supporting Arabic and Hebrew isn't just about flipping text direction—it's about discovering that your app's layout assumptions are fundamentally broken. When you first enable RTL support, you'll find that each platform fails in its own spectacular way, from iOS constraint explosions to Android icons flipping when they absolutely shouldn't.
Here's the thing that catches most teams off guard: the platform-specific nature of RTL failures means your testing strategy needs to be equally platform-specific. What works perfectly on iOS will break differently on Android, and React Native adds its own delightful complications on top of both.
iOS Auto Layout Constraint Failures: Leading vs Left
iOS Auto Layout handles RTL reasonably well—until you mix leading/trailing constraints with left/right constraints in the same view hierarchy. When Interface Builder generates constraints, it often defaults to left and right, creating a constraint conflict that only surfaces when RTL is enabled.
Here's what a typical constraint failure looks like:
// This constraint setup will break in RTL
titleLabel.leftAnchor.constraint(equalTo: containerView.leftAnchor, constant: 16)
subtitleLabel.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 8)
The problem: leftAnchor stays left in RTL, but leadingAnchor flips to right, creating impossible constraints. Your console fills with constraint warnings, and layouts break unpredictably.
The fix: Use semantic constraints consistently:
// Correct: All semantic constraints
titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16)
subtitleLabel.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 8)
// OR: All positional constraints (for elements that shouldn't flip)
logoImageView.leftAnchor.constraint(equalTo: containerView.leftAnchor, constant: 16)
versionLabel.rightAnchor.constraint(equalTo: containerView.rightAnchor, constant: -16)
Testing strategy: Enable "Right to Left" in Simulator settings, then navigate through every screen. Constraint warnings will appear in the console immediately, but visual issues might be subtle.
Pro tip: Set up a scheme that launches your app with -AppleLanguages (ar) and -NSForceRightToLeftWritingDirection YES arguments to catch these issues during development.
Android Drawable Mirroring Gotchas
Android automatically mirrors most drawables in RTL layouts, which sounds helpful until your hamburger menu becomes a backward hamburger menu. The system assumes all icons should flip, but many shouldn't—logos, media controls, and directionally-neutral icons look wrong when mirrored.
<!-- This drawable will flip incorrectly in RTL -->
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_hamburger_menu" />
When RTL is enabled, Android automatically mirrors this drawable, making your three-line menu icon look backwards—which confuses users who expect consistent iconography.
The fix: Use autoMirrored attribute strategically:
<!-- In your drawable XML files -->
<!-- ic_back_arrow.xml - SHOULD flip -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<!-- arrow pointing left becomes arrow pointing right in RTL -->
</vector>
<!-- ic_hamburger_menu.xml - should NOT flip -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="false"
android:viewportWidth="24"
android:viewportHeight="24">
<!-- stays the same in RTL -->
</vector>
Which icons should flip?
- Navigation arrows (back, forward, next)
- Directional indicators (sort arrows, expand/collapse)
- Layout-related icons (align left/right)
Which icons shouldn't flip?
- Logos and branding
- Media controls (play, pause, stop)
- Search icons
- Hamburger menus
- Checkmarks and X icons
Testing strategy: Create a simple activity that displays all your app's icons in a grid, then test with RTL enabled. You'll immediately see which ones look wrong when flipped.
React Native FlexDirection Inheritance Issues
React Native's RTL support depends on the flexDirection being row to automatically flip layouts. But flex inheritance creates scenarios where child components don't flip when their parents do, leading to inconsistent layouts that break the RTL experience.
// This layout breaks in subtle ways
const ProfileCard = () => (
<View style={{ flexDirection: 'column' }}>
<View style={{ flexDirection: 'row' }}> // This flips correctly
<Image source={avatar} />
<Text>{name}</Text>
</View>
<View style={{ justifyContent: 'flex-end' }}> // This doesn't flip
<Button title="Edit" />
</View>
</View>
);
The image and text flip correctly because their container has flexDirection: 'row', but the button stays right-aligned because justifyContent: 'flex-end' on a column doesn't get RTL treatment.
The fix: Use the I18nManager to explicitly handle RTL-aware styling:
import { I18nManager } from 'react-native';
const ProfileCard = () => {
const isRTL = I18nManager.isRTL;
return (
<View style={{ flexDirection: 'column' }}>
<View style={{ flexDirection: 'row' }}>
<Image source={avatar} />
<Text>{name}</Text>
</View>
<View style={{
alignItems: isRTL ? 'flex-start' : 'flex-end'
}}>
<Button title="Edit" />
</View>
</View>
);
};
For more complex layouts, consider creating RTL-aware style utilities:
const createRTLStyle = (ltrStyle, rtlStyle = {}) => {
return I18nManager.isRTL ? { ...ltrStyle, ...rtlStyle } : ltrStyle;
};
const styles = StyleSheet.create({
container: createRTLStyle(
{ paddingLeft: 16, marginRight: 8 },
{ paddingLeft: 8, marginRight: 16 }
),
});
Testing strategy: Enable RTL in your device settings (Settings > Developer Options > Force RTL layout direction), then test on both iOS and Android since they handle flex inheritance slightly differently.
Text Input Cursor Positioning Bugs
Text input fields in RTL languages expose cursor positioning bugs that don't appear during LTR testing. The cursor might appear at the wrong end of the field, jump unexpectedly during typing, or disappear entirely when switching between number input and text input.
On iOS, this manifests as the cursor starting at the visual left side of RTL text fields:
// Problem: Cursor starts at wrong position
let textField = UITextField()
textField.placeholder = "أدخل النص هنا" // Arabic placeholder
textField.textAlignment = .natural // Should adapt to RTL
On Android, similar issues occur with EditText views:
<!-- Cursor positioning issues in RTL -->
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="أدخل النص هنا"
android:textDirection="firstStrongRtl" />
The fix varies by platform:
iOS solution:
// Explicitly set text alignment and writing direction
textField.textAlignment = .natural
textField.semanticContentAttribute = .forceRightToLeft
// For mixed content inputs (like phone numbers), be more specific
phoneField.textAlignment = .right
phoneField.keyboardType = .numberPad
Android solution:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="أدخل النص هنا"
android:textDirection="anyRtl"
android:textAlignment="viewStart"
android:gravity="start" />
React Native solution:
<TextInput
placeholder="أدخل النص هنا"
style={{
textAlign: I18nManager.isRTL ? 'right' : 'left',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr'
}}
/>
Testing strategy: Test with actual Arabic or Hebrew input, not just placeholders. Pay special attention to fields that accept mixed content (phone numbers, email addresses) and fields that switch input types dynamically.
Navigation Flow Reversals That Confuse Users
RTL languages reverse navigation expectations—back buttons should be on the right, forward progression moves right-to-left, and hierarchical navigation behaves differently. But platforms handle this inconsistently, and developers often override the automatic behavior incorrectly.
The most common mistake is hardcoding navigation button positions:
// iOS - Wrong: Hardcoded left position
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back",
style: .plain,
target: self,
action: #selector(goBack))
// Correct: Use semantic positions
navigationItem.backBarButtonItem = UIBarButtonItem(title: "Back",
style: .plain,
target: nil,
action: nil)
For Android, the issue appears in custom navigation implementations:
// Wrong: Hardcoded left position
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.arrow_left);
// Better: Let the system handle RTL
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Use an auto-mirrored drawable that flips appropriately
React Native navigation requires explicit RTL configuration:
// React Navigation v6 - Configure RTL support
import { I18nManager } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
// Force RTL layout for navigation
I18nManager.allowRTL(true);
I18nManager.forceRTL(true);
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerBackTitleVisible: false,
// Let the system handle back button positioning
}}
>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Tab bar issues are equally common. iOS and Android handle tab order differently in RTL:
// React Native - Explicit tab ordering for RTL
const TabNavigator = createBottomTabNavigator();
function TabsScreen() {
const tabOrder = I18nManager.isRTL
? ['Settings', 'Profile', 'Search', 'Home']
: ['Home', 'Search', 'Profile', 'Settings'];
return (
<TabNavigator>
{tabOrder.map(screenName => (
<TabNavigator.Screen
key={screenName}
name={screenName}
component={screens[screenName]}
/>
))}
</TabNavigator>
);
}
Testing strategy: Navigate through your entire app flow in RTL mode. Pay attention to whether the navigation feels natural—users should be able to follow the flow intuitively without thinking about direction.
Different platforms handle RTL with varying levels of automation. Understanding what each platform does automatically helps you focus testing efforts on manual fixes:
| RTL Feature |
iOS |
Android |
React Native |
| Text direction |
Automatic |
Automatic |
Automatic |
| Layout mirroring |
Auto (with semantic constraints) |
Automatic |
Manual (flex row only) |
| Icon mirroring |
Manual |
Auto (with opt-out) |
Manual |
| Navigation flow |
Auto (system controls) |
Auto (system controls) |
Manual |
| Input cursor |
Manual fixes needed |
Manual fixes needed |
Manual |
| Custom animations |
Manual |
Manual |
Manual |
iOS gives you the most automatic RTL support if you use semantic constraints consistently. The main manual work is fixing constraint conflicts and handling custom UI elements.
Android automatically handles the most cases, but you need to explicitly control icon mirroring and fix text input issues. The autoMirrored attribute gives you fine-grained control.
React Native requires the most manual intervention, but gives you the most control over the RTL experience. You'll need to handle most layout adjustments explicitly.
This variance means your testing strategy should be platform-specific. iOS apps need thorough constraint testing, Android apps need drawable review, and React Native apps need comprehensive layout testing.
Testing Strategy That Actually Catches RTL Issues
Effective RTL testing goes beyond enabling RTL mode and clicking around. Each platform surfaces different issues at different times, requiring targeted testing approaches.
Automated testing setup:
# iOS Simulator - Add to your test scheme
-AppleLanguages (ar)
-NSForceRightToLeftWritingDirection YES
# Android emulator - Force RTL for all apps
adb shell settings put global debug.force_rtl 1
# React Native - Add to your dev environment
import { I18nManager } from 'react-native';
if (__DEV__) {
I18nManager.forceRTL(true);
}
Manual testing checklist:
1. Navigate through every screen with RTL enabled
2. Test all form inputs with actual Arabic/Hebrew text
3. Verify that directional icons look correct
4. Check that navigation flows feel intuitive
5. Test edge cases like empty states and loading screens
6. Verify that animations move in the expected direction
The key insight from teams that successfully support RTL is this: test with real RTL content, not just flipped English text. Many RTL issues only appear when you're working with actual Arabic or Hebrew strings that have different text length and character behavior than Latin text.
For comprehensive string management that properly handles RTL languages, check out our guide on cross-platform string management and learn about platform-specific localization gotchas that affect RTL implementations.
Supporting RTL languages effectively means acknowledging that each platform breaks differently and requires targeted fixes. iOS constraint management, Android drawable control, and React Native manual layout adjustments each need specific approaches and testing strategies.
The teams that succeed with RTL support treat it as a first-class design consideration, not an afterthought. They test with real RTL content throughout development, understand their platform's automatic behaviors, and build manual fixes for everything else.
Start with one platform, get RTL working properly there, then apply the lessons learned to the other platforms. Don't try to solve everything at once—RTL support is complex enough without trying to coordinate fixes across multiple platforms simultaneously.