国际化与无障碍
多语言与无障碍支持
概述
国际化(i18n)让你的应用支持多种语言和地区,无障碍(Accessibility)则确保残障人士也能正常使用你的应用。HarmonyOS 提供了完善的国际化资源管理和无障碍 API,帮助你构建面向全球用户、包容性强的应用。
为什么需要国际化?如果你的应用只支持中文,那么非中文用户将无法使用。通过国际化,同一套代码可以适配不同语言和地区,大大扩展了用户群体。为什么需要无障碍?视障用户依赖屏幕阅读器操作手机,如果界面元素没有正确的无障碍标签,他们就无法理解和使用你的应用。
基础概念
资源目录结构:HarmonyOS 通过不同的资源目录存放不同语言的资源文件。base 目录存放默认资源,特定语言目录(如 en_US、zh_CN)存放对应语言的资源。
资源引用:在代码中通过 $r() 函数引用资源,系统会根据当前设备语言自动选择对应目录的资源。
无障碍属性:通过 accessibilityText、accessibilityGroup 等属性为界面元素添加无障碍信息,供屏幕阅读器使用。
无障碍服务:HarmonyOS 内置了屏幕阅读器(TalkBack)等无障碍服务,会读取界面元素的无障碍属性并朗读给用户。
快速上手
国际化资源目录
resources/
├── base/ # 默认资源(无语言限定时使用)
│ └── element/
│ └── string.json # 默认字符串
├── en_US/ # 英文(美国)
│ └── element/
│ └── string.json # 英文字符串
├── zh_CN/ # 中文(中国)
│ └── element/
│ └── string.json # 中文字符串
└── ja_JP/ # 日文(日本)
└── element/
└── string.json # 日文字符串
资源文件内容
// base/element/string.json(默认)
{
"string": [
{ "name": "app_name", "value": "MyApp" },
{ "name": "greeting", "value": "Hello" },
{ "name": "submit", "value": "Submit" }
]
}
// zh_CN/element/string.json(中文)
{
"string": [
{ "name": "app_name", "value": "我的应用" },
{ "name": "greeting", "value": "你好" },
{ "name": "submit", "value": "提交" }
]
}
// en_US/element/string.json(英文)
{
"string": [
{ "name": "app_name", "value": "My App" },
{ "name": "greeting", "value": "Hello" },
{ "name": "submit", "value": "Submit" }
]
}
在代码中引用资源
@Entry
@Component
struct I18nDemo {
build() {
Column() {
// 通过 $r() 引用字符串资源,自动匹配当前语言
Text($r('app.string.greeting'))
.fontSize(24)
Text($r('app.string.app_name'))
.fontSize(18)
Button($r('app.string.submit'))
.onClick(() => {
console.info('提交按钮被点击')
})
}
}
}
详细用法
带参数的字符串
// string.json
{
"string": [
{ "name": "welcome", "value": "欢迎, %s!" },
{ "name": "item_count", "value": "共 %d 个项目" },
{ "name": "price", "value": "价格: %.2f 元" }
]
}
import i18n from '@ohos.i18n';
// 格式化带参数的字符串
const welcomeMsg = i18n.formatString($r('app.string.welcome'), '张三');
const countMsg = i18n.formatString($r('app.string.item_count'), 42);
const priceMsg = i18n.formatString($r('app.string.price'), 99.9);
复数形式处理
不同语言有不同的复数规则。英语有单数和复数,阿拉伯语有六种复数形式:
// en_US/element/string.json
{
"string": [
{ "name": "message_count_one", "value": "%d message" },
{ "name": "message_count_other", "value": "%d messages" }
]
}
// zh_CN/element/string.json(中文没有复数变化)
{
"string": [
{ "name": "message_count_one", "value": "%d 条消息" },
{ "name": "message_count_other", "value": "%d 条消息" }
]
}
日期和时间格式化
import i18n from '@ohos.i18n';
// 获取当前区域设置
const locale = i18n.getSystemLocale(); // 例如 "zh-CN"
// 格式化日期
const dateFormat = new i18n.DateTimeFormat(locale, {
dateStyle: 'full',
timeStyle: 'medium',
});
const formattedDate = dateFormat.format(new Date());
console.info(formattedDate); // 中文: 2026年6月14日 星期日 14:30:00
// 格式化数字
const numberFormat = new i18n.NumberFormat(locale);
const formattedNumber = numberFormat.format(1234567.89);
console.info(formattedNumber); // 中文: 1,234,567.89
// 货币格式化
const currencyFormat = new i18n.NumberFormat(locale, {
style: 'currency',
currency: 'CNY',
});
const formattedCurrency = currencyFormat.format(99.9);
console.info(formattedCurrency); // 中文: ¥99.90
获取和设置语言
import i18n from '@ohos.i18n';
// 获取系统语言
const systemLocale = i18n.getSystemLocale();
console.info(`系统语言: ${systemLocale}`); // zh-CN
// 获取系统地区
const systemRegion = i18n.getSystemRegion();
console.info(`系统地区: ${systemRegion}`); // CN
// 获取首选语言列表
const preferredLanguages = i18n.getPreferredLanguageList();
console.info(`首选语言: ${preferredLanguages}`);
// 判断是否为从右到左的语言(如阿拉伯语)
const isRTL = i18n.isRTL(systemLocale);
console.info(`是否RTL: ${isRTL}`);
无障碍基础
@Entry
@Component
struct AccessibilityDemo {
build() {
Column() {
// accessibilityText:为元素添加无障碍描述
// 屏幕阅读器会朗读这个文本
Image($r('app.media.icon'))
.width(48)
.height(48)
.accessibilityText('应用图标') // 图片没有文字,需要无障碍描述
// accessibilityGroup:将多个元素组合为一个无障碍节点
Row() {
Text('温度')
Text('25°C')
}
.accessibilityGroup(true)
.accessibilityText('温度 25摄氏度') // 朗读组合的描述
// accessibilityLevel:控制元素是否可被无障碍服务聚焦
Text('装饰性文字')
.accessibilityLevel('no') // 不被屏幕阅读器聚焦
// accessibilityDescription:提供更详细的描述
Button('提交')
.accessibilityText('提交按钮')
.accessibilityDescription('点击此按钮提交表单数据')
}
}
}
无障碍事件
@Component
struct AccessibilityEventDemo {
@State result: string = ''
build() {
Column() {
Text('操作结果: ' + this.result)
.fontSize(18)
// 自定义无障碍操作
Button('执行操作')
.accessibilityText('执行操作按钮')
.onAccessibilityClick(() => {
this.result = '操作已执行'
return true // 返回 true 表示事件已处理
})
}
}
}
常见场景
完整的多语言应用
import i18n from '@ohos.i18n'
@Entry
@Component
struct MultiLanguageApp {
@State currentLocale: string = ''
aboutToAppear() {
// 获取当前系统语言
this.currentLocale = i18n.getSystemLocale()
}
build() {
Column() {
// 标题使用资源引用
Text($r('app.string.app_name'))
.fontSize(28)
.fontWeight(FontWeight.Bold)
// 列表项使用资源引用
List() {
ListItem() {
Row() {
Text($r('app.string.greeting'))
}
}
ListItem() {
Row() {
Text($r('app.string.submit'))
}
}
}
// 显示当前语言
Text(`当前语言: ${this.currentLocale}`)
.fontSize(14)
.fontColor('#999999')
.margin({ top: 20 })
}
.padding(16)
}
}
无障碍表单
@Component
struct AccessibleForm {
@State username: string = ''
@State password: string = ''
build() {
Column() {
// 每个输入框都有无障碍标签
TextInput({ placeholder: '请输入用户名' })
.accessibilityText('用户名输入框')
.accessibilityDescription('请在此输入您的用户名')
.onChange((value) => { this.username = value })
TextInput({ placeholder: '请输入密码' })
.type(InputType.Password)
.accessibilityText('密码输入框')
.accessibilityDescription('请在此输入您的密码')
.onChange((value) => { this.password = value })
Button('登录')
.accessibilityText('登录按钮')
.accessibilityDescription('点击登录您的账户')
.onClick(() => {
// 登录逻辑
})
}
.padding(16)
}
}
注意事项
资源文件编码:所有资源文件必须使用 UTF-8 编码,否则非 ASCII 字符可能显示异常。
默认资源必须完整:base 目录中的资源是兜底方案,当系统找不到匹配语言的资源时会使用默认资源。确保 base 目录包含所有资源项。
字符串拼接问题:不要在代码中拼接不同语言的字符串,因为不同语言的语序可能不同。使用带参数的字符串模板代替。
无障碍测试:开发时应开启设备的屏幕阅读器功能进行测试,确保所有界面元素都能被正确朗读。
RTL 布局:如果应用需要支持阿拉伯语等从右到左的语言,需要测试布局是否正确镜像。
进阶用法
动态切换语言
import i18n from '@ohos.i18n'
import resourceManager from '@ohos.resourceManager'
@Component
struct LanguageSwitcher {
@State currentLang: string = 'zh-CN'
switchLanguage(lang: string) {
// 注意:应用内切换语言需要重新加载资源上下文
this.currentLang = lang
// 实际项目中可能需要重启 Activity 或重新创建页面
}
build() {
Column() {
Text('当前语言: ' + this.currentLang)
Button('中文').onClick(() => this.switchLanguage('zh-CN'))
Button('English').onClick(() => this.switchLanguage('en-US'))
Button('日本語').onClick(() => this.switchLanguage('ja-JP'))
}
}
}
无障碍实时区域
@Component
struct LiveRegionDemo {
@State statusMessage: string = '等待操作...'
build() {
Column() {
// accessibilityLiveRegion:当内容变化时自动通知屏幕阅读器
Text(this.statusMessage)
.fontSize(18)
.accessibilityLiveRegion('polite') // polite: 不打断当前朗读
// 也可以使用 'assertive': 立即打断当前朗读
Button('开始处理').onClick(() => {
this.statusMessage = '正在处理中...'
// 模拟异步操作
setTimeout(() => {
this.statusMessage = '处理完成'
}, 2000)
})
}
}
}