前置知识: HarmonyOS

国际化与无障碍

00:00
3 min Intermediate 2026/6/14

多语言与无障碍支持

概述

国际化(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)
      })
    }
  }
}

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式