HarmonyOS7 国际化适配:i18n 让你的 App 走向全球

文章目录

前言

我之前做过一个出海项目,App 在国内跑得好好的,一到海外就各种问题------日期格式不对、货币符号乱了、阿拉伯语界面直接崩了。这些问题都归到"国际化适配"这个大坑里。HarmonyOS7 的 i18n 工具链其实做得挺完善,关键是你要知道怎么用。今天就来系统聊聊。

国际化不只是"翻译字符串"那么简单。日期、货币、数字格式、排版方向------每个地区都有自己的习惯。i18n 的目标就是让同一套代码在不同地区都能正常工作。HarmonyOS7 提供了 @kit.LocalizationKit,里面集成了语言检测、格式化、RTL 适配等一整套能力。

i18n 能力概览

HarmonyOS7 的国际化能力主要分这几块:

能力 模块 用途
语言/区域检测 i18n.System 获取系统语言、区域、Locale
偏好语言设置 i18n.System 为应用单独设置语言
日期时间格式化 Intl.DateTimeFormat 按地区格式化日期时间
数字/货币格式化 Intl.NumberFormat 按地区格式化数字和货币
相对时间格式化 Intl.RelativeTimeFormat "3分钟前"这类相对时间
资源加载 资源限定词目录 自动匹配当前语言的字符串资源

资源文件组织

先搭好目录结构,这是国际化的基础:

复制代码
resources/
├── base/
│   └── element/
│       └── string.json          ← 默认语言(英文)
├── zh-Hans/
│   └── element/
│       └── string.json          ← 简体中文
├── zh-Hant/
│   └── element/
│       └── string.json          ← 繁体中文
├── ja/
│   └── element/
│       └── string.json          ← 日文
└── ar/
    └── element/
        └── string.json          ← 阿拉伯文

目录命名规则是 语言[-脚本][-地区],比如 zh-Hans-CN。系统会按优先级匹配:精确匹配 > 语言+脚本 > 仅语言 > base(默认)。

默认 string.json(base/element/string.json)
json 复制代码
{
  "string": [
    { "name": "app_name", "value": "MyApp" },
    { "name": "greeting", "value": "Hello" },
    { "name": "settings", "value": "Settings" },
    { "name": "confirm", "value": "OK" }
  ]
}
中文 string.json(zh-Hans/element/string.json)
json 复制代码
{
  "string": [
    { "name": "app_name", "value": "我的应用" },
    { "name": "greeting", "value": "你好" },
    { "name": "settings", "value": "设置" },
    { "name": "confirm", "value": "确定" }
  ]
}

name 必须完全一致,系统根据当前 Locale 自动选择对应目录的 value。

字符串国际化

代码中使用字符串资源非常简单:

typescript 复制代码
@Entry
@Component
struct I18nDemo {
  build() {
    Column() {
      Text($r('app.string.app_name'))
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Text($r('app.string.greeting'))
        .fontSize(16)
        .margin({ top: 12 })

      Button($r('app.string.settings'))
        .margin({ top: 24 })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

关键点 :用 $r('app.string.xxx') 引用字符串,别直接写中文字符串。系统会根据当前 Locale 自动加载对应的 string.json。如果找不到对应语言,就回退到 base 目录。

带参数的字符串也支持,在 string.json 里用 %s%d 占位:

json 复制代码
{ "name": "welcome_msg", "value": "Welcome, %s!" }

日期与货币格式化

不同地区的日期和货币格式差异很大,手动处理容易出错。用 Intl 模块可以自动适配。

日期格式化
typescript 复制代码
import Intl from '@ohos.intl'

let date = new Date(2026, 6, 10, 14, 30, 0)

// 中文格式
let zhFormat = new Intl.DateTimeFormat('zh-CN', {
  dateStyle: 'full',
  timeStyle: 'short'
})
let zhResult = zhFormat.format(date)
// 输出: 2026年7月10日星期五 14:30

// 英文格式
let enFormat = new Intl.DateTimeFormat('en-US', {
  dateStyle: 'short',
  timeStyle: 'short'
})
let enResult = enFormat.format(date)
// 输出: 7/10/26, 2:30 PM

// 德文格式
let deFormat = new Intl.DateTimeFormat('de-DE', {
  dateStyle: 'long',
  timeStyle: 'short'
})
let deResult = deFormat.format(date)
// 输出: 10. Juli 2026, 14:30

讲解DateTimeFormat 的构造函数接收两个参数------Locale 和格式选项。dateStyletimeStyle 控制 granularity:full 最详细(含星期),short 最精简。同一个 Date 对象,不同 Locale 输出完全不同的格式。

货币格式化
typescript 复制代码
let priceFormat = new Intl.NumberFormat('zh-CN', {
  style: 'currency',
  currency: 'CNY'
})
let price = priceFormat.format(99.9)
// 输出: ¥99.90

let usdFormat = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
})
let usdPrice = usdFormat.format(99.9)
// 输出: $99.90

let yenFormat = new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY'
})
let yenPrice = yenFormat.format(1000)
// 输出: ¥1,000

讲解style: 'currency' 表示货币格式,currency 指定货币代码(ISO 4217)。日元没有小数位,格式化时自动省略------这事儿手动处理很容易忘,用 NumberFormat 就不会出错。

运行时切换语言

有些 App 需要内置语言切换功能,不用跟着系统走。i18n.System 提供了接口:

typescript 复制代码
import { i18n } from '@kit.LocalizationKit'

@Entry
@Component
struct LanguageSwitchDemo {
  @State currentLang: string = i18n.System.getAppPreferredLanguage()

  switchLanguage(lang: string) {
    try {
      i18n.System.setAppPreferredLanguage(lang)
      this.currentLang = lang
    } catch (error) {
      console.error(`语言切换失败: ${error}`)
    }
  }

  build() {
    Column() {
      Text(`当前语言: ${this.currentLang}`)
        .fontSize(16)
        .margin({ bottom: 20 })

      Row() {
        Button('中文')
          .onClick(() => this.switchLanguage('zh-Hans'))
          .margin({ right: 12 })

        Button('English')
          .onClick(() => this.switchLanguage('en'))

        Button('日本語')
          .onClick(() => this.switchLanguage('ja'))
          .margin({ left: 12 })
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

注意setAppPreferredLanguage 只影响当前 App,不会改系统语言。设置后 App 需要重新加载资源才能生效------通常的做法是回到首页或重启 Activity。

RTL 布局适配

阿拉伯语、希伯来语是从右往左写的(RTL),布局方向要反转。HarmonyOS7 提供了自动 RTL 适配能力:

typescript 复制代码
import { i18n } from '@kit.LocalizationKit'

@Entry
@Component
struct RtlDemo {
  isRtl: boolean = i18n.System.isRTL(i18n.System.getSystemLocale())

  build() {
    Row() {
      Text('标题')
        .fontSize(16)
      Blank()
      Text('详情')
        .fontSize(14)
    }
    .width('100%')
    .direction(this.isRtl ? Direction.Rtl : Direction.Ltr)
    .padding(16)
  }
}

讲解i18n.System.isRTL() 判断当前 Locale 是否是 RTL 语言。Row 和 Column 组件支持 direction 属性,设为 Rtl 后子组件从右往左排列。

RTL 适配的坑

  • 图标方向可能需要镜像(比如"返回"箭头要翻转)
  • 数字始终从左往右,不要镜像
  • marginpaddingleft/right 在 RTL 下会自动对调,用 start/end 更安全

真香警告 :用资源限定词目录的方式,字符串自动匹配;用 direction 属性,布局自动反转。HarmonyOS7 的 RTL 适配已经帮你处理了大部分情况。

写在最后

国际化这件事,越早做越好。等项目做大了再回来加多语言支持,那叫一个痛苦------到处是硬编码的中文字符串,改起来没完没了。

我的建议是:项目第一天就建好 basezh-Hans 两个资源目录,所有字符串都走 $r() 引用。后面加新语言只是往目录里加 string.json 的事,代码一行不用改。

货币和日期格式化同理,从一开始就用 Intl 模块,别手动拼字符串。这两种格式的地区差异比你想象的大得多,靠人肉处理迟早翻车。

相关推荐
無法複制22 分钟前
Windows10安装配置Docker Desktop教程
运维·docker·容器
AI智图坊25 分钟前
宠物用品电商视觉内容生产的技术难点与自动化方案分析
大数据·运维·人工智能·ai作画·自动化·aigc
xiaoxiangsiyan37 分钟前
网络智能化转型核心模块全解析
运维·服务器·网络·数据库·学习·架构·php
陕西企来客1 小时前
2026年8月真实安装案例:门窗现场施工到完工记录
大数据·运维·真实安装案例
盟接之桥1 小时前
半导体供应链破局:EDI如何成为中国制造的数字通行证
大数据·运维·服务器·网络·数据库·人工智能·制造
其实防守也摸鱼1 小时前
ZLibrary 类项目合规避坑指南:从技术实现到法律风险的全景梳理
运维·服务器·数据库·安全·自动化·github·copilot
前端 贾公子2 小时前
第09章:上下文与记忆 (4)
java·服务器·前端
BangD2 小时前
visual stdio解决中文乱码问题
服务器