@harmonyos-arkts/d2h 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/.claude-plugin/marketplace.json +15 -13
  2. package/.claude-plugin/plugin.json +13 -7
  3. package/AGENTS.md +5 -5
  4. package/README.md +212 -212
  5. package/agents/android-to-hmos-00-orchestrator.md +318 -318
  6. package/agents/d2h.md +157 -157
  7. package/dist/bin/install-opencode.js +30 -30
  8. package/docs/environment-check.md +86 -86
  9. package/install-opencode.sh +160 -160
  10. package/opencode/agents.json +16 -16
  11. package/opencode/vscode-d2h-package.json +7 -7
  12. package/opencode/vscode-d2h-plugin.mjs +29 -29
  13. package/package.json +54 -54
  14. package/schemas/android-source-manifest.schema.json +25 -25
  15. package/schemas/checkpoint-provenance.schema.json +67 -67
  16. package/schemas/final-acceptance.schema.json +29 -29
  17. package/schemas/managed-evidence-index.schema.json +15 -15
  18. package/schemas/managed-evidence.schema.json +114 -114
  19. package/schemas/migration-config.schema.json +51 -51
  20. package/schemas/migration-report-index.schema.json +39 -39
  21. package/schemas/migration-report-item.schema.json +51 -51
  22. package/schemas/migration-report-summary.schema.json +25 -25
  23. package/schemas/migration-status.schema.json +202 -202
  24. package/schemas/preflight.schema.json +60 -60
  25. package/schemas/source-order-audit.schema.json +16 -16
  26. package/schemas/source-provenance-event.schema.json +19 -19
  27. package/schemas/spec-app-shard.schema.json +45 -45
  28. package/schemas/spec-fact-corrections-shard.schema.json +13 -13
  29. package/schemas/spec-features-shard.schema.json +43 -43
  30. package/schemas/spec-index.schema.json +26 -26
  31. package/schemas/spec-interactions-shard.schema.json +40 -40
  32. package/schemas/spec-page.schema.json +98 -98
  33. package/schemas/spec-pages-index.schema.json +1 -1
  34. package/schemas/spec-unresolved-shard.schema.json +1 -1
  35. package/schemas/task-envelope.schema.json +109 -109
  36. package/schemas/task-plan.schema.json +341 -341
  37. package/scripts/check-environment.mjs +320 -320
  38. package/skills/android2hmos_resources_convert/SKILL.md +180 -180
  39. package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -230
  40. package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -175
  41. package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -513
  42. package/skills/arkts-app-identity/SKILL.md +238 -238
  43. package/skills/arkts-i18n/SKILL.md +496 -496
  44. package/skills/arkts-i18n/evals/evals.json +84 -84
  45. package/skills/arkts-i18n/references/code-examples.md +302 -302
  46. package/skills/arkts-i18n/references/common-pitfalls.md +391 -391
  47. package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -604
  48. package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -348
  49. package/skills/arkts-i18n/references/language-codes.md +104 -104
  50. package/skills/arkts-i18n/references/resource-file-structure.md +775 -775
  51. package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -242
  52. package/skills/arkts-i18n/references/v1-compat.md +244 -244
  53. package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -174
  54. package/skills/arkts-icon-sizing/SKILL.md +211 -211
  55. package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -131
  56. package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -88
  57. package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -179
  58. package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -119
  59. package/skills/arkts-mvvm-architecture/SKILL.md +613 -613
  60. package/skills/d2h-hypium-forge/SKILL.md +309 -309
  61. package/skills/d2h-hypium-forge/references/functional-spec-template.md +140 -140
  62. package/skills/d2h-hypium-forge/references/official-api.md +135 -135
  63. package/skills/d2h-hypium-forge/references/pitfalls.md +98 -98
  64. package/skills/d2h-hypium-forge/references/testability-template.md +71 -71
  65. package/skills/d2h-hypium-forge/scripts/_common_template.py +659 -659
  66. package/skills/d2h-hypium-forge/scripts/analyze_results.py +165 -165
  67. package/skills/d2h-hypium-forge/scripts/assert_audit.py +107 -107
  68. package/skills/d2h-hypium-forge/scripts/collect_runtime_pages.py +169 -169
  69. package/skills/d2h-hypium-forge/scripts/forge_report.py +325 -325
  70. package/skills/d2h-hypium-forge/scripts/inject_resource_ids.py +114 -114
  71. package/skills/d2h-hypium-forge/scripts/lint_asserts.py +80 -80
  72. package/skills/d2h-hypium-forge/scripts/lint_locate.py +60 -60
  73. package/skills/d2h-hypium-forge/scripts/merge_appgraph.py +249 -249
  74. package/skills/d2h-hypium-forge/scripts/precondition.py +140 -140
  75. package/skills/d2h-hypium-forge/scripts/preinject_ids.py +232 -232
  76. package/skills/d2h-hypium-forge/scripts/probe.py +197 -197
  77. package/skills/d2h-hypium-forge/scripts/run_direct.py +823 -823
  78. package/skills/d2h-hypium-forge/scripts/run_parallel.py +121 -121
  79. package/skills/d2h-hypium-forge/scripts/scan_features.py +255 -255
  80. package/skills/d2h-hypium-forge/scripts/upgrade_asserts.py +66 -66
  81. package/skills/d2h-hypium-forge/scripts/validate_scenarios.py +84 -84
  82. package/skills/d2h-page-develop/SKILL.md +112 -112
  83. package/skills/d2h-page-develop/agents/openai.yaml +7 -7
  84. package/skills/d2h-page-spec-generate/SKILL.md +206 -206
  85. package/skills/d2h-page-spec-generate/scripts/build_batch_report.py +227 -227
  86. package/skills/d2h-utg-ai-generate/SKILL.md +128 -128
  87. package/skills/d2h-utg-ai-generate/references/click-paths-schema.json +76 -76
  88. package/skills/d2h-utg-ai-generate/references/extraction-guide.md +34 -34
  89. package/skills/d2h-utg-ai-generate/references/facts-contract.md +96 -96
  90. package/skills/d2h-utg-ai-generate/references/page-schema.json +61 -61
  91. package/skills/d2h-utg-ai-generate/references/utg-schema.json +89 -89
  92. package/skills/d2h-utg-ai-generate/scripts/build_page_json.py +314 -314
  93. package/skills/d2h-utg-ai-generate/scripts/build_utg.py +123 -123
  94. package/skills/d2h-utg-ai-generate/scripts/capture_from_page_json.ts +280 -280
  95. package/skills/d2h-utg-ai-generate/scripts/utg_common.py +524 -524
  96. package/skills/d2h-utg-ai-generate/scripts/utg_html.py +489 -489
  97. package/skills/d2h-utg-ai-generate/scripts/validate_utg.py +69 -69
  98. package/skills/d2h-utg-ai-generate/scripts/vendor/D3-LICENSE +13 -13
  99. package/skills/d2h-utg-ai-generate/scripts/vendor/d3-7.9.0.min.js +2 -2
  100. package/skills/d2h-utg-ai-generate/tests/test_pipeline.py +369 -369
  101. package/skills/d2h-utg-static-generate/SKILL.md +73 -73
  102. package/skills/d2h-utg-static-generate/references/discoveries.md +38 -38
  103. package/skills/d2h-utg-static-generate/scripts/generate.py +499 -499
  104. package/skills/d2h-utg-static-generate/tests/test_static.py +335 -335
  105. package/skills/d2h-utg-static-generate/tests/verify-offline-html.py +60 -60
  106. package/skills/d2h-utg-static-generate/tests/verify-produced-fixture.py +131 -131
  107. package/skills/harmonyos-migration-playbook/SKILL.md +83 -83
  108. package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -7
  109. package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -24
  110. package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -53
  111. package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -45
  112. package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -23
  113. package/skills/harmonyos-migration-playbook/references/ui-automation.md +53 -53
  114. package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -38
  115. package/skills/maintaining-migration-report/SKILL.md +144 -144
  116. package/skills/maintaining-migration-report/references/final-acceptance.md +48 -48
  117. package/skills/maintaining-migration-report/references/source-provenance.md +66 -66
  118. package/skills/managing-d2h-toolchain/SKILL.md +24 -24
  119. package/skills/managing-d2h-toolchain/environment-requirements.json +16 -16
  120. package/skills/managing-d2h-toolchain/scripts/manage-toolchain.mjs +98 -98
  121. package/skills/managing-d2h-toolchain/toolchain.json +16 -16
  122. package/skills/native-library-substitution/SKILL.md +384 -384
  123. package/skills/native-library-substitution/references/native-library-substitution.json +56905 -56905
  124. package/skills/native-library-substitution/references/native-library-substitution.md +163 -163
  125. package/skills/preparing-migration-workspace/SKILL.md +131 -131
  126. package/skills/reviewing-migration-process/SKILL.md +62 -62
@@ -1,391 +1,391 @@
1
- # 国际化避坑指南(V2)
2
-
3
- > 常见国际化错误与正确做法对照表。**本项目锁 V2**:所有代码示例使用 `@ComponentV2 / @Local / @ObservedV2 / @Trace / AppStorageV2`。i18n API(`resourceManager` / `$r()` 等)与装饰器版本无关。
4
- >
5
- > V1 老项目兼容写法见 [`v1-compat.md`](./v1-compat.md)。
6
-
7
- ---
8
-
9
- ## ⚠️ 文档说明
10
-
11
- - ✅ **已验证项**:基于代码仓实际使用或官方文档
12
- - ⚠️ **未验证项**:需要查证官方文档确认
13
- - 💡 **建议**:使用前查阅 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/i18n-overview-V5
14
-
15
- ---
16
-
17
- ## 目录命名错误
18
-
19
- ### 错误 vs 正确
20
-
21
- | 错误写法 | 正确写法 | 说明 |
22
- |---------|---------|------|
23
- | `chinese` | `zh` | 使用 ISO 639-1 |
24
- | `china` | `zh_CN` | 使用 ISO 639-1 + ISO 3166-1 |
25
- | `Chinese` | `zh-Hans` | 小写,或带区域码 |
26
- | `zh-CN` | `zh_CN` | 下划线而非连字符(部分版本接受连字符) |
27
- | `en-uk` | `en_GB` | 英国英文用 `en_GB` |
28
-
29
- ### 完整语言代码参考
30
-
31
- | 语言 | 代码 | 区域变体 |
32
- |------|------|---------|
33
- | 中文 | `zh` | `zh_CN`, `zh_HK`, `zh_TW`, `zh_Hans`, `zh_Hant` |
34
- | 英文 | `en` | `en_US`, `en_GB`, `en_AU`, `en_CA` |
35
- | 日文 | `ja` | - |
36
- | 韩文 | `ko` | - |
37
- | 德文 | `de` | `de_DE`, `de_AT`, `de_CH` |
38
- | 法文 | `fr` | `fr_FR`, `fr_CA`, `fr_CH` |
39
- | 西班牙文 | `es` | `es_ES`, `es_MX`, `es_AR` |
40
- | 葡萄牙文 | `pt` | `pt_BR`, `pt_PT` |
41
- | 俄文 | `ru` | - |
42
- | 阿拉伯文 | `ar` | - |
43
-
44
- ---
45
-
46
- ## string.json 格式错误
47
-
48
- ### 错误 1:value 使用错误引号
49
-
50
- ```json
51
- // 错误 — value 被双引号包裹
52
- {
53
- "string": [
54
- { "name": "app_name", "value": "\"图库\"" }
55
- ]
56
- }
57
-
58
- // 正确 — value 直接是字符串
59
- {
60
- "string": [
61
- { "name": "app_name", "value": "图库" }
62
- ]
63
- }
64
- ```
65
-
66
- ### 错误 2:缺少必要字段
67
-
68
- ```json
69
- // 错误 — 缺少 name 或 value
70
- {
71
- "string": [
72
- { "value": "图库" },
73
- { "name": "app_name" }
74
- ]
75
- }
76
-
77
- // 正确
78
- {
79
- "string": [
80
- { "name": "app_name", "value": "图库" }
81
- ]
82
- }
83
- ```
84
-
85
- ### 错误 3:JSON 语法错误
86
-
87
- ```json
88
- // 错误 — 尾随逗号
89
- {
90
- "string": [
91
- { "name": "a", "value": "A" },
92
- { "name": "b", "value": "B" }, // ← 尾随逗号
93
- ]
94
- }
95
-
96
- // 正确
97
- {
98
- "string": [
99
- { "name": "a", "value": "A" },
100
- { "name": "b", "value": "B" }
101
- ]
102
- }
103
- ```
104
-
105
- ---
106
-
107
- ## 硬编码字符串
108
-
109
- ### 错误 vs 正确
110
-
111
- | 错误写法 | 正确写法 |
112
- |---------|---------|
113
- | `Text('确认')` | `Text($r('app.string.confirm'))` |
114
- | `Button('取消')` | `Button($r('app.string.cancel'))` |
115
- | `'共 ' + count + ' 个文件'` | `$r('app.string.file_count', count)` |
116
- | `promptAction.showToast('操作成功')` | `promptAction.showToast($r('app.string.success'))` |
117
-
118
- ### 容易硬编码的地方
119
-
120
- 1. **Button 文字**
121
- 2. **Dialog 标题和内容**
122
- 3. **Toast 消息**
123
- 4. **Placeholder 文案**
124
- 5. **Error 消息**
125
- 6. **Menu 项**
126
- 7. **Tab 标签**
127
-
128
- ---
129
-
130
- ## 动态切换不生效
131
-
132
- ### 原因 1:未声明 supportedLanguages
133
-
134
- ```json5
135
- // module.json5
136
- {
137
- "module": {
138
- "supportedLanguages": ["zh", "en", "ja"] // ← 必须声明
139
- }
140
- }
141
- ```
142
-
143
- ### 原因 2:UI 未触发重建
144
-
145
- ```typescript
146
- // 错误 — 切换后 UI 不更新
147
- async switchLanguage(lang: string) {
148
- await resMgr.setPreferredLanguage([lang])
149
- // 直接返回,UI 不会更新
150
- }
151
-
152
- // 正确 — 触发状态更新
153
- async switchLanguage(lang: string) {
154
- await resMgr.setPreferredLanguage([lang])
155
- this.refreshKey++ // 触发重建
156
- this.currentLang = lang
157
- }
158
- ```
159
-
160
- ### 原因 3:组件未持有响应式 locale 状态
161
-
162
- ```typescript
163
- // 错误 — 组件不响应状态变化
164
- @Entry
165
- @ComponentV2
166
- struct Page {
167
- build() {
168
- Column() {
169
- Text($r('app.string.hello')) // 不会自动更新
170
- }
171
- }
172
- }
173
-
174
- // 正确 — 使用 @Local + LocaleModel 包装
175
- @ObservedV2
176
- class LocaleModel {
177
- @Trace currentLanguage: string = 'zh'
178
- @Trace refreshKey: number = 0
179
- }
180
-
181
- @Entry
182
- @ComponentV2
183
- struct Page {
184
- @Local locale: LocaleModel = AppStorageV2.connect(
185
- LocaleModel, 'locale', () => new LocaleModel()
186
- )!
187
-
188
- build() {
189
- Column() {
190
- Text($r('app.string.hello'))
191
- .id('page_' + this.locale.refreshKey) // refreshKey 改即重建
192
- }
193
- }
194
- }
195
- ```
196
-
197
- > V2 关键点:`@Local` 持有 `@ObservedV2` 实例 → 实例的 `@Trace` 字段变化自动刷新该组件。无需 V1 的 `@StorageLink('refreshKey')` 单字段绑定。
198
-
199
- ---
200
-
201
- ## 图片国际化问题
202
-
203
- ### 错误:文件名不一致
204
-
205
- ```
206
- base/media/
207
- └── logo.png ✓
208
-
209
- en/media/
210
- └── logo_en.png ✗ 应该是 logo.png
211
-
212
- ja/media/
213
- └── logo_jp.png ✗ 应该是 logo.png
214
- ```
215
-
216
- ### 正确做法
217
-
218
- ```
219
- base/media/
220
- └── logo.png ✓
221
-
222
- en/media/
223
- └── logo.png ✓(同名文件,内容不同)
224
-
225
- ja/media/
226
- └── logo.png ✓(同名文件,内容不同)
227
- ```
228
-
229
- ---
230
-
231
- ## 插值参数使用错误
232
-
233
- > ⚠️ **验证状态**:`IntlString.format()` API 需要查证官方文档
234
- >
235
- > **推荐方案**:使用字符串模板 ` \`...${variable}...\` ` 或 `resourceManager.getStringByName()`
236
-
237
- ### 错误 1:参数顺序错误
238
-
239
- ```typescript
240
- // string.json: { "name": "info", "value": "文件:%s,大小:%s" }
241
-
242
- // ⚠️ 以下 API 需要查证 - IntlString.format 可能不存在
243
- // 错误 — 参数顺序颠倒
244
- // IntlString.format($r('app.string.info'), size, name)
245
-
246
- // ✅ 推荐 — 使用字符串模板
247
- const name = 'photo.jpg'
248
- const size = '1.5MB'
249
- const message = `文件:${name},大小:${size}`
250
-
251
- // ⚠️ 或者 — 如果 resourceManager 支持参数
252
- // resourceManager.getStringByName('info', name, size)
253
- ```
254
-
255
- ### 错误 2:参数类型不匹配
256
-
257
- ```typescript
258
- // string.json: { "name": "count", "value": "数量:%d" }
259
-
260
- // ⚠️ 以下 API 需要查证
261
- // 错误 — 传字符串
262
- // IntlString.format($r('app.string.count'), '10')
263
-
264
- // ✅ 推荐 — 使用字符串模板
265
- const count = 10
266
- const message = `数量:${count}`
267
- ```
268
-
269
- ### 错误 3:参数数量不匹配
270
-
271
- ```typescript
272
- // string.json: { "name": "info", "value": "%s 大小:%s" }
273
-
274
- // ⚠️ 以下 API 需要查证
275
- // 错误 — 只传一个参数
276
- // IntlString.format($r('app.string.info'), name)
277
-
278
- // ✅ 推荐 — 使用字符串模板
279
- const name = 'file.txt'
280
- const size = '2KB'
281
- const message = `${name} 大小:${size}`
282
- ```
283
-
284
- ---
285
-
286
- ## RTL 语言注意事项
287
-
288
- 阿拉伯文、希伯来文等 RTL(从右到左)语言需要额外处理:
289
-
290
- ### 1. 布局适配
291
-
292
- ```typescript
293
- // 检测 RTL
294
- import { i18n } from '@kit.LocalizationKit'
295
-
296
- const isRTL = i18n.isRTL()
297
- ```
298
-
299
- ### 2. 布局方向
300
-
301
- ```typescript
302
- Column() {
303
- // RTL 语言下自动反转
304
- }
305
- .layoutDirection(isRTL ? LayoutDirection.RTL : LayoutDirection.LTR)
306
- ```
307
-
308
- ### 3. 图片翻转
309
-
310
- ```typescript
311
- Image($r('app.media.arrow'))
312
- .rotate({ angle: isRTL ? 180 : 0 }) // 箭头方向需反转
313
- ```
314
-
315
- ---
316
-
317
- ## module.json5 supportedLanguages 陷阱
318
-
319
- ### 问题:声明了但不生效
320
-
321
- ```json5
322
- // module.json5
323
- {
324
- "module": {
325
- "supportedLanguages": ["zh", "en"]
326
- }
327
- }
328
- ```
329
-
330
- 可能原因:
331
- 1. **语言代码不匹配**:`"zh-Hans"` vs `"zh"`
332
- 2. **应用未重启**:切换语言后需要重启应用
333
- 3. **资源文件缺失**:声明了但对应目录不存在
334
-
335
- ### 正确声明
336
-
337
- ```json5
338
- {
339
- "module": {
340
- "supportedLanguages": [
341
- "zh", // 中文(默认)
342
- "zh-Hans", // 简体中文(API 12+)
343
- "zh-Hant", // 繁体中文(API 12+)
344
- "en", // 英文
345
- "ja" // 日文
346
- ]
347
- }
348
- }
349
- ```
350
-
351
- ---
352
-
353
- ## 调试技巧
354
-
355
- ### 1. 打印当前语言
356
-
357
- ```typescript
358
- import { resourceManager } from '@kit.LocalizationKit'
359
-
360
- const langs = await context.resourceManager.getPreferredLanguage()
361
- hilog.info(0x0000, 'I18N', 'Current language: %{public}s', langs[0])
362
- ```
363
-
364
- ### 2. 检查资源是否加载
365
-
366
- ```typescript
367
- const resMgr = context.resourceManager
368
- const str = await resMgr.getStringByName('app_name')
369
- hilog.info(0x0000, 'I18N', 'String value: %{public}s', str)
370
- ```
371
-
372
- ### 3. 检查资源目录是否存在
373
-
374
- ```typescript
375
- // 在 DevEco Studio 中
376
- // File > Project Structure > Module > Resources
377
- // 查看 supportedLanguages 是否与实际目录匹配
378
- ```
379
-
380
- ---
381
-
382
- ## 错误代码速查
383
-
384
- | 错误现象 | 可能原因 | 解决方案 |
385
- |---------|---------|---------|
386
- | 切换语言后 UI 不更新 | 未触发重建 | 增加 `@Local refreshKey` 或 `@ObservedV2 LocaleModel` 的 `@Trace refreshKey` 字段并改变 ID |
387
- | `$r()` 引用报错 | string.json 中 key 不存在 | 检查 key 拼写 |
388
- | setPreferredLanguage 不生效 | 未声明 supportedLanguages | 在 module.json5 中声明 |
389
- | 英文资源不加载 | 目录名错误 | 使用 `en` 而非 `en_US` |
390
- | 日文显示乱码 | 文件编码错误 | 确保文件是 UTF-8 编码 |
391
- | 图片不切换 | 文件名不一致 | 不同语言目录图片文件名相同 |
1
+ # 国际化避坑指南(V2)
2
+
3
+ > 常见国际化错误与正确做法对照表。**本项目锁 V2**:所有代码示例使用 `@ComponentV2 / @Local / @ObservedV2 / @Trace / AppStorageV2`。i18n API(`resourceManager` / `$r()` 等)与装饰器版本无关。
4
+ >
5
+ > V1 老项目兼容写法见 [`v1-compat.md`](./v1-compat.md)。
6
+
7
+ ---
8
+
9
+ ## ⚠️ 文档说明
10
+
11
+ - ✅ **已验证项**:基于代码仓实际使用或官方文档
12
+ - ⚠️ **未验证项**:需要查证官方文档确认
13
+ - 💡 **建议**:使用前查阅 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/i18n-overview-V5
14
+
15
+ ---
16
+
17
+ ## 目录命名错误
18
+
19
+ ### 错误 vs 正确
20
+
21
+ | 错误写法 | 正确写法 | 说明 |
22
+ |---------|---------|------|
23
+ | `chinese` | `zh` | 使用 ISO 639-1 |
24
+ | `china` | `zh_CN` | 使用 ISO 639-1 + ISO 3166-1 |
25
+ | `Chinese` | `zh-Hans` | 小写,或带区域码 |
26
+ | `zh-CN` | `zh_CN` | 下划线而非连字符(部分版本接受连字符) |
27
+ | `en-uk` | `en_GB` | 英国英文用 `en_GB` |
28
+
29
+ ### 完整语言代码参考
30
+
31
+ | 语言 | 代码 | 区域变体 |
32
+ |------|------|---------|
33
+ | 中文 | `zh` | `zh_CN`, `zh_HK`, `zh_TW`, `zh_Hans`, `zh_Hant` |
34
+ | 英文 | `en` | `en_US`, `en_GB`, `en_AU`, `en_CA` |
35
+ | 日文 | `ja` | - |
36
+ | 韩文 | `ko` | - |
37
+ | 德文 | `de` | `de_DE`, `de_AT`, `de_CH` |
38
+ | 法文 | `fr` | `fr_FR`, `fr_CA`, `fr_CH` |
39
+ | 西班牙文 | `es` | `es_ES`, `es_MX`, `es_AR` |
40
+ | 葡萄牙文 | `pt` | `pt_BR`, `pt_PT` |
41
+ | 俄文 | `ru` | - |
42
+ | 阿拉伯文 | `ar` | - |
43
+
44
+ ---
45
+
46
+ ## string.json 格式错误
47
+
48
+ ### 错误 1:value 使用错误引号
49
+
50
+ ```json
51
+ // 错误 — value 被双引号包裹
52
+ {
53
+ "string": [
54
+ { "name": "app_name", "value": "\"图库\"" }
55
+ ]
56
+ }
57
+
58
+ // 正确 — value 直接是字符串
59
+ {
60
+ "string": [
61
+ { "name": "app_name", "value": "图库" }
62
+ ]
63
+ }
64
+ ```
65
+
66
+ ### 错误 2:缺少必要字段
67
+
68
+ ```json
69
+ // 错误 — 缺少 name 或 value
70
+ {
71
+ "string": [
72
+ { "value": "图库" },
73
+ { "name": "app_name" }
74
+ ]
75
+ }
76
+
77
+ // 正确
78
+ {
79
+ "string": [
80
+ { "name": "app_name", "value": "图库" }
81
+ ]
82
+ }
83
+ ```
84
+
85
+ ### 错误 3:JSON 语法错误
86
+
87
+ ```json
88
+ // 错误 — 尾随逗号
89
+ {
90
+ "string": [
91
+ { "name": "a", "value": "A" },
92
+ { "name": "b", "value": "B" }, // ← 尾随逗号
93
+ ]
94
+ }
95
+
96
+ // 正确
97
+ {
98
+ "string": [
99
+ { "name": "a", "value": "A" },
100
+ { "name": "b", "value": "B" }
101
+ ]
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 硬编码字符串
108
+
109
+ ### 错误 vs 正确
110
+
111
+ | 错误写法 | 正确写法 |
112
+ |---------|---------|
113
+ | `Text('确认')` | `Text($r('app.string.confirm'))` |
114
+ | `Button('取消')` | `Button($r('app.string.cancel'))` |
115
+ | `'共 ' + count + ' 个文件'` | `$r('app.string.file_count', count)` |
116
+ | `promptAction.showToast('操作成功')` | `promptAction.showToast($r('app.string.success'))` |
117
+
118
+ ### 容易硬编码的地方
119
+
120
+ 1. **Button 文字**
121
+ 2. **Dialog 标题和内容**
122
+ 3. **Toast 消息**
123
+ 4. **Placeholder 文案**
124
+ 5. **Error 消息**
125
+ 6. **Menu 项**
126
+ 7. **Tab 标签**
127
+
128
+ ---
129
+
130
+ ## 动态切换不生效
131
+
132
+ ### 原因 1:未声明 supportedLanguages
133
+
134
+ ```json5
135
+ // module.json5
136
+ {
137
+ "module": {
138
+ "supportedLanguages": ["zh", "en", "ja"] // ← 必须声明
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### 原因 2:UI 未触发重建
144
+
145
+ ```typescript
146
+ // 错误 — 切换后 UI 不更新
147
+ async switchLanguage(lang: string) {
148
+ await resMgr.setPreferredLanguage([lang])
149
+ // 直接返回,UI 不会更新
150
+ }
151
+
152
+ // 正确 — 触发状态更新
153
+ async switchLanguage(lang: string) {
154
+ await resMgr.setPreferredLanguage([lang])
155
+ this.refreshKey++ // 触发重建
156
+ this.currentLang = lang
157
+ }
158
+ ```
159
+
160
+ ### 原因 3:组件未持有响应式 locale 状态
161
+
162
+ ```typescript
163
+ // 错误 — 组件不响应状态变化
164
+ @Entry
165
+ @ComponentV2
166
+ struct Page {
167
+ build() {
168
+ Column() {
169
+ Text($r('app.string.hello')) // 不会自动更新
170
+ }
171
+ }
172
+ }
173
+
174
+ // 正确 — 使用 @Local + LocaleModel 包装
175
+ @ObservedV2
176
+ class LocaleModel {
177
+ @Trace currentLanguage: string = 'zh'
178
+ @Trace refreshKey: number = 0
179
+ }
180
+
181
+ @Entry
182
+ @ComponentV2
183
+ struct Page {
184
+ @Local locale: LocaleModel = AppStorageV2.connect(
185
+ LocaleModel, 'locale', () => new LocaleModel()
186
+ )!
187
+
188
+ build() {
189
+ Column() {
190
+ Text($r('app.string.hello'))
191
+ .id('page_' + this.locale.refreshKey) // refreshKey 改即重建
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ > V2 关键点:`@Local` 持有 `@ObservedV2` 实例 → 实例的 `@Trace` 字段变化自动刷新该组件。无需 V1 的 `@StorageLink('refreshKey')` 单字段绑定。
198
+
199
+ ---
200
+
201
+ ## 图片国际化问题
202
+
203
+ ### 错误:文件名不一致
204
+
205
+ ```
206
+ base/media/
207
+ └── logo.png ✓
208
+
209
+ en/media/
210
+ └── logo_en.png ✗ 应该是 logo.png
211
+
212
+ ja/media/
213
+ └── logo_jp.png ✗ 应该是 logo.png
214
+ ```
215
+
216
+ ### 正确做法
217
+
218
+ ```
219
+ base/media/
220
+ └── logo.png ✓
221
+
222
+ en/media/
223
+ └── logo.png ✓(同名文件,内容不同)
224
+
225
+ ja/media/
226
+ └── logo.png ✓(同名文件,内容不同)
227
+ ```
228
+
229
+ ---
230
+
231
+ ## 插值参数使用错误
232
+
233
+ > ⚠️ **验证状态**:`IntlString.format()` API 需要查证官方文档
234
+ >
235
+ > **推荐方案**:使用字符串模板 ` \`...${variable}...\` ` 或 `resourceManager.getStringByName()`
236
+
237
+ ### 错误 1:参数顺序错误
238
+
239
+ ```typescript
240
+ // string.json: { "name": "info", "value": "文件:%s,大小:%s" }
241
+
242
+ // ⚠️ 以下 API 需要查证 - IntlString.format 可能不存在
243
+ // 错误 — 参数顺序颠倒
244
+ // IntlString.format($r('app.string.info'), size, name)
245
+
246
+ // ✅ 推荐 — 使用字符串模板
247
+ const name = 'photo.jpg'
248
+ const size = '1.5MB'
249
+ const message = `文件:${name},大小:${size}`
250
+
251
+ // ⚠️ 或者 — 如果 resourceManager 支持参数
252
+ // resourceManager.getStringByName('info', name, size)
253
+ ```
254
+
255
+ ### 错误 2:参数类型不匹配
256
+
257
+ ```typescript
258
+ // string.json: { "name": "count", "value": "数量:%d" }
259
+
260
+ // ⚠️ 以下 API 需要查证
261
+ // 错误 — 传字符串
262
+ // IntlString.format($r('app.string.count'), '10')
263
+
264
+ // ✅ 推荐 — 使用字符串模板
265
+ const count = 10
266
+ const message = `数量:${count}`
267
+ ```
268
+
269
+ ### 错误 3:参数数量不匹配
270
+
271
+ ```typescript
272
+ // string.json: { "name": "info", "value": "%s 大小:%s" }
273
+
274
+ // ⚠️ 以下 API 需要查证
275
+ // 错误 — 只传一个参数
276
+ // IntlString.format($r('app.string.info'), name)
277
+
278
+ // ✅ 推荐 — 使用字符串模板
279
+ const name = 'file.txt'
280
+ const size = '2KB'
281
+ const message = `${name} 大小:${size}`
282
+ ```
283
+
284
+ ---
285
+
286
+ ## RTL 语言注意事项
287
+
288
+ 阿拉伯文、希伯来文等 RTL(从右到左)语言需要额外处理:
289
+
290
+ ### 1. 布局适配
291
+
292
+ ```typescript
293
+ // 检测 RTL
294
+ import { i18n } from '@kit.LocalizationKit'
295
+
296
+ const isRTL = i18n.isRTL()
297
+ ```
298
+
299
+ ### 2. 布局方向
300
+
301
+ ```typescript
302
+ Column() {
303
+ // RTL 语言下自动反转
304
+ }
305
+ .layoutDirection(isRTL ? LayoutDirection.RTL : LayoutDirection.LTR)
306
+ ```
307
+
308
+ ### 3. 图片翻转
309
+
310
+ ```typescript
311
+ Image($r('app.media.arrow'))
312
+ .rotate({ angle: isRTL ? 180 : 0 }) // 箭头方向需反转
313
+ ```
314
+
315
+ ---
316
+
317
+ ## module.json5 supportedLanguages 陷阱
318
+
319
+ ### 问题:声明了但不生效
320
+
321
+ ```json5
322
+ // module.json5
323
+ {
324
+ "module": {
325
+ "supportedLanguages": ["zh", "en"]
326
+ }
327
+ }
328
+ ```
329
+
330
+ 可能原因:
331
+ 1. **语言代码不匹配**:`"zh-Hans"` vs `"zh"`
332
+ 2. **应用未重启**:切换语言后需要重启应用
333
+ 3. **资源文件缺失**:声明了但对应目录不存在
334
+
335
+ ### 正确声明
336
+
337
+ ```json5
338
+ {
339
+ "module": {
340
+ "supportedLanguages": [
341
+ "zh", // 中文(默认)
342
+ "zh-Hans", // 简体中文(API 12+)
343
+ "zh-Hant", // 繁体中文(API 12+)
344
+ "en", // 英文
345
+ "ja" // 日文
346
+ ]
347
+ }
348
+ }
349
+ ```
350
+
351
+ ---
352
+
353
+ ## 调试技巧
354
+
355
+ ### 1. 打印当前语言
356
+
357
+ ```typescript
358
+ import { resourceManager } from '@kit.LocalizationKit'
359
+
360
+ const langs = await context.resourceManager.getPreferredLanguage()
361
+ hilog.info(0x0000, 'I18N', 'Current language: %{public}s', langs[0])
362
+ ```
363
+
364
+ ### 2. 检查资源是否加载
365
+
366
+ ```typescript
367
+ const resMgr = context.resourceManager
368
+ const str = await resMgr.getStringByName('app_name')
369
+ hilog.info(0x0000, 'I18N', 'String value: %{public}s', str)
370
+ ```
371
+
372
+ ### 3. 检查资源目录是否存在
373
+
374
+ ```typescript
375
+ // 在 DevEco Studio 中
376
+ // File > Project Structure > Module > Resources
377
+ // 查看 supportedLanguages 是否与实际目录匹配
378
+ ```
379
+
380
+ ---
381
+
382
+ ## 错误代码速查
383
+
384
+ | 错误现象 | 可能原因 | 解决方案 |
385
+ |---------|---------|---------|
386
+ | 切换语言后 UI 不更新 | 未触发重建 | 增加 `@Local refreshKey` 或 `@ObservedV2 LocaleModel` 的 `@Trace refreshKey` 字段并改变 ID |
387
+ | `$r()` 引用报错 | string.json 中 key 不存在 | 检查 key 拼写 |
388
+ | setPreferredLanguage 不生效 | 未声明 supportedLanguages | 在 module.json5 中声明 |
389
+ | 英文资源不加载 | 目录名错误 | 使用 `en` 而非 `en_US` |
390
+ | 日文显示乱码 | 文件编码错误 | 确保文件是 UTF-8 编码 |
391
+ | 图片不切换 | 文件名不一致 | 不同语言目录图片文件名相同 |