Internationalization (i18n)
Reference doc — auto-synced from the
harmony-app-devAgentSkill. Source:references/i18n.md
HarmonyOS 国际化(i18n)与本地化(l10n):多语言、多区域、文化差异适配。
Purpose
读这个文件当:
- 应用要支持多语言(至少中英)
- 用户可能在不同区域使用(日期、货币、数字格式不同)
- 需要 RTL(阿拉伯语、希伯来语)布局
- 时间 / 数字 / 货币的本地化展示
i18n 与 resource-management.md 紧密关联——i18n 是「为什么要做」,resource-management 是「怎么组织文件」。
Capability mapping
- coverage 域:E4. 国际化 (i18n / l10n)
- 关联文件:
resource-management.md(多语言资源放置),ui-implementation-rules.md(不要硬编码文本)
Official documentation entry points
- 国际化总览:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/i18n-introduction-V5
- 多语言能力:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/i18n-multi-language-V5
- 时间日期格式化:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/i18n-time-date-V5
- 数字货币格式化:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/i18n-number-V5
- intl API:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-intl-V5
- i18n API:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-i18n-V5
Concept model
关键概念
- Locale:语言_地区,如
zh_CN、en_US、ja_JP、ar_SA - i18n:framework + API(intl / i18n 模块)
- l10n:在不同 locale 下提供对应资源
系统语言 vs 应用语言
系统语言(用户设置中切换)└── 默认情况下,应用跟随系统 └── 应用也可强制覆盖(应用内语言切换)资源限定符与 locale 的对应
| 限定符目录 | 匹配 |
|---|---|
zh_CN/ | 简体中文(中国大陆) |
zh_TW/ | 繁体中文(台湾) |
zh/ | 中文(任何地区,作为后备) |
en_US/ | 英文(美国) |
en/ | 英文(任何地区) |
base/ | 全部 fallback |
匹配优先级:精确 locale > 仅语言 > base/
Decision tree
应用需要支持几种语言?├─ 1 种 → 不用做 i18n,硬编码也行└─ ≥ 2 种 → 走 i18n 流程
需要应用内语言切换?├─ 不需要(跟随系统) → 只需放好资源文件└─ 需要 → 配合 i18n.System.setAppPreferredLanguage
需要本地化日期/数字/货币?├─ 否 → 用 toString 即可└─ 是 → 用 intl.DateTimeFormat / NumberFormat
需要支持 RTL?├─ 否(仅 zh/en/ja 等 LTR) → 不需要特别处理└─ 是(包含 ar/he 等) → 用 Direction.Rtl + 镜像图标Implementation patterns
Pattern 1: 多语言字符串
{ "string": [ { "name": "welcome", "value": "欢迎使用" }, { "name": "cart_items", "value": "购物车有 %d 件商品" }]}
// resources/en_US/element/string.json{ "string": [ { "name": "welcome", "value": "Welcome" }, { "name": "cart_items", "value": "%d items in cart" }]}Text($r('app.string.welcome'))Text($r('app.string.cart_items', itemCount))Pattern 2: 应用内强制切换语言
import { i18n } from '@kit.LocalizationKit';
// 切换为英文(重启 UIAbility 后生效)i18n.System.setAppPreferredLanguage('en-US');
// 获取当前应用语言const lang = i18n.System.getAppPreferredLanguage();
// 获取系统语言(用户设置)const sysLang = i18n.System.getSystemLanguage();⚠️ 切换语言后,当前 UIAbility 需要重新加载页面才能生效。建议:
- 切换后弹出「重启应用」提示
- 或主动
router.replaceUrl把当前页面重新加载
Pattern 3: 日期与时间格式化
import { intl } from '@kit.LocalizationKit';
const date = new Date();
// 中文长格式:2026年5月11日 星期一const zhFormat = new intl.DateTimeFormat('zh-CN', { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long'});console.info(zhFormat.format(date));
// 英文短格式:5/11/26const enFormat = new intl.DateTimeFormat('en-US', { dateStyle: 'short' });console.info(enFormat.format(date));
// 时间相对("3 分钟前")const rtf = new intl.RelativeTimeFormat('zh-CN');console.info(rtf.format(-3, 'minute')); // "3 分钟前"Pattern 4: 数字与货币
import { intl } from '@kit.LocalizationKit';
// 千分位const numFmt = new intl.NumberFormat('en-US');console.info(numFmt.format(1234567.89)); // "1,234,567.89"
// 中文人民币const cnyFmt = new intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY'});console.info(cnyFmt.format(99.9)); // "¥99.90"
// 美元const usdFmt = new intl.NumberFormat('en-US', { style: 'currency', currency: 'USD'});
// 百分比const pctFmt = new intl.NumberFormat('zh-CN', { style: 'percent', minimumFractionDigits: 1});console.info(pctFmt.format(0.135)); // "13.5%"Pattern 5: RTL 适配
import { i18n } from '@kit.LocalizationKit';
// 检测当前 locale 是否 RTLconst isRtl = i18n.isRTL(i18n.System.getSystemLanguage());
// 在容器上设置方向Row() .direction(isRtl ? Direction.Rtl : Direction.Ltr) .width('100%')
// 图标镜像(前进/后退箭头)Image($r('app.media.ic_arrow_back')) .scale({ x: isRtl ? -1 : 1 })Pattern 6: 复数处理
// 不同语言对复数的处理不同// 英文:1 item / 2 items// 中文:N 件商品(无单复数)// 俄文:5 形态规则// 推荐:分别定义资源 key(避免复杂的复数逻辑)$r('app.string.cart_zero') // 购物车空$r('app.string.cart_singular') // 1 item$r('app.string.cart_plural', n) // %d itemsCommon pitfalls
Pitfall 1: 字符串拼接破坏语序
// ❌ 错误:英文里 "go to {city}",中文需要 "去 {city}",但词序可能不同Text(`Go to ${cityName}`)
// ✅ 正确:用占位符 + 资源$r('app.string.go_to_city', cityName)// zh: "前往 %s"// en: "Go to %s"// ja: "%s に行く"Pitfall 2: 时间用 toLocaleString 不可控
// ❌ 不同设备 / 系统语言下输出格式无法预测new Date().toLocaleString()
// ✅ 用 intl.DateTimeFormat 显式指定 locale 与格式new intl.DateTimeFormat('zh-CN', { dateStyle: 'medium' }).format(date)Pitfall 3: 货币不能直接用「¥」
// ❌ ¥99 会被英文用户误认为日元Text(`¥${price}`)
// ✅ 用 currency 格式化(中国 = CNY,日本 = JPY)new intl.NumberFormat(locale, { style: 'currency', currency: 'CNY' }).format(price)Pitfall 4: 假定所有语言长度相近
德语单词常常是英文 1.5 倍长,UI 容器不能用固定宽度装文本。
- 按钮宽度用
padding而不是固定 width - 标题区域允许换行或省略号
- 用 LayoutInspector 在长文本下检验
Pitfall 5: 忘记翻译资源回退
base/element/string.json 必须包含所有 key,否则某 locale 缺 key 时会显示空白或资源 ID。
Verification before commit
- 所有用户可见文本走资源系统
- base/ 包含所有 string key 的默认值
- 至少 2 个 locale(zh_CN + en_US)的字符串完整
- 日期/数字/货币不直接
toString(),统一走 intl - 长字符串场景做了 UI 容器宽度测试
When to escalate to official docs
- 复杂复数规则(俄语、阿拉伯语等)
- 自定义日历(伊斯兰日历、农历)
- 双向文本(混合 LTR + RTL)的高级排版
- 字符集(拉丁扩展、CJK、表情符号)渲染问题
参考 official-search-playbook.md。