【ArkUI 练中学】第17课:国际化与无障碍适配
本节目标
· 理解资源限定词机制,掌握多语言资源的目录组织与 $r() 引用方式
· 掌握复数处理、日期/数字/货币的区域化格式化方法
· 掌握应用内运行时语言切换的实现方式,理解持久化语言偏好的必要性
· 掌握无障碍属性配置,能够为文本类与非文本类组件正确设置屏幕朗读内容
· 理解 accessibilityGroup、accessibilityText、accessibilityDescription 等属性的适用场景
· 掌握 RTL 镜像布局的适配方法,能够将绝对方向布局改造为 start/end 相对方向布局
· 理解多设备适配的断点系统,掌握 GridRow + GridCol 栅格布局的实现方式
· 能够为一个应用建立完整的国际化、无障碍与多设备适配方案
一、资源管理与多语言适配
1.1 为什么不能硬编码
把界面上的文字、颜色和图片直接写进 ArkTS,短期看很省事;一旦应用需要切换语言、适配深色模式或调整品牌视觉,散落在页面里的硬编码就会迅速成为维护负担。具体来说,硬编码至少带来以下问题:文案散落在代码中,翻译人员难以统一处理;深色模式切换时需要在多个组件中手动判断颜色;图片路径与业务代码耦合;相同文案重复出现无法保证用词一致。
更稳妥的方式是让 ArkTS 只引用资源标识,把具体内容交给资源系统管理。
1.2 资源目录结构
Stage 模型的模块资源通常位于 entry/src/main/resources,核心结构如下:
entry/src/main/resources/
├── base/ # 默认资源目录(兜底)
│ ├── element/
│ │ ├── string.json # 字符串资源
│ │ ├── color.json # 颜色资源
│ │ ├── float.json # 尺寸资源
│ │ └── plural.json # 复数资源
│ ├── media/ # 图片资源
│ └── profile/ # 页面配置
├── zh_CN/ # 中文限定目录
│ └── element/string.json
├── en_US/ # 英文限定目录
│ └── element/string.json
└── dark/ # 深色模式限定目录
└── element/color.json
base 是默认资源目录。当更具体的限定词目录没有匹配内容时,系统会回退到 base。zh_CN、en_US 是语言与地区限定目录,dark 用于深色模式。资源目录名称和文件格式有固定约定,不建议自行创建任意层级来代替限定词目录。
1.3 定义与引用字符串资源
在 base/element/string.json 中准备默认文案:
{
"string": [
{ "name": "profile_title", "value": "个人中心" },
{ "name": "welcome_user", "value": "你好,%s" },
{ "name": "save", "value": "保存" }
]
}
在 ArkUI 组件中通过 $r 引用:
@Entry
@Component
struct ProfilePage {
build() {
Column({ space: 16 }) {
Text($r('app.string.profile_title'))
.fontSize(22)
.fontWeight(FontWeight.Bold)
Button($r('app.string.save'))
}
.width('100%')
.padding(20)
}
}
app 表示应用资源,string 表示资源类型,末尾是资源名。资源名应表达语义,例如 profile_title,不要使用 text1、label_a 这类难以维护的名称。$r 引用要求 name 必须在所有语言目录中保持一致,只允许 value 不同。
1.4 资源键命名规范
推荐使用语义化键名:
{ "name": "mode_time_attack_desc", "value": "分秒必争,限时收集挑战" }
不推荐把键命名为拼音或 text1、label2。语义键让开发者无需打开 JSON 就知道用途,也能区分同一个中文词在不同上下文中的翻译。按钮、标题、描述和提示应使用不同键——很多语言的词形会随上下文变化,不能因为中文看起来相同就强行复用。
1.5 复数处理
对于涉及数量变化的文案,可使用 plural.json 定义复数资源,运行时按数量选择文案:
{
"plural": [
{
"name": "file_count",
"value": [
{ "quantity": "one", "value": "%d 个文件" },
{ "quantity": "other", "value": "%d 个文件" }
]
}
]
}
或使用 resourceManager.getPluralStringValue(id, num) 获取复数化字符串。
1.6 日期、数字、货币的区域化
使用 @ohos.intl 进行本地化格式化,避免硬编码格式:
import { intl } from '@kit.LocalizationKit';
// 日期格式化
const dateFmt = new intl.DateTimeFormat('zh-CN', {
dateStyle: 'medium',
timeStyle: 'short'
});
const dateStr = dateFmt.format(new Date());
// 货币格式化
const numFmt = new intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
});
const priceStr = numFmt.format(199.99);
二、无障碍适配
2.1 无障碍能力概述
HarmonyOS 系统提供了屏幕朗读、大字体、高对比度文字、色彩校正、颜色反转、单声道音频、音量平衡、屏幕触控等一整套无障碍系统服务。开发者的核心任务是让屏幕朗读能够正确提取和播报界面元素信息。
无障碍文本的优先级大于显示文本,即当无障碍文本不为空时,会朗读无障碍文本,否则朗读显示文本。
2.2 accessibilityText 无障碍文本
accessibilityText 用于为组件设置屏幕朗读的文本内容。当组件不包含可被屏幕朗读的文本属性(如 Image 等本身不直接显示文本的组件)时,屏幕朗读选中此组件时不播报,使用者无法清楚地知道当前选中了什么组件。为解决此问题,开发人员可为不包含文本属性的组件设置无障碍文本。
// 非文本类控件使用 accessibilityText 提供朗读信息
Image($r('app.media.banner'))
.width('100%')
.height(200)
.accessibilityText('首页轮播图:新用户专享优惠活动')
// 文本类控件如额外提供颜色等视觉信息,可用无障碍文本补充
Button('提交')
.accessibilityText('提交表单,点击后将验证并保存信息')
设计规则:对于文本类控件,尽量使用显示文本来表达信息,使视障用户和视力健全用户可以获取到相同的信息。如果除显示文本外还额外提供了颜色等视觉效果,可采用无障碍文本为视障用户提供更多信息。对于非文本类控件,可采用无障碍文本为视障用户提供朗读信息。
2.3 accessibilityGroup 无障碍分组
accessibilityGroup 设置为 true 时表示该组件及其所有子组件为一个整体的可选中组件,无障碍辅助服务将不再关注其子组件内容。
若组件启用无障碍分组,当组件不包含通用文本属性且未设置无障碍文本时,将默认拼接其子组件的通用文本属性作为组件的合并文本。若某一子组件没有通用文本属性,则忽略该子组件不进行拼接。
// 将日期、天气、温度等多个信息合并为一个可选中组件
Row() {
Text('周一').fontSize(14)
Text('晴').fontSize(14)
Text('25°C').fontSize(14)
}
.accessibilityGroup(true)
.accessibilityText('周一,晴天,气温25度')
典型场景:默认只有子组件才能获取焦点,日期、天气、温度等信息在每个组件独立获取焦点时分别朗读,体验不佳。将 accessibilityGroup 设置为 true 后,子组件无法获取焦点,整个卡片作为一个整体被选中朗读。
2.4 accessibilityDescription 无障碍说明
accessibilityDescription 用于为用户进一步说明当前组件,尤其是当操作后果无法从组件本身属性与无障碍文本中了解到时。组件被选中时,先播报组件的文本属性(或 accessibilityText),再播报无障碍说明属性的内容。
Button('删除')
.accessibilityDescription('点击此按钮将永久删除当前条目,操作不可撤销')
2.5 accessibilityLevel 无障碍重要性
accessibilityLevel 用于控制某个组件是否可被无障碍辅助服务所识别:
auto 由系统根据组件类型自动决定是否可被识别,默认为该值。yes 当前组件可被无障碍辅助服务选中。no 当前组件不可被无障碍辅助服务选中。no-hide-descendants 无障碍辅助服务忽略当前组件及其所有子组件。
// 装饰性图片无需被屏幕朗读选中
Image($r('app.media.decorative_line'))
.accessibilityLevel('no')
// 容器组件默认不会被选中,如需使其可被选中
Column() {
// 自定义组件内容
}
.accessibilityLevel('yes')
.accessibilityGroup(true)
.accessibilityText('自定义组件描述')
2.6 无障碍焦点顺序与最佳实践
屏幕朗读适配的核心是“聚焦有序 + 朗读清晰”,确保视障用户逻辑导航和信息获取无障碍。一个业务控件应暴露一个清晰的可朗读节点,避免父子都可访问造成顺序异常或重复播报。
最佳实践要点:
为所有图片添加 accessibilityText 描述内容。使用 accessibilityGroup(true) 将逻辑上属于同一信息的多个子组件合并为一个焦点单元。使用 accessibilityDescription 说明操作后果(如“删除”“提交”等按钮)。装饰性元素设置 accessibilityLevel(‘no’) 避免冗余播报。通过 accessibilityNextFocusId 控制焦点顺序(API 18+),确保朗读顺序符合视觉逻辑。
三、RTL 镜像布局适配
3.1 RTL 布局的基本概念
不同国家对文本对齐方式和读取顺序有所不同,例如英语采用从左到右(LTR)的顺序,阿拉伯语和希腊语则采用从右到左(RTL)的顺序。为满足不同用户的阅读习惯,ArkUI 提供了镜像能力,在特定情况下将显示内容在 X 轴上进行镜像反转,由从左到右显示变成从右到左显示。
当组件满足以下任意条件时,镜像能力生效:
组件的 direction 属性设置为 Direction.Rtl。组件的 direction 属性设置为 Direction.Auto,且当前的系统语言(如维吾尔语、阿拉伯语)的阅读习惯是从右向左。
3.2 全局 RTL 设置
在应用入口通过 Configuration 设置全局布局方向为 RTL:
// 方式一:设置应用级布局方向
import { ConfigurationConstant } from '@kit.AbilityKit';
// 在 EntryAbility 中设置
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 系统语言为 RTL 时自动生效
}
组件级设置:
Row()
.direction(Direction.Rtl) // 强制 RTL
Column()
.direction(Direction.Auto) // 自动跟随系统语言
3.3 使用 start/end 替代 left/right
RTL 适配的关键是将所有绝对方向的描述改为相对方向的描述,即使用 start 和 end 替代 left 和 right。ArkUI 的通用属性中,以下属性需要使用新入参类型适配:
尺寸设置:padding、margin 使用 start / end 替代 left / right。
位置设置:position 使用 start / top 替代 x / y。
import { LengthMetrics } from '@kit.ArkUI';
// ❌ 不推荐:使用绝对方向
Column()
.padding({ left: 16, right: 16 })
.margin({ left: 20 })
// ✅ 推荐:使用 start/end 相对方向,自动适配 RTL
Column()
.padding({
start: LengthMetrics.vp(16),
end: LengthMetrics.vp(16)
})
.margin({ start: LengthMetrics.vp(20) })
需要同时支持 LTR 和 RTL 时使用 API 12 新增的 LocalizedEdges 入参类型,仅支持 LTR 时等同于原来的 x/y 设置。
3.4 RelativeContainer 的对齐适配
在 RelativeContainer 中,alignRules 提供 start 和 end 方向的对齐规则,替换原来的 left 和 right 方向参数以支持 RTL 布局的镜像。
RelativeContainer() {
Text('内容')
.alignRules({
start: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
}
3.5 Canvas 的 RTL 处理
Canvas 组件的绘制内容和坐标均不支持自动镜像,已绘制到 Canvas 上的内容不会跟随系统语言切换自动做镜像效果。需要应用监听到系统语言切换后自行重新绘制。CanvasRenderingContext2D 的文本绘制支持镜像能力,其 direction 属性优先级高于 Canvas 组件的 direction 属性。
3.6 RTL 适配检查清单
支持 RTL 不仅仅是文字方向的切换,更是整个 UI 布局的镜像翻转——导航栏、按钮排列、列表滑动方向都需要相应调整。上线前建议逐项检查:
所有 padding / margin 是否已改为 start / end。所有 position 是否已改为 start / top。图片资源中是否包含方向性图标(如箭头、返回按钮)需要单独提供 RTL 版本。列表滑动删除方向是否正确。RelativeContainer 的 alignRules 是否使用了 start / end。
四、多设备适配
4.1 断点系统
HarmonyOS 提供了一套断点系统,用来根据设备宽度做布局适配。栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题,保证不同设备上各个模块的布局一致性。
常用断点划分如下:
· xs(超小屏) :宽度 < 320vp
· sm(小屏) :320vp ≤ 宽度 < 600vp,对应手机竖屏
· md(中屏) :600vp ≤ 宽度 < 840vp,对应手机横屏、折叠屏展开态
· lg(大屏) :宽度 ≥ 840vp,对应平板、PC
4.2 GridRow 栅格布局
GridRow 是栅格行布局容器,仅可以和栅格子组件 GridCol 在栅格布局场景中使用,支持根据设备尺寸和断点动态调整列数与间距,实现响应式布局。
@Entry
@Component
struct GridLayoutDemo {
build() {
GridRow({
columns: { xs: 2, sm: 4, md: 8, lg: 12 }, // 不同断点下的列数
gutter: { x: 12, y: 12 }, // 间距
breakpoints: { value: ['320vp', '600vp', '840vp'] }
}) {
GridCol({ span: { xs: 2, sm: 2, md: 4, lg: 6 } }) {
this.card('卡片一', '#E8F0FE')
}
GridCol({ span: { xs: 2, sm: 2, md: 4, lg: 6 } }) {
this.card('卡片二', '#FEF0E8')
}
}
.width('100%')
.padding(16)
}
@Builder
card(title: string, bgColor: string) {
Column() {
Text(title).fontSize(18).fontWeight(FontWeight.Bold)
}
.width('100%')
.height(120)
.backgroundColor(bgColor)
.borderRadius(12)
.justifyContent(FlexAlign.Center)
}
}
columns 支持按断点设置不同列数,gutter 设置栅格间距,breakpoints 自定义断点位置。GridCol 的 span 属性支持按断点设置子组件占用的列数。
4.3 媒体查询动态适配
使用 mediaquery 模块可以监听窗口尺寸变化,动态调整 UI 布局:
import { mediaquery } from '@kit.ArkUI';
@Entry
@Component
struct ResponsivePage {
@State currentBreakpoint: string = 'sm';
private listener: mediaquery.MediaQueryListener | null = null;
aboutToAppear(): void {
this.listener = mediaquery.matchMediaSync('(width>=840vp)');
this.listener.on('change', (result: mediaquery.MediaQueryResult) => {
this.currentBreakpoint = result.matches ? 'lg' : 'sm';
});
}
aboutToDisappear(): void {
this.listener?.off('change');
}
build() {
Column() {
if (this.currentBreakpoint === 'lg') {
// 大屏布局:分栏展示
Row() {
this.sidePanel()
this.mainContent()
}
} else {
// 小屏布局:单栏展示
this.mainContent()
}
}
.width('100%')
.height('100%')
}
@Builder
sidePanel() {
Column()
.width(240)
.height('100%')
.backgroundColor('#F5F5F5')
}
@Builder
mainContent() {
Column()
.layoutWeight(1)
.height('100%')
.backgroundColor(Color.White)
}
}
当检测到窗口尺寸跨越断点时,触发组件实例的布局重计算和样式切换,实现从手机竖屏到横屏、折叠屏折叠状态切换以及窗口分屏等场景布局转换的实时适配。
4.4 多设备适配最佳实践
布局统一使用 GridRow 栅格 + sm / md / lg 断点,尺寸单位全用 vp/fp 不用 px,再配合 @ohos.mediaquery 监听屏幕变化做局部调整。组件样式尽量抽成资源文件(color/size 按 dpi 分目录),这样大部分页面不用为每个设备单独写样式。UI 复杂的地方再用 onWindowSizeChange 做分屏/折叠特判。
五、多元化习题
习题 1(判断题)
题目:在 ArkUI 中,$r(‘app.string.xxx’) 引用资源时,资源名 xxx 可以在不同语言目录中使用不同的名称。
答案:错误
解读:$r 引用要求 name 必须在所有语言目录中保持一致,只允许 value 不同。系统根据当前语言环境匹配对应目录中的同名资源,如果名称不一致则无法正确匹配。
习题 2(单选题)
题目:以下哪个属性用于控制组件是否可被无障碍辅助服务识别?
A. accessibilityText
B. accessibilityGroup
C. accessibilityLevel
D. accessibilityDescription
答案:C
解读:accessibilityLevel 用于控制某个组件是否可被无障碍辅助服务所识别。accessibilityText 设置无障碍文本,accessibilityGroup 设置无障碍分组,accessibilityDescription 设置无障碍说明。
习题 3(多选题)
题目:关于 RTL 镜像布局适配,以下说法正确的有(多选):
A. 组件 direction 属性设置为 Direction.Rtl 时镜像能力生效
B. 在 RTL 布局中,left / right 会自动切换为 start / end
C. 建议使用 start / end 替代 left / right 以支持 RTL
D. Canvas 组件的内容会自动跟随系统语言切换做镜像
答案:A、C
解读:组件 direction 设置为 Direction.Rtl 时镜像能力生效,选项 A 正确。在 RTL 布局中,left / right 不会自动切换,需要开发者使用 start / end 替代,选项 B 错误,选项 C 正确。Canvas 组件的绘制内容和坐标均不支持自动镜像,需要应用监听到系统语言切换后自行重新绘制,选项 D 错误。
习题 4(代码填空题)
题目:请补全以下代码,使 Image 组件在屏幕朗读时能够被正确播报。
Image($r('app.media.banner'))
.width('100%')
.height(200)
// 在此处填写代码,设置无障碍文本
______________
答案:.accessibilityText(‘首页轮播图:新用户专享优惠活动’)
解读:Image 组件本身不直接显示文本,屏幕朗读选中此组件时不播报。需要为不包含文本属性的组件设置无障碍文本,当屏幕朗读选中此组件时播报无障碍文本的内容。
习题 5(代码改错题)
题目:以下代码存在国际化适配问题,请指出问题并修正。
Column() {
Text('个人信息')
.fontSize(20)
.fontColor('#333333')
Text('姓名:张三')
.fontSize(16)
}
.padding({ left: 16, right: 16 })
.margin({ left: 20 })
答案:代码存在两个问题。第一,文字内容硬编码,无法适配多语言;第二,padding 和 margin 使用了 left / right 绝对方向,无法适配 RTL 布局。修正如下:
Column() {
Text($r('app.string.profile_info'))
.fontSize(20)
.fontColor($r('app.color.text_primary'))
Text($r('app.string.name_label', { name: '张三' }))
.fontSize(16)
}
.padding({ start: LengthMetrics.vp(16), end: LengthMetrics.vp(16) })
.margin({ start: LengthMetrics.vp(20) })
解读:所有界面文字应使用 $r 引用资源,便于多语言翻译;所有方向性属性应使用 start / end 替代 left / right,以便在 RTL 语言下自动镜像。
习题 6(简答题)
题目:简述资源限定词机制的工作原理,以及 base 目录的作用。
答案:资源限定词机制通过在 resources 目录下创建带有限定词的子目录(如 zh_CN、en_US、dark、float_ldpi 等)来组织不同环境下的资源。系统根据当前设备的语言、主题、屏幕密度等配置自动选择匹配的资源文件。开发者只需在代码中使用 $r(‘app.string.key’) 引用资源,无需关心当前环境。base 目录是默认资源目录,当更具体的限定词目录没有匹配内容时,系统会回退到 base。例如,如果用户的语言是法语但应用没有提供 fr 限定词目录,系统会自动使用 base 中的默认资源。
解读:资源限定词机制极大简化了国际化适配工作。开发者只需将翻译后的资源放入对应语言目录,系统会自动匹配。base 作为兜底目录确保在任何环境下都有资源可用。
习题 7(简答题)
题目:简述在 HarmonyOS 中实现多设备适配的核心方法,以及 GridRow 栅格布局在其中的作用。
答案:多设备适配的核心方法包括:使用断点系统(sm/md/lg)根据设备宽度切换布局;使用 GridRow + GridCol 栅格布局实现内容的自适应排列;使用 vp/fp 单位替代 px 确保不同密度屏幕下尺寸一致;使用 @ohos.mediaquery 监听窗口尺寸变化动态调整;使用 Navigation 的自适应模式自动切换单栏/分栏布局。GridRow 栅格布局的作用是为布局提供规律性的结构,通过 columns 属性按断点设置不同列数,通过 gutter 设置栅格间距,通过 breakpoints 自定义断点位置。GridCol 的 span 属性支持按断点设置子组件占用的列数,实现同一套代码在不同设备上呈现最优布局。
解读:多设备适配的关键在于“一次开发,多端部署”。GridRow 栅格布局为响应式设计提供了标准化的结构,配合断点系统和媒体查询,开发者可以用一套代码适配手机、平板、折叠屏、PC 等多种设备形态。
六、本节知识点总结
资源管理与多语言
通过资源限定词目录(base、zh_CN、en_US、dark 等)组织不同环境下的资源,系统自动匹配并回退到 base。使用 $r(‘app.string.key’) 引用资源,资源名在所有语言目录中保持一致。资源键应使用语义化命名,避免 text1 等无意义名称。
复数与区域化格式
复数资源使用 plural.json 定义,按数量选择文案。日期、数字、货币使用 @ohos.intl 的 DateTimeFormat、NumberFormat 进行区域化格式化,避免硬编码格式。
无障碍适配
屏幕朗读核心属性包括 accessibilityText(无障碍文本,优先级高于显示文本)、accessibilityGroup(合并子组件为一个焦点单元)、accessibilityDescription(操作后果说明)、accessibilityLevel(控制是否可被识别)。文本类控件优先使用显示文本,非文本类控件必须设置无障碍文本,装饰性元素设置 accessibilityLevel(‘no’)。
RTL 镜像布局
当组件 direction 设置为 Direction.Rtl 或 Direction.Auto 且系统语言为 RTL 时镜像生效。使用 start / end 替代 left / right,使用 LengthMetrics 类型。Canvas 不自动镜像需手动处理。方向性图标需单独提供 RTL 版本。
多设备适配
使用断点系统(sm/md/lg)和 GridRow + GridCol 栅格布局实现响应式设计。尺寸单位使用 vp/fp,配合 mediaquery 监听窗口变化。GridRow 通过 columns 按断点设置列数,GridCol 通过 span 按断点控制子组件占用列数。
下节预告
第18课将进入 ArkUI 应用性能优化进阶的学习,涵盖启动优化、渲染优化、内存优化与包体积优化的实战方法论与工具链使用。
更多推荐



所有评论(0)