@harmonyos-arkts/d2h 0.1.10 → 0.1.12

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,496 +1,496 @@
1
- ---
2
- name: arkts-i18n
3
- description: >
4
- ArkTS/HarmonyOS 国际化(i18n)技能(V2 优先,API 12+)。当用户需要添加多语言支持、实现动态语言切换、配置资源文件、处理国际化资源目录结构、扫描硬编码字符串,或任何与"国际化"、"多语言"、"切换语言"相关的问题时,务必触发此 skill。
5
- 即使用户只是说"怎么添加英文"或"怎么做中英文切换",也应触发。
6
- type: domain
7
- domain: engineering
8
- tags: [i18n, localization, multi-language, resource-manager]
9
- ---
10
-
11
- # ArkTS I18N — 国际化指南(V2 优先)
12
-
13
- ## 输入与统一结果
14
-
15
- 根据用户请求和当前工作区确定范围,始终执行同一套资源迁移、代码替换和完整性审计流程,不识别或修改外部编排状态。
16
-
17
- 输入:
18
-
19
- - HarmonyOS 工程根路径;
20
- - Android→HarmonyOS 迁移时提供 Android 工程根路径;纯 HarmonyOS i18n 工作不要求 Android 工程;
21
- - 请求范围内实际读取到的 `values*/strings.xml`、代码引用和 HarmonyOS 资源文件;
22
- - 目标 locales。未显式给出时,从 Android 语言限定符和现有 HarmonyOS 目录确定,不凭空新增语言。
23
-
24
- 返回结果:
25
-
26
- ```json
27
- {
28
- "status": "completed | partial | failed",
29
- "changedFiles": [],
30
- "sourceFiles": [],
31
- "evidence": [],
32
- "domainChecks": [{"name": "", "status": "passed | failed", "evidence": []}],
33
- "unresolved": [{"kind": "", "blocking": true, "location": "", "reason": ""}],
34
- "verificationRequests": [{"type": "build | test | device | review", "scope": [], "reason": ""}],
35
- "suggestedRemediation": [],
36
- "details": {"locales": [], "translatedCount": 0}
37
- }
38
- ```
39
-
40
- `status` 只表示本 Skill 的领域工作状态,不表示工程已构建或功能已验证。不得把翻译摘要当作 Android 源码依据,也不得在仍有阻断性未解决翻译时返回 `completed`。需要构建、语言切换测试或设备核对时写入 `verificationRequests`。
41
-
42
- ## API 版本与项目策略
43
-
44
- 本 skill 的代码示例基于 **API 12+(HarmonyOS 5.0.0+)和 ArkTS V2 装饰器体系**。
45
-
46
- > **项目锁 V2**:所有新生成的"语言切换 / locale 状态 / refreshKey"代码使用 V2 装饰器(`@ComponentV2 / @Local / @Param / @Once / @ObservedV2 / @Trace / AppStorageV2.connect / PersistenceV2.globalConnect`)。i18n 相关 API(`resourceManager` / `intl` / `$r('app.string.xxx')` / `setPreferredLanguage` / `getPreferredLanguage` / `onLanguageConfigurationUpdate`)在 V1/V2 之间**完全不变**。
47
- >
48
- > 仅当需要查阅老项目 V1 写法(`@Component / @State / @StorageLink('currentLanguage')` 等)时,参阅 [`references/v1-compat.md`](./references/v1-compat.md) 与现有 legacy 文档(已加历史标识)。
49
-
50
- V1 → V2 关键差异(仅与本 skill 相关的 state 部分):
51
-
52
- | V1 | V2 |
53
- |---|---|
54
- | `@Component` | `@ComponentV2` |
55
- | `@State refreshKey: number = 0` | `@Local refreshKey: number = 0` |
56
- | `@StorageLink('currentLanguage') lang: string = 'zh'` | `@Local locale: LocaleModel = AppStorageV2.connect(LocaleModel, 'locale', () => new LocaleModel())!`(需先定义 `@ObservedV2 LocaleModel`) |
57
- | `@Prop currentColumns: number = 3` | `@Param @Once currentColumns: number = 3`(只读) |
58
- | `@Watch('lang') onLangChange()` | `@Monitor('lang') onLangChange(m: IMonitor)` |
59
- | `AppStorage.setOrCreate('refreshKey', n)` | `localeModel.refreshKey = n`(直接改 @Trace 字段,自动同步) |
60
- | `PersistentStorage.persistProp('currentLanguage', 'zh')` | `PersistenceV2.globalConnect({ type: LocaleModel, key: 'locale', defaultCreator: () => new LocaleModel() })` |
61
-
62
- > **重要**:i18n 业务 API(`resourceManager.setPreferredLanguage()` / `$r()` / `onLanguageConfigurationUpdate()` 等)**与装饰器版本无关**。本次升级仅替换状态装饰器,API 调用方式不变。
63
-
64
- ---
65
-
66
- ## 资源迁移与补齐模式
67
-
68
- 当前请求或调用范围需要迁移、补齐文案时执行:
69
-
70
- 1. **读取真实来源**:读取请求范围内的 Android `values*/strings.xml`、Gradle `resValue`、引用这些资源的 Kotlin/Java/XML/Compose 文件,以及当前 HarmonyOS `resources/`。摘要只用于定位。
71
- 2. **确定 locales**:按 Android 语言限定符和现有 HarmonyOS 资源目录分组;非语言限定符不作为 locale。
72
- 3. **建立 key 映射**:保持同一业务文案在各 locale 的稳定 key;记录 Android 源文件和 HarmonyOS 目标文件。
73
- 4. **写入资源**:更新 `string.json`、`plural.json`、`strarray.json` 或语言图片,替换 `.ets` 中的用户可见硬编码文案。
74
- 5. **处理翻译缺口**:能够可靠翻译时写入目标 locale;无法可靠翻译或术语依据不足时保留明确占位并写入 `unresolved`,不得伪造已完成翻译。
75
- 6. **完整性审计**:运行 `scripts/audit_i18n_completeness.sh`,并扫描资源值和 ArkTS 文件中的 `[TODO: translate]`、裸 TODO 和未迁移用户可见文案。
76
- 7. **返回结果**:把修改文件、Android 源文件、locales、审计输出、未解决 key 和后续验证请求写入统一领域结果。
77
-
78
- `audit_i18n_completeness.sh` 退出码 1、仍有缺失 key 或未解决翻译时返回 `partial`/`failed`。
79
-
80
- ---
81
-
82
- ## 核心概念
83
-
84
- HarmonyOS 国际化基于「**资源文件目录分级**」和「**系统语言检测**」:
85
-
86
- ```
87
- resources/
88
- ├── base/ // Fallback 资源(无匹配时使用)
89
- ├── zh_CN/ // 简体中文
90
- ├── en_US/ // 美国英文
91
- └── ja/ // 日文
92
- ```
93
-
94
- - **静态引用**:`$r('app.string.xxx')` 由系统根据当前语言自动加载
95
- - **动态切换**:通过 `resourceManager.setPreferredLanguage()` 手动指定语言
96
-
97
- ### ⚠️ base/ 目录的作用
98
-
99
- **base/ 不是"中文目录",而是 Fallback 目录!**
100
-
101
- ```
102
- 资源匹配规则:
103
- 1. 系统根据当前语言查找匹配目录(zh-CN → zh_CN、zh-Hans、zh)
104
- 2. 找到匹配目录?→ ✅ 使用该目录的资源
105
- 3. 未找到?→ 使用 base/ 目录的资源(Fallback)
106
- ```
107
-
108
- ---
109
-
110
- ## 资源文件结构
111
-
112
- ### 标准目录结构
113
-
114
- ```
115
- src/main/resources/
116
- ├── base/ # Fallback 资源
117
- │ ├── element/
118
- │ │ └── string.json # 字符串资源
119
- │ └── media/ # 图片资源
120
- ├── zh_CN/ # 简体中文
121
- │ └── element/
122
- │ └── string.json
123
- ├── en_US/ # 美国英文
124
- │ ├── element/
125
- │ │ └── string.json
126
- │ └── media/
127
- │ └── logo.png # 英文版 logo(可选)
128
- └── ja/ # 日文
129
- └── element/
130
- └── string.json
131
- ```
132
-
133
- ### 目录命名规范
134
-
135
- | 类型 | 格式 | 示例 |
136
- |------|------|------|
137
- | 语言 | ISO 639-1 | `en`, `ja`, `ko`, `zh` |
138
- | 语言+区域 | ISO 639-1 + ISO 3166-1 | `zh_CN`, `en_US`, `zh_TW` |
139
-
140
- > ⚠️ 目录名必须完全匹配,否则系统无法识别
141
-
142
- ---
143
-
144
- ## 字符串资源文件
145
-
146
- ### JSON 格式
147
-
148
- **base/element/string.json**:
149
- ```json
150
- {
151
- "string": [
152
- { "name": "app_name", "value": "App Name" },
153
- { "name": "confirm", "value": "Confirm" }
154
- ]
155
- }
156
- ```
157
-
158
- **zh_CN/element/string.json**:
159
- ```json
160
- {
161
- "string": [
162
- { "name": "app_name", "value": "应用名称" },
163
- { "name": "confirm", "value": "确认" }
164
- ]
165
- }
166
- ```
167
-
168
- ### 引用方式
169
-
170
- ```typescript
171
- // 静态引用
172
- Text($r('app.string.app_name'))
173
- Button($r('app.string.confirm'))
174
- Image($r('app.media.logo'))
175
- ```
176
-
177
- ### 插值参数(占位符)
178
-
179
- ```json
180
- // string.json
181
- { "name": "file_count", "value": "共 %d 个文件" }
182
- ```
183
-
184
- ```typescript
185
- // 代码中替换
186
- const str = resourceManager.getStringByNameSync('file_count')
187
- const message = str.replace('%d', count.toString())
188
- ```
189
-
190
- ---
191
-
192
- ## module.json5 配置
193
-
194
- ### 声明支持的语言
195
-
196
- ```json5
197
- {
198
- "module": {
199
- "name": "entry",
200
- "type": "entry",
201
- "supportedLanguages": [
202
- "zh-Hans", // 简体中文
203
- "en", // 英文
204
- "ja" // 日文
205
- ]
206
- }
207
- }
208
- ```
209
-
210
- > ⚠️ `supportedLanguages` 声明后,`setPreferredLanguage()` 才能正确工作
211
- > 当前代码仓未配置此字段,如需动态语言切换,建议添加
212
-
213
- ---
214
-
215
- ## 动态语言切换(V2)
216
-
217
- ### 基本实现
218
-
219
- V2 项目用 `@Local` 持有 refreshKey;i18n API 不变:
220
-
221
- ```typescript
222
- import { resourceManager } from '@kit.LocalizationKit'
223
- import { common } from '@kit.AbilityKit'
224
-
225
- @Entry
226
- @ComponentV2
227
- struct LanguageSettings {
228
- private context = getContext(this) as common.UIAbilityContext
229
- @Local private refreshKey: number = 0
230
-
231
- async switchLanguage(lang: string): Promise<void> {
232
- const resMgr = this.context.resourceManager
233
- await resMgr.setPreferredLanguage([lang])
234
- this.refreshKey++ // 触发 UI 重建
235
- }
236
-
237
- build() {
238
- Column() {
239
- Text($r('app.string.welcome'))
240
- .fontSize(20)
241
- }
242
- .id('lang_' + this.refreshKey)
243
- }
244
- }
245
- ```
246
-
247
- ### 全局共享 locale 状态(V2 推荐)
248
-
249
- 把 `currentLanguage` / `refreshKey` 封装到一个 `@ObservedV2 LocaleModel`,用 `AppStorageV2.connect` 在所有页面共享单例:
250
-
251
- ```typescript
252
- // common/LocaleModel.ets
253
- @ObservedV2
254
- export class LocaleModel {
255
- @Trace currentLanguage: string = 'zh'
256
- @Trace refreshKey: number = 0
257
- }
258
-
259
- // 任意 V2 组件
260
- @ComponentV2
261
- struct AnyPage {
262
- @Local locale: LocaleModel = AppStorageV2.connect(
263
- LocaleModel,
264
- 'locale',
265
- () => new LocaleModel()
266
- )!
267
-
268
- build() {
269
- Column() {
270
- Text($r('app.string.hello'))
271
- .id('page_' + this.locale.refreshKey) // refreshKey 改 → 自动重建
272
- }
273
- }
274
- }
275
- ```
276
-
277
- > 修改 `this.locale.refreshKey++` 即可同步刷新所有 `connect` 同一 key 的组件,无需 V1 `@StorageLink` 散字段。
278
-
279
- ### 获取当前系统语言
280
-
281
- ```typescript
282
- async getCurrentLanguage(): Promise<string> {
283
- const resMgr = this.context.resourceManager
284
- const languages = await resMgr.getPreferredLanguage()
285
- return languages[0] || 'zh'
286
- }
287
- ```
288
-
289
- ### 监听语言变化
290
-
291
- ```typescript
292
- // 在 EntryAbility 中
293
- onLanguageConfigurationUpdate(): void {
294
- hilog.info(0x0000, 'I18N', 'Language changed')
295
- }
296
- ```
297
-
298
- ---
299
-
300
- ## 图片国际化
301
-
302
- ```
303
- resources/
304
- ├── base/media/logo.png # 默认 logo
305
- ├── en/media/logo.png # 英文版 logo(同名)
306
- └── zh_CN/media/logo.png # 中文版 logo(同名)
307
- ```
308
-
309
- ```typescript
310
- // 同一引用路径,不同语言加载不同文件
311
- Image($r('app.media.logo'))
312
- ```
313
-
314
- > ⚠️ 不同语言目录下的图片**文件名必须一致**
315
-
316
- ---
317
-
318
- ## 常见错误
319
-
320
- ### 错误 1:硬编码字符串
321
-
322
- ```typescript
323
- // ❌ 错误
324
- Text('确认')
325
- Button('登录')
326
-
327
- // ✅ 正确
328
- Text($r('app.string.confirm'))
329
- Button($r('app.string.login'))
330
- ```
331
-
332
- ### 错误 2:目录命名错误
333
-
334
- ```typescript
335
- // ❌ 错误
336
- resources/chinese/element/string.json
337
-
338
- // ✅ 正确
339
- resources/zh_CN/element/string.json
340
- ```
341
-
342
- ### 错误 3:未声明 supportedLanguages
343
-
344
- 导致 `setPreferredLanguage()` 不生效。
345
-
346
- ### 错误 4:动态切换后 UI 不更新
347
-
348
- ```typescript
349
- // ❌ 错误 — 直接返回
350
- await resMgr.setPreferredLanguage([lang])
351
-
352
- // ✅ 正确 — 触发重建
353
- await resMgr.setPreferredLanguage([lang])
354
- this.refreshKey++
355
- ```
356
-
357
- ### 错误 5:string.json 格式错误
358
-
359
- ```json
360
- // ❌ 错误 — value 被双引号包裹
361
- { "name": "app_name", "value": "\"图库\"" }
362
-
363
- // ✅ 正确
364
- { "name": "app_name", "value": "图库" }
365
- ```
366
-
367
- ---
368
-
369
- ## 硬编码字符串扫描与迁移
370
-
371
- 当用户需要**扫描项目中的硬编码字符串并迁移到 resources**时,执行以下流程:
372
-
373
- ### 步骤 1:扫描硬编码
374
-
375
- ```bash
376
- rg "Text\(['\"]" --type ets -n
377
- rg "Button\(['\"]" --type ets -n
378
- rg "showToast\(['\"]" --type ets -n
379
- ```
380
-
381
- ### 步骤 2:识别需要迁移的字符串
382
-
383
- **排除**:已使用 `$r()` 引用、URL、代码变量、正则、单字符
384
-
385
- **需要迁移**:
386
- ```typescript
387
- Text('确认')
388
- Button('取消')
389
- .title('设置页面')
390
- promptAction.showToast('操作成功')
391
- ```
392
-
393
- ### 步骤 3:生成资源 key
394
-
395
- | 场景 | key 格式 | 示例 |
396
- |------|---------|------|
397
- | 按钮 | `btn_xxx` | `btn_confirm`, `btn_cancel` |
398
- | 标题 | `title_xxx` | `title_settings` |
399
- | 消息 | `msg_xxx` | `msg_delete_confirm` |
400
- | Toast | `toast_xxx` | `toast_save_success` |
401
-
402
- ### 步骤 4:添加到 string.json
403
-
404
- **base/element/string.json**:
405
- ```json
406
- { "name": "confirm", "value": "确认" }
407
- ```
408
-
409
- **en_US/element/string.json**:
410
- ```json
411
- { "name": "confirm", "value": "Confirm" }
412
- ```
413
-
414
- ### 步骤 5:替换代码
415
-
416
- ```typescript
417
- // 替换前
418
- Text('确认')
419
-
420
- // 替换后
421
- Text($r('app.string.confirm'))
422
- ```
423
-
424
- ### 步骤 6:验证
425
-
426
- **1) 跨 locale key 一致性(自动)** — 跑本 skill 自带的完整性脚本:
427
-
428
- ```bash
429
- bash <skill-root>/arkts-i18n/scripts/audit_i18n_completeness.sh \
430
- --project-root <abs> \
431
- --output-json docs/i18n-audit.json
432
- ```
433
-
434
- 职责:扫 `string.json` / `plural.json` / `strarray.json`,base 的全部 key 必须在每个 language locale(zh_CN / en_US / ...)出现;mode locale(dark / horizontal / vertical 等)按差异覆盖即可,豁免。
435
-
436
- 退出码:`0` 全 PASS / `1` 有 key_missing FAIL / `2` 脚本参数错误。
437
-
438
- `audit_i18n_completeness.sh` 负责 locale 间 key 集合一致性,不负责识别占位文本。当前调用范围收尾时还必须使用文本搜索扫描 HarmonyOS 资源值和 `.ets` 文件中的 `[TODO: translate]`、裸 TODO,以及仍然硬编码的用户可见文案;发现项写入 `unresolved`。不得依赖其他 Skill 代为扫描。
439
-
440
- **2) 手工 / 编译检查**:
441
-
442
- - [ ] 所有用户可见字符串都已替换为 `$r()` 引用
443
- - [ ] 替换后能正常编译
444
-
445
- ---
446
-
447
- ## 生成检查清单
448
-
449
- - [ ] 资源目录使用标准语言代码
450
- - [ ] string.json 格式正确
451
- - [ ] 所有用户可见字符串使用 `$r()` 引用
452
- - [ ] module.json5 中声明 `supportedLanguages`(如需动态切换)
453
- - [ ] 不同语言目录下的图片文件名一致
454
- - [ ] 已扫描硬编码字符串并迁移到 resources
455
-
456
- ---
457
-
458
- ## API 验证状态
459
-
460
- | API | 状态 |
461
- |-----|------|
462
- | `resourceManager.getStringByNameSync()` | ✅ 已验证 |
463
- | `resourceManager.getStringByName()` | ⚠️ 需查证 |
464
- | `setPreferredLanguage()` | ⚠️ 需查证 |
465
- | `getPreferredLanguage()` | ⚠️ 需查证 |
466
- | `$r('app.string.xxx')` | ✅ 已验证 |
467
-
468
- ---
469
-
470
- ## References
471
-
472
- 详细文档和完整代码示例请参阅(**全部代码示例已升级 V2**):
473
-
474
- | 文件 | 内容 |
475
- |------|------|
476
- | `references/resource-file-structure.md` | 完整资源目录结构模板(与装饰器版本无关) |
477
- | `references/dynamic-language-switch.md` | 动态语言切换完整代码(V2,含 LocaleModel) |
478
- | `references/common-pitfalls.md` | 避坑指南与错误对照表(V2) |
479
- | `references/hardcoded-string-scanner.md` | 硬编码扫描脚本和工具(与装饰器版本无关) |
480
- | `references/language-codes.md` | 语言代码对照表(ISO 标准) |
481
- | `references/code-examples.md` | ColumnsDialog 等代码示例(V2) |
482
- | `references/static-vs-dynamic.md` | 纯静态 vs 动态切换方案对比 |
483
- | `references/v1-compat.md` | **V1 兼容写法**(@Component / @State / @StorageLink),仅老项目查阅 |
484
-
485
- ---
486
-
487
- ## 可选领域协作
488
-
489
- 下列能力存在时可以协作,但不改变本 Skill 的输入、流程或统一结果:
490
-
491
- | 需要什么 | 读取哪里 |
492
- |---------|---------|
493
- | V2 状态管理、页面和跨页 locale 状态 | `arkts-mvvm-architecture` |
494
- | 图片资源与语言图片迁移 | `android2hmos_resources_convert` |
495
-
496
- 本 Skill 不更新迁移报告、不创建 checkpoint、不调用 `complete`,也不选择仓库级构建或设备测试命令;这些动作通过 `verificationRequests` 交给调用方。
1
+ ---
2
+ name: arkts-i18n
3
+ description: >
4
+ ArkTS/HarmonyOS 国际化(i18n)技能(V2 优先,API 12+)。当用户需要添加多语言支持、实现动态语言切换、配置资源文件、处理国际化资源目录结构、扫描硬编码字符串,或任何与"国际化"、"多语言"、"切换语言"相关的问题时,务必触发此 skill。
5
+ 即使用户只是说"怎么添加英文"或"怎么做中英文切换",也应触发。
6
+ type: domain
7
+ domain: engineering
8
+ tags: [i18n, localization, multi-language, resource-manager]
9
+ ---
10
+
11
+ # ArkTS I18N — 国际化指南(V2 优先)
12
+
13
+ ## 输入与统一结果
14
+
15
+ 根据用户请求和当前工作区确定范围,始终执行同一套资源迁移、代码替换和完整性审计流程,不识别或修改外部编排状态。
16
+
17
+ 输入:
18
+
19
+ - HarmonyOS 工程根路径;
20
+ - Android→HarmonyOS 迁移时提供 Android 工程根路径;纯 HarmonyOS i18n 工作不要求 Android 工程;
21
+ - 请求范围内实际读取到的 `values*/strings.xml`、代码引用和 HarmonyOS 资源文件;
22
+ - 目标 locales。未显式给出时,从 Android 语言限定符和现有 HarmonyOS 目录确定,不凭空新增语言。
23
+
24
+ 返回结果:
25
+
26
+ ```json
27
+ {
28
+ "status": "completed | partial | failed",
29
+ "changedFiles": [],
30
+ "sourceFiles": [],
31
+ "evidence": [],
32
+ "domainChecks": [{"name": "", "status": "passed | failed", "evidence": []}],
33
+ "unresolved": [{"kind": "", "blocking": true, "location": "", "reason": ""}],
34
+ "verificationRequests": [{"type": "build | test | device | review", "scope": [], "reason": ""}],
35
+ "suggestedRemediation": [],
36
+ "details": {"locales": [], "translatedCount": 0}
37
+ }
38
+ ```
39
+
40
+ `status` 只表示本 Skill 的领域工作状态,不表示工程已构建或功能已验证。不得把翻译摘要当作 Android 源码依据,也不得在仍有阻断性未解决翻译时返回 `completed`。需要构建、语言切换测试或设备核对时写入 `verificationRequests`。
41
+
42
+ ## API 版本与项目策略
43
+
44
+ 本 skill 的代码示例基于 **API 12+(HarmonyOS 5.0.0+)和 ArkTS V2 装饰器体系**。
45
+
46
+ > **项目锁 V2**:所有新生成的"语言切换 / locale 状态 / refreshKey"代码使用 V2 装饰器(`@ComponentV2 / @Local / @Param / @Once / @ObservedV2 / @Trace / AppStorageV2.connect / PersistenceV2.globalConnect`)。i18n 相关 API(`resourceManager` / `intl` / `$r('app.string.xxx')` / `setPreferredLanguage` / `getPreferredLanguage` / `onLanguageConfigurationUpdate`)在 V1/V2 之间**完全不变**。
47
+ >
48
+ > 仅当需要查阅老项目 V1 写法(`@Component / @State / @StorageLink('currentLanguage')` 等)时,参阅 [`references/v1-compat.md`](./references/v1-compat.md) 与现有 legacy 文档(已加历史标识)。
49
+
50
+ V1 → V2 关键差异(仅与本 skill 相关的 state 部分):
51
+
52
+ | V1 | V2 |
53
+ |---|---|
54
+ | `@Component` | `@ComponentV2` |
55
+ | `@State refreshKey: number = 0` | `@Local refreshKey: number = 0` |
56
+ | `@StorageLink('currentLanguage') lang: string = 'zh'` | `@Local locale: LocaleModel = AppStorageV2.connect(LocaleModel, 'locale', () => new LocaleModel())!`(需先定义 `@ObservedV2 LocaleModel`) |
57
+ | `@Prop currentColumns: number = 3` | `@Param @Once currentColumns: number = 3`(只读) |
58
+ | `@Watch('lang') onLangChange()` | `@Monitor('lang') onLangChange(m: IMonitor)` |
59
+ | `AppStorage.setOrCreate('refreshKey', n)` | `localeModel.refreshKey = n`(直接改 @Trace 字段,自动同步) |
60
+ | `PersistentStorage.persistProp('currentLanguage', 'zh')` | `PersistenceV2.globalConnect({ type: LocaleModel, key: 'locale', defaultCreator: () => new LocaleModel() })` |
61
+
62
+ > **重要**:i18n 业务 API(`resourceManager.setPreferredLanguage()` / `$r()` / `onLanguageConfigurationUpdate()` 等)**与装饰器版本无关**。本次升级仅替换状态装饰器,API 调用方式不变。
63
+
64
+ ---
65
+
66
+ ## 资源迁移与补齐模式
67
+
68
+ 当前请求或调用范围需要迁移、补齐文案时执行:
69
+
70
+ 1. **读取真实来源**:读取请求范围内的 Android `values*/strings.xml`、Gradle `resValue`、引用这些资源的 Kotlin/Java/XML/Compose 文件,以及当前 HarmonyOS `resources/`。摘要只用于定位。
71
+ 2. **确定 locales**:按 Android 语言限定符和现有 HarmonyOS 资源目录分组;非语言限定符不作为 locale。
72
+ 3. **建立 key 映射**:保持同一业务文案在各 locale 的稳定 key;记录 Android 源文件和 HarmonyOS 目标文件。
73
+ 4. **写入资源**:更新 `string.json`、`plural.json`、`strarray.json` 或语言图片,替换 `.ets` 中的用户可见硬编码文案。
74
+ 5. **处理翻译缺口**:能够可靠翻译时写入目标 locale;无法可靠翻译或术语依据不足时保留明确占位并写入 `unresolved`,不得伪造已完成翻译。
75
+ 6. **完整性审计**:运行 `scripts/audit_i18n_completeness.sh`,并扫描资源值和 ArkTS 文件中的 `[TODO: translate]`、裸 TODO 和未迁移用户可见文案。
76
+ 7. **返回结果**:把修改文件、Android 源文件、locales、审计输出、未解决 key 和后续验证请求写入统一领域结果。
77
+
78
+ `audit_i18n_completeness.sh` 退出码 1、仍有缺失 key 或未解决翻译时返回 `partial`/`failed`。
79
+
80
+ ---
81
+
82
+ ## 核心概念
83
+
84
+ HarmonyOS 国际化基于「**资源文件目录分级**」和「**系统语言检测**」:
85
+
86
+ ```
87
+ resources/
88
+ ├── base/ // Fallback 资源(无匹配时使用)
89
+ ├── zh_CN/ // 简体中文
90
+ ├── en_US/ // 美国英文
91
+ └── ja/ // 日文
92
+ ```
93
+
94
+ - **静态引用**:`$r('app.string.xxx')` 由系统根据当前语言自动加载
95
+ - **动态切换**:通过 `resourceManager.setPreferredLanguage()` 手动指定语言
96
+
97
+ ### ⚠️ base/ 目录的作用
98
+
99
+ **base/ 不是"中文目录",而是 Fallback 目录!**
100
+
101
+ ```
102
+ 资源匹配规则:
103
+ 1. 系统根据当前语言查找匹配目录(zh-CN → zh_CN、zh-Hans、zh)
104
+ 2. 找到匹配目录?→ ✅ 使用该目录的资源
105
+ 3. 未找到?→ 使用 base/ 目录的资源(Fallback)
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 资源文件结构
111
+
112
+ ### 标准目录结构
113
+
114
+ ```
115
+ src/main/resources/
116
+ ├── base/ # Fallback 资源
117
+ │ ├── element/
118
+ │ │ └── string.json # 字符串资源
119
+ │ └── media/ # 图片资源
120
+ ├── zh_CN/ # 简体中文
121
+ │ └── element/
122
+ │ └── string.json
123
+ ├── en_US/ # 美国英文
124
+ │ ├── element/
125
+ │ │ └── string.json
126
+ │ └── media/
127
+ │ └── logo.png # 英文版 logo(可选)
128
+ └── ja/ # 日文
129
+ └── element/
130
+ └── string.json
131
+ ```
132
+
133
+ ### 目录命名规范
134
+
135
+ | 类型 | 格式 | 示例 |
136
+ |------|------|------|
137
+ | 语言 | ISO 639-1 | `en`, `ja`, `ko`, `zh` |
138
+ | 语言+区域 | ISO 639-1 + ISO 3166-1 | `zh_CN`, `en_US`, `zh_TW` |
139
+
140
+ > ⚠️ 目录名必须完全匹配,否则系统无法识别
141
+
142
+ ---
143
+
144
+ ## 字符串资源文件
145
+
146
+ ### JSON 格式
147
+
148
+ **base/element/string.json**:
149
+ ```json
150
+ {
151
+ "string": [
152
+ { "name": "app_name", "value": "App Name" },
153
+ { "name": "confirm", "value": "Confirm" }
154
+ ]
155
+ }
156
+ ```
157
+
158
+ **zh_CN/element/string.json**:
159
+ ```json
160
+ {
161
+ "string": [
162
+ { "name": "app_name", "value": "应用名称" },
163
+ { "name": "confirm", "value": "确认" }
164
+ ]
165
+ }
166
+ ```
167
+
168
+ ### 引用方式
169
+
170
+ ```typescript
171
+ // 静态引用
172
+ Text($r('app.string.app_name'))
173
+ Button($r('app.string.confirm'))
174
+ Image($r('app.media.logo'))
175
+ ```
176
+
177
+ ### 插值参数(占位符)
178
+
179
+ ```json
180
+ // string.json
181
+ { "name": "file_count", "value": "共 %d 个文件" }
182
+ ```
183
+
184
+ ```typescript
185
+ // 代码中替换
186
+ const str = resourceManager.getStringByNameSync('file_count')
187
+ const message = str.replace('%d', count.toString())
188
+ ```
189
+
190
+ ---
191
+
192
+ ## module.json5 配置
193
+
194
+ ### 声明支持的语言
195
+
196
+ ```json5
197
+ {
198
+ "module": {
199
+ "name": "entry",
200
+ "type": "entry",
201
+ "supportedLanguages": [
202
+ "zh-Hans", // 简体中文
203
+ "en", // 英文
204
+ "ja" // 日文
205
+ ]
206
+ }
207
+ }
208
+ ```
209
+
210
+ > ⚠️ `supportedLanguages` 声明后,`setPreferredLanguage()` 才能正确工作
211
+ > 当前代码仓未配置此字段,如需动态语言切换,建议添加
212
+
213
+ ---
214
+
215
+ ## 动态语言切换(V2)
216
+
217
+ ### 基本实现
218
+
219
+ V2 项目用 `@Local` 持有 refreshKey;i18n API 不变:
220
+
221
+ ```typescript
222
+ import { resourceManager } from '@kit.LocalizationKit'
223
+ import { common } from '@kit.AbilityKit'
224
+
225
+ @Entry
226
+ @ComponentV2
227
+ struct LanguageSettings {
228
+ private context = getContext(this) as common.UIAbilityContext
229
+ @Local private refreshKey: number = 0
230
+
231
+ async switchLanguage(lang: string): Promise<void> {
232
+ const resMgr = this.context.resourceManager
233
+ await resMgr.setPreferredLanguage([lang])
234
+ this.refreshKey++ // 触发 UI 重建
235
+ }
236
+
237
+ build() {
238
+ Column() {
239
+ Text($r('app.string.welcome'))
240
+ .fontSize(20)
241
+ }
242
+ .id('lang_' + this.refreshKey)
243
+ }
244
+ }
245
+ ```
246
+
247
+ ### 全局共享 locale 状态(V2 推荐)
248
+
249
+ 把 `currentLanguage` / `refreshKey` 封装到一个 `@ObservedV2 LocaleModel`,用 `AppStorageV2.connect` 在所有页面共享单例:
250
+
251
+ ```typescript
252
+ // common/LocaleModel.ets
253
+ @ObservedV2
254
+ export class LocaleModel {
255
+ @Trace currentLanguage: string = 'zh'
256
+ @Trace refreshKey: number = 0
257
+ }
258
+
259
+ // 任意 V2 组件
260
+ @ComponentV2
261
+ struct AnyPage {
262
+ @Local locale: LocaleModel = AppStorageV2.connect(
263
+ LocaleModel,
264
+ 'locale',
265
+ () => new LocaleModel()
266
+ )!
267
+
268
+ build() {
269
+ Column() {
270
+ Text($r('app.string.hello'))
271
+ .id('page_' + this.locale.refreshKey) // refreshKey 改 → 自动重建
272
+ }
273
+ }
274
+ }
275
+ ```
276
+
277
+ > 修改 `this.locale.refreshKey++` 即可同步刷新所有 `connect` 同一 key 的组件,无需 V1 `@StorageLink` 散字段。
278
+
279
+ ### 获取当前系统语言
280
+
281
+ ```typescript
282
+ async getCurrentLanguage(): Promise<string> {
283
+ const resMgr = this.context.resourceManager
284
+ const languages = await resMgr.getPreferredLanguage()
285
+ return languages[0] || 'zh'
286
+ }
287
+ ```
288
+
289
+ ### 监听语言变化
290
+
291
+ ```typescript
292
+ // 在 EntryAbility 中
293
+ onLanguageConfigurationUpdate(): void {
294
+ hilog.info(0x0000, 'I18N', 'Language changed')
295
+ }
296
+ ```
297
+
298
+ ---
299
+
300
+ ## 图片国际化
301
+
302
+ ```
303
+ resources/
304
+ ├── base/media/logo.png # 默认 logo
305
+ ├── en/media/logo.png # 英文版 logo(同名)
306
+ └── zh_CN/media/logo.png # 中文版 logo(同名)
307
+ ```
308
+
309
+ ```typescript
310
+ // 同一引用路径,不同语言加载不同文件
311
+ Image($r('app.media.logo'))
312
+ ```
313
+
314
+ > ⚠️ 不同语言目录下的图片**文件名必须一致**
315
+
316
+ ---
317
+
318
+ ## 常见错误
319
+
320
+ ### 错误 1:硬编码字符串
321
+
322
+ ```typescript
323
+ // ❌ 错误
324
+ Text('确认')
325
+ Button('登录')
326
+
327
+ // ✅ 正确
328
+ Text($r('app.string.confirm'))
329
+ Button($r('app.string.login'))
330
+ ```
331
+
332
+ ### 错误 2:目录命名错误
333
+
334
+ ```typescript
335
+ // ❌ 错误
336
+ resources/chinese/element/string.json
337
+
338
+ // ✅ 正确
339
+ resources/zh_CN/element/string.json
340
+ ```
341
+
342
+ ### 错误 3:未声明 supportedLanguages
343
+
344
+ 导致 `setPreferredLanguage()` 不生效。
345
+
346
+ ### 错误 4:动态切换后 UI 不更新
347
+
348
+ ```typescript
349
+ // ❌ 错误 — 直接返回
350
+ await resMgr.setPreferredLanguage([lang])
351
+
352
+ // ✅ 正确 — 触发重建
353
+ await resMgr.setPreferredLanguage([lang])
354
+ this.refreshKey++
355
+ ```
356
+
357
+ ### 错误 5:string.json 格式错误
358
+
359
+ ```json
360
+ // ❌ 错误 — value 被双引号包裹
361
+ { "name": "app_name", "value": "\"图库\"" }
362
+
363
+ // ✅ 正确
364
+ { "name": "app_name", "value": "图库" }
365
+ ```
366
+
367
+ ---
368
+
369
+ ## 硬编码字符串扫描与迁移
370
+
371
+ 当用户需要**扫描项目中的硬编码字符串并迁移到 resources**时,执行以下流程:
372
+
373
+ ### 步骤 1:扫描硬编码
374
+
375
+ ```bash
376
+ rg "Text\(['\"]" --type ets -n
377
+ rg "Button\(['\"]" --type ets -n
378
+ rg "showToast\(['\"]" --type ets -n
379
+ ```
380
+
381
+ ### 步骤 2:识别需要迁移的字符串
382
+
383
+ **排除**:已使用 `$r()` 引用、URL、代码变量、正则、单字符
384
+
385
+ **需要迁移**:
386
+ ```typescript
387
+ Text('确认')
388
+ Button('取消')
389
+ .title('设置页面')
390
+ promptAction.showToast('操作成功')
391
+ ```
392
+
393
+ ### 步骤 3:生成资源 key
394
+
395
+ | 场景 | key 格式 | 示例 |
396
+ |------|---------|------|
397
+ | 按钮 | `btn_xxx` | `btn_confirm`, `btn_cancel` |
398
+ | 标题 | `title_xxx` | `title_settings` |
399
+ | 消息 | `msg_xxx` | `msg_delete_confirm` |
400
+ | Toast | `toast_xxx` | `toast_save_success` |
401
+
402
+ ### 步骤 4:添加到 string.json
403
+
404
+ **base/element/string.json**:
405
+ ```json
406
+ { "name": "confirm", "value": "确认" }
407
+ ```
408
+
409
+ **en_US/element/string.json**:
410
+ ```json
411
+ { "name": "confirm", "value": "Confirm" }
412
+ ```
413
+
414
+ ### 步骤 5:替换代码
415
+
416
+ ```typescript
417
+ // 替换前
418
+ Text('确认')
419
+
420
+ // 替换后
421
+ Text($r('app.string.confirm'))
422
+ ```
423
+
424
+ ### 步骤 6:验证
425
+
426
+ **1) 跨 locale key 一致性(自动)** — 跑本 skill 自带的完整性脚本:
427
+
428
+ ```bash
429
+ bash <skill-root>/arkts-i18n/scripts/audit_i18n_completeness.sh \
430
+ --project-root <abs> \
431
+ --output-json docs/i18n-audit.json
432
+ ```
433
+
434
+ 职责:扫 `string.json` / `plural.json` / `strarray.json`,base 的全部 key 必须在每个 language locale(zh_CN / en_US / ...)出现;mode locale(dark / horizontal / vertical 等)按差异覆盖即可,豁免。
435
+
436
+ 退出码:`0` 全 PASS / `1` 有 key_missing FAIL / `2` 脚本参数错误。
437
+
438
+ `audit_i18n_completeness.sh` 负责 locale 间 key 集合一致性,不负责识别占位文本。当前调用范围收尾时还必须使用文本搜索扫描 HarmonyOS 资源值和 `.ets` 文件中的 `[TODO: translate]`、裸 TODO,以及仍然硬编码的用户可见文案;发现项写入 `unresolved`。不得依赖其他 Skill 代为扫描。
439
+
440
+ **2) 手工 / 编译检查**:
441
+
442
+ - [ ] 所有用户可见字符串都已替换为 `$r()` 引用
443
+ - [ ] 替换后能正常编译
444
+
445
+ ---
446
+
447
+ ## 生成检查清单
448
+
449
+ - [ ] 资源目录使用标准语言代码
450
+ - [ ] string.json 格式正确
451
+ - [ ] 所有用户可见字符串使用 `$r()` 引用
452
+ - [ ] module.json5 中声明 `supportedLanguages`(如需动态切换)
453
+ - [ ] 不同语言目录下的图片文件名一致
454
+ - [ ] 已扫描硬编码字符串并迁移到 resources
455
+
456
+ ---
457
+
458
+ ## API 验证状态
459
+
460
+ | API | 状态 |
461
+ |-----|------|
462
+ | `resourceManager.getStringByNameSync()` | ✅ 已验证 |
463
+ | `resourceManager.getStringByName()` | ⚠️ 需查证 |
464
+ | `setPreferredLanguage()` | ⚠️ 需查证 |
465
+ | `getPreferredLanguage()` | ⚠️ 需查证 |
466
+ | `$r('app.string.xxx')` | ✅ 已验证 |
467
+
468
+ ---
469
+
470
+ ## References
471
+
472
+ 详细文档和完整代码示例请参阅(**全部代码示例已升级 V2**):
473
+
474
+ | 文件 | 内容 |
475
+ |------|------|
476
+ | `references/resource-file-structure.md` | 完整资源目录结构模板(与装饰器版本无关) |
477
+ | `references/dynamic-language-switch.md` | 动态语言切换完整代码(V2,含 LocaleModel) |
478
+ | `references/common-pitfalls.md` | 避坑指南与错误对照表(V2) |
479
+ | `references/hardcoded-string-scanner.md` | 硬编码扫描脚本和工具(与装饰器版本无关) |
480
+ | `references/language-codes.md` | 语言代码对照表(ISO 标准) |
481
+ | `references/code-examples.md` | ColumnsDialog 等代码示例(V2) |
482
+ | `references/static-vs-dynamic.md` | 纯静态 vs 动态切换方案对比 |
483
+ | `references/v1-compat.md` | **V1 兼容写法**(@Component / @State / @StorageLink),仅老项目查阅 |
484
+
485
+ ---
486
+
487
+ ## 可选领域协作
488
+
489
+ 下列能力存在时可以协作,但不改变本 Skill 的输入、流程或统一结果:
490
+
491
+ | 需要什么 | 读取哪里 |
492
+ |---------|---------|
493
+ | V2 状态管理、页面和跨页 locale 状态 | `arkts-mvvm-architecture` |
494
+ | 图片资源与语言图片迁移 | `android2hmos_resources_convert` |
495
+
496
+ 本 Skill 不更新迁移报告、不创建 checkpoint、不调用 `complete`,也不选择仓库级构建或设备测试命令;这些动作通过 `verificationRequests` 交给调用方。