GetTranslated.AI
Mobile App Development

5 Platform-Specific Ways RTL Languages Break Your Mobile App (And How to Fix Each One)

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.


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.


Platform Comparison: What's Automatic vs Manual

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.


Takeaway: RTL Success Requires Platform-Specific Strategies

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.

Ready to localize your app?

Get started free — no credit card required.

Start Translating →