为 React Native 应用提供从右到左布局支持
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
将应用发布到应用商店后,国际化是扩大受众范围的下一步。全球有超过 20 个国家和众多人口使用从右到左(RTL)的语言。因此,为这些用户提供 RTL 支持是必要的。
我们很高兴地宣布,React Native 已经改进以支持 RTL 布局。该功能现在已在 react-native 的主分支中提供,并将在下一个候选版本(RC)中发布:v0.33.0-rc。
这涉及到更改 RN 使用的核心布局引擎 css-layout、RN 核心实现,以及特定的开源 JS 组件以支持 RTL。
为了在生产环境中测试 RTL 支持,最新版本的 Facebook Ads Manager 应用(首个跨平台 100% RN 应用)现已提供阿拉伯语和希伯来语的 RTL 布局,支持 iOS 和 Android。以下是这些 RTL 语言下的效果:

RN 中支持 RTL 的更改概述
css-layout 的布局中已有 start 和 end 的概念。在从左到右(LTR)布局中,start 表示 left,end 表示 right。但在 RTL 中,start 表示 right,end 表示 left。这意味着我们可以让 RN 依赖 start 和 end 的计算来确定正确的布局,包括 position、padding 和 margin。
此外,css-layout 已经让每个组件的方向继承自其父组件。这意味着,我们只需将根组件的方向设置为 RTL,整个应用就会翻转。
下图从高层次描述了这些更改:

这些更改包括:
-
在 RN 核心实现中,将
left和right映射为start和end(针对影子节点) -
并公开了一个桥接实用模块来帮助控制 RTL 布局
通过此更新,当您为应用启用 RTL 布局时:
-
每个组件的布局将水平翻转
-
如果您使用的是支持 RTL 的开源组件,一些手势和动画将自动具有 RTL 布局
-
只需极少的额外工作,即可让您的应用完全支持 RTL
让应用支持 RTL
-
要支持 RTL,首先需要将 RTL 语言包添加到应用中。
-
在你的应用原生代码起始处调用
allowRTL()函数以启用 RTL 布局。我们提供此工具函数,确保仅在你的应用准备就绪时才应用 RTL 布局。示例如下:iOS:
// in AppDelegate.m
[[RCTI18nUtil sharedInstance] allowRTL:YES];Android:
// in MainActivity.java
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
sharedI18nUtilInstance.allowRTL(context, true); -
对于 Android 应用,你需要在
AndroidManifest.xml文件的<application>元素中添加android:supportsRtl="true"属性。
现在,当你重新编译应用并将设备语言切换为 RTL 语言(如阿拉伯语或希伯来语)时,应用布局应会自动切换为 RTL 模式。
编写支持 RTL 的组件
通常情况下,大多数组件已原生支持 RTL 布局,例如:
- 从左到右布局
- 从右到左布局
但需要注意几种特殊情况,这时你需要使用 I18nManager。在 I18nManager 中,常量 isRTL 可判断当前布局是否为 RTL,从而进行相应调整。
具有方向含义的图标
当组件包含图标或图像时,它们在 LTR 和 RTL 布局中的显示方向不会自动改变。因此你需要根据布局手动调整方向:
- 从左到右布局
- 从右到左布局
以下两种方法可实现图标方向切换:
-
为图像组件添加
transform样式:<Image
source={...}
style={{transform: [{scaleX: I18nManager.isRTL ? -1 : 1}]}}
/> -
或根据布局方向切换图像源:
let imageSource = require('./back.png');
if (I18nManager.isRTL) {
imageSource = require('./forward.png');
}
return <Image source={imageSource} />;
手势与动画
在 Android 和 iOS 开发中,切换到 RTL 布局时手势和动画方向与 LTR 相反。目前在 React Native 中,手势和动画的支持不在核心代码层,而是在组件层。好消息是部分组件(如 SwipeableRow 和 NavigationExperimental)已支持 RTL 布局。但其他涉及手势的组件仍需手动适配。
SwipeableRow 是演示 RTL 手势支持的典型范例。


手势示例
// SwipeableRow.js
_isSwipingExcessivelyRightFromClosedPosition(gestureState: Object): boolean {
// ...
const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
return (
this._isSwipingRightFromClosed(gestureState) &&
gestureStateDx > RIGHT_SWIPE_THRESHOLD
);
},
动画示例
// SwipeableRow.js
_animateBounceBack(duration: number): void {
// ...
const swipeBounceBackDistance = IS_RTL ?
-RIGHT_SWIPE_BOUNCE_BACK_DISTANCE :
RIGHT_SWIPE_BOUNCE_BACK_DISTANCE;
this._animateTo(
-swipeBounceBackDistance,
duration,
this._animateToClosedPositionDuringBounce,
);
},
维护 RTL 应用
即使在初始发布 RTL 兼容应用后,您仍可能需要迭代新功能。为提高开发效率,I18nManager 提供了 forceRTL() 函数,无需更改测试设备语言即可快速进行 RTL 测试。您可以在应用中添加简单的切换开关实现此功能。以下是 RNTester 中 RTL 示例的参考实现:

<RNTesterBlock title={'Quickly Test RTL Layout'}>
<View style={styles.flexDirectionRow}>
<Text style={styles.switchRowTextView}>forceRTL</Text>
<View style={styles.switchRowSwitchView}>
<Switch
onValueChange={this._onDirectionChange}
style={styles.rightAlignStyle}
value={this.state.isRTL}
/>
</View>
</View>
</RNTesterBlock>;
_onDirectionChange = () => {
I18nManager.forceRTL(!this.state.isRTL);
this.setState({isRTL: !this.state.isRTL});
Alert.alert(
'Reload this page',
'Please reload this page to change the UI direction! ' +
'All examples in this app will be affected. ' +
'Check them out to see what they look like in RTL layout.',
);
};
开发新功能时,您可以轻松切换此按钮并重新加载应用查看 RTL 布局。这样做的好处是无需更改语言设置即可测试,但如下一节所述,某些文本对齐方式不会自动改变。因此建议在正式发布前使用 RTL 语言全面测试应用。
限制与未来计划
RTL 支持已覆盖应用中的大部分用户体验,但目前仍存在以下限制:
- 文本对齐行为在 Android 和 iOS 上存在差异
- iOS 中默认文本对齐取决于活动语言包,始终位于固定侧。Android 中则取决于文本内容的语言,例如英语左对齐而阿拉伯语右对齐
- 理论上应保持跨平台一致性,但用户可能偏好不同对齐方式。需更多用户体验研究确定最佳实践
-
不存在"绝对左/右"定位
如前所述,我们将 JS 端的
left/right样式映射为start/end,导致 RTL 布局中代码的left实际显示为"右",right显示为"左"。这减少了代码修改量,但也意味着无法在代码中指定"绝对左侧"或"绝对右侧"。未来可能需要支持组件独立控制布局方向。 -
提升手势和动画的 RTL 开发体验
当前实现手势和动画的 RTL 兼容性仍需较多编程工作。未来将探索更友好的开发方案。
立即尝试!
查看 RNTester 中的 RTLExample 深入了解 RTL 支持,欢迎反馈使用体验!
最后,感谢您的阅读!希望 React Native 的 RTL 支持能帮助您拓展国际用户群体!