在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、RTL 是什么

RTL(Right-To-Left) 是从右向左书写的文字方向,全球使用者约 4 亿人,覆盖阿拉伯语(ar)、希伯来语(he)、波斯语(fa)、乌尔都语(ur)、普什图语(ps)等。支持 RTL 绝不是「把文字右对齐」这么简单,而是整个布局逻辑的镜像

  • 阅读顺序:从右上角开始,逐行向下;
  • 行内顺序:Row 的首元素位于最右侧;
  • 对齐语义:TextAlign.Start 在 RTL 环境下表现为右对齐;
  • 图标/箭头:前进箭头应向左指(镜像翻转);
  • 手势方向:左滑/右滑的语义同样需要镜像。

如果不做镜像,RTL 用户看到的界面顺序与阅读习惯完全相反,产品会被判定为「不可用」。本应用以「中东新闻」为演示场景,用中文(LTR)与阿拉伯语(RTL)双语言展示:语言切换的瞬间,文本方向、Row 排布、对齐方式全部镜像,让开发者直观理解 RTL 的完整技术栈。

二、数据模型:方向显式携带

2.1 语言配置

本应用的做法是数据模型显式携带方向字段,而不是在代码里散落硬编码判断:

interface LangCfg {
  code: string;   // locale 代码,如 zh_CN / ar_AE
  name: string;   // 语言显示名
  flag: string;   // 国旗 emoji
  dir: string;    // 'ltr' | 'rtl',方向元数据
}

const LANGS: LangCfg[] = [
  { code: 'zh_CN', name: '简体中文', flag: '🇨🇳', dir: 'ltr' },
  { code: 'ar_AE', name: 'العربية', flag: '🇦🇪', dir: 'rtl' }
];

方向字段 dir 有两个作用:

  1. 状态展示:语言徽章上直接显示 (LTR) / (RTL),用户对当前方向一目了然;
  2. 逻辑判定isRtl() 只需一行即可收敛方向判断,供全页复用:
private isRtl(): boolean {
  return this.currentLocale === 'ar_AE';
}

把方向判断收敛到单一方法里,是 RTL 工程化的第一原则——不要在页面各处写 if (locale === 'ar') 这种散落的判断,否则后续新增波斯语、希伯来语时,每处都要改。用 dir 字段驱动判断,新增语言只需在 LANGS 里加一行。

2.2 新闻数据双语文案

演示数据同时携带中阿双语,由 isRtl() 决定取哪份:

interface NewsItem {
  id: string;
  zhTitle: string;
  arTitle: string;
  zhSummary: string;
  arSummary: string;
  timeZh: string;
  timeAr: string;
  categoryZh: string;
  categoryAr: string;
  viewsZh: string;
  viewsAr: string;
}

private titleOf(n: NewsItem): string {
  return this.isRtl() ? n.arTitle : n.zhTitle;
}

private summaryOf(n: NewsItem): string {
  return this.isRtl() ? n.arSummary : n.zhSummary;
}

注意这里的抽取逻辑:UI 永远只调用 titleOf(n),不关心当前语言。切换语言后 @State currentLocale 变化触发整页重渲染,所有 xxxOf(n) 方法重新执行,文本层瞬间切换——业务代码与方向判断完全解耦。

三、文本层:bidi 混排(系统负责)

3.1 什么是 bidi

一个字符串同时含阿拉伯语、英文、数字、标点时,需要 bidi(Bidirectional Text,双向文本)算法决定每一段的显示顺序。Unicode 标准用 UAX#9 定义了这一算法,ICU 在其基础上实现了完整方案。

经典例子:

'Order #1001 costs $50.50'     // 在 RTL 环境下:整句从右读,数字段内部保持 LTR
'مرحبا بالعالم رقم ١٢٣'         // 阿语文本 + 阿拉伯-印度数字混排

关键规律:数字与英文内嵌段的方向不受外层段落方向影响#1001 即使身处 RTL 段落,仍按 1001 从左到右读;$50.50 内部仍是 LTR 序列。这是因为 bidi 算法为每个字符分配方向类别,再用「隔离/嵌入」规则确定显示顺序。

3.2 Text 组件自动处理

HarmonyOS 的 Text 组件内部基于 ICU 的 bidi 算法自动排布,阿语文本无需任何额外处理即可正确显示。这是文本层的核心结论:Text 负责 bidi,开发者不需要手写方向解析。

本应用的 bidi 混排演示区:

Text(this.isRtl()
  ? (this.showBidi
    ? 'نموذج مختلط: الإصدار iOS 17.2 يدعم 120 دولة و 30 لغة.'
    : 'هاتفك الجديد iPhone 15 Pro متوفر الآن بسعر 999$ فقط.')
  : (this.showBidi
    ? '混排样例:iOS 17.2 支持 120 个国家、30 种语言。'
    : '你的新手机 iPhone 15 Pro 现在仅售 $999。'))
  .fontSize(15).fontColor('#1F2937')
  .textAlign(this.isRtl() ? TextAlign.End : TextAlign.Start)
  .width('100%')

iOS 17.2120iPhone 15 Pro999$ 这些内嵌的 LTR 段在整句 RTL 中保持自身方向——这正是 bidi 算法的价值。Toggle 开关在两组样例间切换,方便用户反复观察「数字/英文段在阿语句子里的位置变化」。

3.3 混排的坑

  • 不要手工拼接方向标记:有些开发者试图用 Unicode 控制符(如 RLE U+202B、PDF U+202C)手动强制方向,这极易出错且难以维护,交给系统算法即可;
  • 测试要覆盖真实数据:订单号、版本号、价格这类「数字+字母」混合串是最容易出问题的,必须用真机 + 阿语键盘实测;
  • emoji 与国旗:emoji 是方向中立的,但 ZWJ 序列(如家庭 emoji)在 RTL 下可能有细微差异,常规场景可忽略。

四、布局层:镜像(开发者负责)

4.1 Row 顺序镜像

文本层由系统搞定,布局层必须开发者手动处理。最典型的是 Row 的顺序:LTR 下第一个子组件在左,RTL 下应镜像到右。

新闻列表行:

Row() {
  Text(this.catOf(n)).fontSize(10).fontColor('#B45309')
    .padding({ left: 8, right: 8, top: 3, bottom: 3 }).backgroundColor('#FEF3C7').borderRadius(8)
  Text(this.timeOf(n)).fontSize(11).fontColor('#9CA3AF')
    .layoutWeight(1).textAlign(this.isRtl() ? TextAlign.Start : TextAlign.End)
}.width('100%')
  • LTR:分类徽标在左,时间在右;
  • RTL(镜像):分类徽标在右,时间在左。

4.2 详情页返回键镜像

返回键是最容易被忽略的镜像点。LTR 下「← 返回」在左,RTL 下「→ عودة」在右:

Row() {
  Text(this.T(K.back)).fontSize(14).fontColor('#B45309')
  Text(this.T(K.readMore)).fontSize(12).fontColor('#B45309').layoutWeight(1)
    .textAlign(this.isRtl() ? TextAlign.End : TextAlign.Start)
}.width('100%')
.onClick(() => { this.selectedId = ''; })

注意文案本身也镜像了:中文「← 返回」vs 阿语「→ عودة」,箭头方向相反。这提醒我们:文案翻译也要遵循方向习惯,翻译团队应知晓目标语言的方向。

4.3 生产环境:容器级 direction

本应用为演示「手动镜像」的细节,用 isRtl() 逐处判断。生产环境更推荐容器级 direction 属性——给根容器设置 direction: Direction.Rtl,整个子树自动镜像:

Column()
  .direction(Direction.Rtl)   // 整棵子树镜像,Row 顺序、对齐全部反转

两种方式的取舍:

方式 优点 缺点
手动 isRtl() 判断 精细控制、逐个组件验证 代码分散、易漏改
容器级 direction 一处声明全局生效、零遗漏 第三方组件可能不跟随、调试需全局视角

实际项目建议:框架性容器用 direction,业务组件用 Start/End 逻辑对齐,两者配合。

五、对齐:用 Start/End 而非 Left/Right

5.1 逻辑方向 vs 物理方向

TextAlign.Left / TextAlign.Right 是物理方向,写死不随语言变化;TextAlign.Start / TextAlign.End 是逻辑方向,LTR 下 Start=左,RTL 下 Start=右。

.textAlign(this.isRtl() ? TextAlign.End : TextAlign.Start)

本应用所有对齐都遵循这一原则。段落/列表项在 RTL 下右对齐,数字/百分比等「方向中立」内容由 bidi 算法保证按数字方向显示。

5.2 为什么是硬编码契约

Start/End 列为团队编码规范,比在 code review 时逐个检查 Left/Right 高效得多。lint 规则可以直接禁止 TextAlign.Left / TextAlign.Right 出现,从工具链层面杜绝回归。

六、状态管理与重渲染

6.1 语言状态持久化

@StorageLink(STORAGE_LOCALE) currentLocale: string = DEFAULT_LOCALE;

aboutToAppear(): void {
  if (!AppStorage.get<string>(STORAGE_LOCALE)) {
    AppStorage.setOrCreate(STORAGE_LOCALE, DEFAULT_LOCALE);
  }
  PersistentStorage.persistProp(STORAGE_LOCALE, DEFAULT_LOCALE);
}

@StorageLink + PersistentStorage 的组合:语言选择持久化到磁盘,下次启动恢复上次的语言——RTL 用户不需要每次打开 App 重新切语言。

6.2 状态驱动全页镜像

@State currentLocale / selectedId / showBidi 三个状态驱动整页:

  • currentLocale 变化 → 文本层(xxxOf)与布局层(isRtl)同步刷新;
  • selectedId 变化 → 列表/详情切换;
  • showBidi 变化 → 混排样例切换。

语言一切换,方向状态、混排样例、布局演示同步变化,直观展示镜像效果。这正是 ArkUI 声明式 UI 的优势:状态是唯一事实,UI 是状态的投影,RTL 切换无需任何命令式刷新代码。

七、ArkTS 兼容要点

  1. catch 不需要类型注解:本页几乎没有可能抛错的 I/O 与 intl 调用,try/catch 可省略;
  2. UI 分支内不声明 const:所有派生数据抽成私有方法(titleOf() / summaryOf() / detail()),保证 @Builder 与 UI 分支的 ArkTS 约束;
  3. ForEach key 生成器用稳定值:新闻 n.id、语言 l.code,避免重渲染时组件复用错乱;
  4. ?? 空值合并curCfg() / detail()?? LANGS[0] 兜底,保证非空返回,避免 UI 判空分支;
  5. 模板字符串${l.flag} ${l.name} (${l.dir.toUpperCase()}) 的用法在 ArkTS 中受支持,但注意字符串拼接较多时应抽方法。

八、生产 RTL 检查清单

检查项 说明 本应用对应
Row 顺序 首元素在 RTL 下应在右 列表行、详情元信息行
TextAlign 用 Start/End 而非 Left/Right 所有 .textAlign()
图标/箭头 前进/返回箭头需水平翻转 返回键文案镜像
输入框光标 光标插入位置、选中方向 本应用无输入场景
滚动条 滚动条位置、滚动方向 Scroll 默认跟随
手势 左滑/右滑语义镜像 本应用无手势

九、扩展与进阶

  1. 自动检测系统方向i18n.System.getSystemLanguage() 返回系统语言,判断其方向后自动设置全局 direction,用户无需手动切换;
  2. 数字本地化联动:阿语环境可选用阿拉伯-印度数字(٠١٢٣٤٥٦٧٨٩),intl.NumberFormatnumberingSystem 选项可控制;
  3. TTS 朗读顺序:无障碍朗读应遵循 RTL 顺序,配合 Accessibility 服务配置;
  4. 动画镜像:滑入/滑出、进度条填充方向的动画同样需要镜像。

十、小结

RTL 支持 = 文本 bidi 自动排布(系统负责) + 布局镜像(开发者负责)。本应用把「方向状态 → 混排展示 → 布局镜像」三步演示闭环:Text 组件按 UAX#9 自动处理阿语与数字英文混排;Row 顺序、TextAlign 对齐、返回键方向由 isRtl() 显式镜像。理解了这一层,出海中东/以色列的电商、社交、新闻类产品都能快速落地正确的 RTL 体验。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐