@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.
- package/.claude-plugin/marketplace.json +15 -13
- package/.claude-plugin/plugin.json +13 -7
- package/AGENTS.md +5 -5
- package/README.md +212 -212
- package/agents/android-to-hmos-00-orchestrator.md +318 -318
- package/agents/d2h.md +157 -157
- package/dist/bin/install-opencode.js +30 -30
- package/docs/environment-check.md +86 -86
- package/install-opencode.sh +160 -160
- package/opencode/agents.json +16 -16
- package/opencode/vscode-d2h-package.json +7 -7
- package/opencode/vscode-d2h-plugin.mjs +29 -29
- package/package.json +54 -54
- package/schemas/android-source-manifest.schema.json +25 -25
- package/schemas/checkpoint-provenance.schema.json +67 -67
- package/schemas/final-acceptance.schema.json +29 -29
- package/schemas/managed-evidence-index.schema.json +15 -15
- package/schemas/managed-evidence.schema.json +114 -114
- package/schemas/migration-config.schema.json +51 -51
- package/schemas/migration-report-index.schema.json +39 -39
- package/schemas/migration-report-item.schema.json +51 -51
- package/schemas/migration-report-summary.schema.json +25 -25
- package/schemas/migration-status.schema.json +202 -202
- package/schemas/preflight.schema.json +60 -60
- package/schemas/source-order-audit.schema.json +16 -16
- package/schemas/source-provenance-event.schema.json +19 -19
- package/schemas/spec-app-shard.schema.json +45 -45
- package/schemas/spec-fact-corrections-shard.schema.json +13 -13
- package/schemas/spec-features-shard.schema.json +43 -43
- package/schemas/spec-index.schema.json +26 -26
- package/schemas/spec-interactions-shard.schema.json +40 -40
- package/schemas/spec-page.schema.json +98 -98
- package/schemas/spec-pages-index.schema.json +1 -1
- package/schemas/spec-unresolved-shard.schema.json +1 -1
- package/schemas/task-envelope.schema.json +109 -109
- package/schemas/task-plan.schema.json +341 -341
- package/scripts/check-environment.mjs +320 -320
- package/skills/android2hmos_resources_convert/SKILL.md +180 -180
- package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -230
- package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -175
- package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -513
- package/skills/arkts-app-identity/SKILL.md +238 -238
- package/skills/arkts-i18n/SKILL.md +496 -496
- package/skills/arkts-i18n/evals/evals.json +84 -84
- package/skills/arkts-i18n/references/code-examples.md +302 -302
- package/skills/arkts-i18n/references/common-pitfalls.md +391 -391
- package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -604
- package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -348
- package/skills/arkts-i18n/references/language-codes.md +104 -104
- package/skills/arkts-i18n/references/resource-file-structure.md +775 -775
- package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -242
- package/skills/arkts-i18n/references/v1-compat.md +244 -244
- package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -174
- package/skills/arkts-icon-sizing/SKILL.md +211 -211
- package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -131
- package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -88
- package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -179
- package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -119
- package/skills/arkts-mvvm-architecture/SKILL.md +613 -613
- package/skills/d2h-hypium-forge/SKILL.md +309 -309
- package/skills/d2h-hypium-forge/references/functional-spec-template.md +140 -140
- package/skills/d2h-hypium-forge/references/official-api.md +135 -135
- package/skills/d2h-hypium-forge/references/pitfalls.md +98 -98
- package/skills/d2h-hypium-forge/references/testability-template.md +71 -71
- package/skills/d2h-hypium-forge/scripts/_common_template.py +659 -659
- package/skills/d2h-hypium-forge/scripts/analyze_results.py +165 -165
- package/skills/d2h-hypium-forge/scripts/assert_audit.py +107 -107
- package/skills/d2h-hypium-forge/scripts/collect_runtime_pages.py +169 -169
- package/skills/d2h-hypium-forge/scripts/forge_report.py +325 -325
- package/skills/d2h-hypium-forge/scripts/inject_resource_ids.py +114 -114
- package/skills/d2h-hypium-forge/scripts/lint_asserts.py +80 -80
- package/skills/d2h-hypium-forge/scripts/lint_locate.py +60 -60
- package/skills/d2h-hypium-forge/scripts/merge_appgraph.py +249 -249
- package/skills/d2h-hypium-forge/scripts/precondition.py +140 -140
- package/skills/d2h-hypium-forge/scripts/preinject_ids.py +232 -232
- package/skills/d2h-hypium-forge/scripts/probe.py +197 -197
- package/skills/d2h-hypium-forge/scripts/run_direct.py +823 -823
- package/skills/d2h-hypium-forge/scripts/run_parallel.py +121 -121
- package/skills/d2h-hypium-forge/scripts/scan_features.py +255 -255
- package/skills/d2h-hypium-forge/scripts/upgrade_asserts.py +66 -66
- package/skills/d2h-hypium-forge/scripts/validate_scenarios.py +84 -84
- package/skills/d2h-page-develop/SKILL.md +112 -112
- package/skills/d2h-page-develop/agents/openai.yaml +7 -7
- package/skills/d2h-page-spec-generate/SKILL.md +206 -206
- package/skills/d2h-page-spec-generate/scripts/build_batch_report.py +227 -227
- package/skills/d2h-utg-ai-generate/SKILL.md +128 -128
- package/skills/d2h-utg-ai-generate/references/click-paths-schema.json +76 -76
- package/skills/d2h-utg-ai-generate/references/extraction-guide.md +34 -34
- package/skills/d2h-utg-ai-generate/references/facts-contract.md +96 -96
- package/skills/d2h-utg-ai-generate/references/page-schema.json +61 -61
- package/skills/d2h-utg-ai-generate/references/utg-schema.json +89 -89
- package/skills/d2h-utg-ai-generate/scripts/build_page_json.py +314 -314
- package/skills/d2h-utg-ai-generate/scripts/build_utg.py +123 -123
- package/skills/d2h-utg-ai-generate/scripts/capture_from_page_json.ts +280 -280
- package/skills/d2h-utg-ai-generate/scripts/utg_common.py +524 -524
- package/skills/d2h-utg-ai-generate/scripts/utg_html.py +489 -489
- package/skills/d2h-utg-ai-generate/scripts/validate_utg.py +69 -69
- package/skills/d2h-utg-ai-generate/scripts/vendor/D3-LICENSE +13 -13
- package/skills/d2h-utg-ai-generate/scripts/vendor/d3-7.9.0.min.js +2 -2
- package/skills/d2h-utg-ai-generate/tests/test_pipeline.py +369 -369
- package/skills/d2h-utg-static-generate/SKILL.md +73 -73
- package/skills/d2h-utg-static-generate/references/discoveries.md +38 -38
- package/skills/d2h-utg-static-generate/scripts/generate.py +499 -499
- package/skills/d2h-utg-static-generate/tests/test_static.py +335 -335
- package/skills/d2h-utg-static-generate/tests/verify-offline-html.py +60 -60
- package/skills/d2h-utg-static-generate/tests/verify-produced-fixture.py +131 -131
- package/skills/harmonyos-migration-playbook/SKILL.md +83 -83
- package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -7
- package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -24
- package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -53
- package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -45
- package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -23
- package/skills/harmonyos-migration-playbook/references/ui-automation.md +53 -53
- package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -38
- package/skills/maintaining-migration-report/SKILL.md +144 -144
- package/skills/maintaining-migration-report/references/final-acceptance.md +48 -48
- package/skills/maintaining-migration-report/references/source-provenance.md +66 -66
- package/skills/managing-d2h-toolchain/SKILL.md +24 -24
- package/skills/managing-d2h-toolchain/environment-requirements.json +16 -16
- package/skills/managing-d2h-toolchain/scripts/manage-toolchain.mjs +98 -98
- package/skills/managing-d2h-toolchain/toolchain.json +16 -16
- package/skills/native-library-substitution/SKILL.md +384 -384
- package/skills/native-library-substitution/references/native-library-substitution.json +56905 -56905
- package/skills/native-library-substitution/references/native-library-substitution.md +163 -163
- package/skills/preparing-migration-workspace/SKILL.md +131 -131
- package/skills/reviewing-migration-process/SKILL.md +62 -62
|
@@ -1,302 +1,302 @@
|
|
|
1
|
-
# 代码仓验证示例(V2)
|
|
2
|
-
|
|
3
|
-
> 基于代码仓实际实现的完整代码示例。**本项目锁 V2**:所有 struct 用 `@ComponentV2`,状态用 `@Local / @Param / @ObservedV2`。i18n API(`resourceManager.getStringByNameSync` / `$r()` 等)与装饰器版本无关。
|
|
4
|
-
>
|
|
5
|
-
> V1 老项目兼容写法见 [`v1-compat.md`](./v1-compat.md)。
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## ✅ 已验证:获取字符串资源
|
|
10
|
-
|
|
11
|
-
基于 `entry/src/main/ets/components/dialogs/ColumnsDialog.ets:9-22`
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
import { common } from '@kit.AbilityKit'
|
|
15
|
-
import { hilog } from '@kit.PerformanceAnalysisKit'
|
|
16
|
-
|
|
17
|
-
const DOMAIN = 0xFF00
|
|
18
|
-
const TAG = 'ColumnsDialog'
|
|
19
|
-
|
|
20
|
-
@CustomDialog
|
|
21
|
-
export struct ColumnsDialog {
|
|
22
|
-
dialogController: CustomDialogController
|
|
23
|
-
|
|
24
|
-
aboutToAppear(): void {
|
|
25
|
-
try {
|
|
26
|
-
const context = getContext(this) as common.UIAbilityContext
|
|
27
|
-
const resourceManager = context.resourceManager
|
|
28
|
-
|
|
29
|
-
// ✅ 已验证:同步获取字符串资源
|
|
30
|
-
const str = resourceManager.getStringByNameSync('dialog_column')
|
|
31
|
-
hilog.info(DOMAIN, TAG, `Loaded string: ${str}`)
|
|
32
|
-
} catch (err) {
|
|
33
|
-
const error = err as Error
|
|
34
|
-
hilog.error(DOMAIN, TAG, `Failed to load string: ${error.message}`)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**关键点**:
|
|
41
|
-
1. ✅ 使用 `getContext(this)` 获取 context
|
|
42
|
-
2. ✅ 使用 `resourceManager.getStringByNameSync()` 同步获取字符串
|
|
43
|
-
3. ✅ 使用 try-catch 处理错误
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## ✅ 已验证:语言环境检测
|
|
48
|
-
|
|
49
|
-
> 基于代码仓 `components/dialogs/ColumnsDialog.ets:7-16` 的实际实现
|
|
50
|
-
|
|
51
|
-
**适用场景**:
|
|
52
|
-
- 需要根据语言执行不同逻辑(如复数处理)
|
|
53
|
-
- 不依赖未知 API 的可靠方案
|
|
54
|
-
- 判断当前加载的是中文还是英文资源
|
|
55
|
-
|
|
56
|
-
**完整实现**:
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
@ComponentV2
|
|
60
|
-
export struct ColumnsDialog {
|
|
61
|
-
/**
|
|
62
|
-
* 检测当前语言环境是否为中文
|
|
63
|
-
* @returns true 表示中文环境,false 表示非中文环境
|
|
64
|
-
*/
|
|
65
|
-
private isChineseLocale(): boolean {
|
|
66
|
-
const context = getContext(this)
|
|
67
|
-
const resourceManager = context.resourceManager
|
|
68
|
-
try {
|
|
69
|
-
// 获取任意包含中文的字符串
|
|
70
|
-
const str = resourceManager.getStringByNameSync('dialog_column')
|
|
71
|
-
// 检测中文字符(Unicode 范围:0x4e00 - 0x9fa5)
|
|
72
|
-
return /[\u4e00-\u9fa5]/.test(str)
|
|
73
|
-
} catch {
|
|
74
|
-
return false // 默认非中文环境
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**原理解释**:
|
|
81
|
-
- `[\u4e00-\u9fa5]` 匹配中文常用汉字的 Unicode 范围
|
|
82
|
-
- 如果字符串包含中文字符,说明加载了中文资源
|
|
83
|
-
- 无需依赖未知 API,可靠性高
|
|
84
|
-
|
|
85
|
-
**使用示例**:
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
if (this.isChineseLocale()) {
|
|
89
|
-
// 中文环境:无需复数处理
|
|
90
|
-
label = `${count} 列`
|
|
91
|
-
} else {
|
|
92
|
-
// 英文环境:需要复数处理
|
|
93
|
-
label = count === 1 ? '1 column' : `${count} columns`
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**注意事项**:
|
|
98
|
-
- ✅ 选择有明显中文特征的字符串(如 'dialog_column'、'app_name')
|
|
99
|
-
- ✅ 提供 try-catch 错误处理
|
|
100
|
-
- ✅ 提供默认返回值(false)
|
|
101
|
-
- ⚠️ 字符串需要包含中文才能准确判断
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## ✅ 已验证:复数处理完整实现
|
|
106
|
-
|
|
107
|
-
> 基于代码仓 `components/dialogs/ColumnsDialog.ets:18-31` 的实际实现
|
|
108
|
-
|
|
109
|
-
**适用场景**:
|
|
110
|
-
- 需要显示带数字的文本(如 "3 列"、"3 columns")
|
|
111
|
-
- 英文环境需要复数处理
|
|
112
|
-
- 需要动态替换占位符
|
|
113
|
-
|
|
114
|
-
**完整代码示例**:
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
@ComponentV2
|
|
118
|
-
export struct ColumnsDialog {
|
|
119
|
-
@Param @Once currentColumns: number = 3
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* 获取列数标签(带复数处理)
|
|
123
|
-
* @param num 列数
|
|
124
|
-
* @returns 格式化后的字符串
|
|
125
|
-
*/
|
|
126
|
-
private getColumnLabel(num: number): string {
|
|
127
|
-
const context = getContext(this)
|
|
128
|
-
const resourceManager = context.resourceManager
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
// 1. 获取字符串资源(包含占位符 %d)
|
|
132
|
-
let str = resourceManager.getStringByNameSync('dialog_column')
|
|
133
|
-
// str = "%d 列" (中文) 或 "%d column" (英文)
|
|
134
|
-
|
|
135
|
-
// 2. 替换占位符
|
|
136
|
-
str = str.replace('%d', num.toString())
|
|
137
|
-
|
|
138
|
-
// 3. 英文复数处理(大于1时添加 's')
|
|
139
|
-
if (num > 1 && !this.isChineseLocale() && !str.endsWith('s')) {
|
|
140
|
-
str = str + 's' // "2 column" → "2 columns"
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return str
|
|
144
|
-
} catch {
|
|
145
|
-
// 4. 兜底逻辑:确保始终有返回值
|
|
146
|
-
return num === 1 ? '1 column' : `${num} columns`
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
**处理流程**:
|
|
153
|
-
1. **获取字符串资源**:从 string.json 加载(含占位符 `%d`)
|
|
154
|
-
2. **替换占位符**:`str.replace('%d', value)` 替换为实际数字
|
|
155
|
-
3. **判断复数**:`num > 1 && !isChineseLocale() && !str.endsWith('s')`
|
|
156
|
-
4. **添加复数标记**:英文环境且 num > 1 时添加 's'
|
|
157
|
-
5. **兜底逻辑**:确保异常时也有返回值
|
|
158
|
-
|
|
159
|
-
**关键点**:
|
|
160
|
-
- ✅ 占位符替换:`str.replace('%d', value.toString())`
|
|
161
|
-
- ✅ 复数判断:`num > 1 && !isChineseLocale() && !str.endsWith('s')`
|
|
162
|
-
- ✅ 兜底逻辑:`catch { return num === 1 ? '1 column' : `${num} columns` }`
|
|
163
|
-
|
|
164
|
-
**支持的占位符**:
|
|
165
|
-
|
|
166
|
-
| 占位符 | 类型 | 示例 string.json | 替换结果 |
|
|
167
|
-
|--------|------|-----------------|----------|
|
|
168
|
-
| `%d` | 整数 | `"%d 列"` | `"3 列"` |
|
|
169
|
-
| `%s` | 字符串 | `"文件:%s"` | `"文件:photo.jpg"` |
|
|
170
|
-
| `%f` | 浮点数 | `"大小:%f MB"` | `"大小:1.5 MB"` |
|
|
171
|
-
|
|
172
|
-
**注意事项**:
|
|
173
|
-
- ⚠️ 仅支持规则复数(+s),不规则复数需特殊处理(如 child → children)
|
|
174
|
-
- ⚠️ 中文字符串无需复数处理
|
|
175
|
-
- ✅ 始终提供兜底逻辑,避免崩溃
|
|
176
|
-
- ✅ 使用 `toString()` 转换数字为字符串
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## ✅ 已验证:完整业务场景示例(ColumnsDialog)
|
|
181
|
-
|
|
182
|
-
> 基于代码仓 `components/dialogs/ColumnsDialog.ets` 的完整实现
|
|
183
|
-
|
|
184
|
-
**业务场景**:让用户选择网格列数(1-20),需要:
|
|
185
|
-
- 显示格式化文本("3 列" 或 "3 columns")
|
|
186
|
-
- 根据语言环境处理复数
|
|
187
|
-
- 提供友好的用户界面
|
|
188
|
-
|
|
189
|
-
**完整代码**:
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
@ComponentV2
|
|
193
|
-
export struct ColumnsDialog {
|
|
194
|
-
@Param @Once currentColumns: number = 3
|
|
195
|
-
@Event onColumnsChange: (columns: number) => void = () => {}
|
|
196
|
-
@Event onCancel: () => void = () => {}
|
|
197
|
-
|
|
198
|
-
// ✅ 技术点1:语言环境检测
|
|
199
|
-
private isChineseLocale(): boolean {
|
|
200
|
-
const context = getContext(this)
|
|
201
|
-
const resourceManager = context.resourceManager
|
|
202
|
-
try {
|
|
203
|
-
const str = resourceManager.getStringByNameSync('dialog_column')
|
|
204
|
-
return /[\u4e00-\u9fa5]/.test(str)
|
|
205
|
-
} catch {
|
|
206
|
-
return false
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// ✅ 技术点2:复数处理 + 占位符替换
|
|
211
|
-
private getColumnLabel(num: number): string {
|
|
212
|
-
const context = getContext(this)
|
|
213
|
-
const resourceManager = context.resourceManager
|
|
214
|
-
try {
|
|
215
|
-
let str = resourceManager.getStringByNameSync('dialog_column')
|
|
216
|
-
str = str.replace('%d', num.toString())
|
|
217
|
-
|
|
218
|
-
// 英文复数处理
|
|
219
|
-
if (num > 1 && !this.isChineseLocale() && !str.endsWith('s')) {
|
|
220
|
-
str = str + 's'
|
|
221
|
-
}
|
|
222
|
-
return str
|
|
223
|
-
} catch {
|
|
224
|
-
// 兜底逻辑
|
|
225
|
-
return num === 1 ? '1 column' : `${num} columns`
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
@Builder
|
|
230
|
-
columnOption(num: number) {
|
|
231
|
-
Row() {
|
|
232
|
-
Text(this.currentColumns === num ? '✓ ' : ' ')
|
|
233
|
-
.fontSize(14)
|
|
234
|
-
.fontColor('#007DFF')
|
|
235
|
-
.width(24)
|
|
236
|
-
Text(this.getColumnLabel(num))
|
|
237
|
-
.fontSize(14)
|
|
238
|
-
.fontColor(this.currentColumns === num ? '#007DFF' : '#333333')
|
|
239
|
-
}
|
|
240
|
-
.width('100%')
|
|
241
|
-
.padding({ left: 12, right: 12, top: 10, bottom: 10 })
|
|
242
|
-
.backgroundColor(this.currentColumns === num ? '#E6F0FF' : '#FFFFFF')
|
|
243
|
-
.borderRadius(8)
|
|
244
|
-
.margin({ bottom: 4 })
|
|
245
|
-
.onClick(() => {
|
|
246
|
-
this.onColumnsChange(num)
|
|
247
|
-
})
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
build() {
|
|
251
|
-
Column() {
|
|
252
|
-
// ✅ 技术点3:静态资源引用
|
|
253
|
-
Text($r('app.string.dialog_columns_title'))
|
|
254
|
-
.fontSize(18)
|
|
255
|
-
.fontWeight(FontWeight.Bold)
|
|
256
|
-
.margin({ top: 20, bottom: 16 })
|
|
257
|
-
|
|
258
|
-
Scroll() {
|
|
259
|
-
Column() {
|
|
260
|
-
// 动态生成 1-20 的列选项
|
|
261
|
-
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
|
262
|
-
(num: number) => {
|
|
263
|
-
this.columnOption(num)
|
|
264
|
-
}
|
|
265
|
-
)
|
|
266
|
-
}
|
|
267
|
-
.width('100%')
|
|
268
|
-
}
|
|
269
|
-
.constraintSize({ maxHeight: 360 })
|
|
270
|
-
.scrollBar(BarState.Auto)
|
|
271
|
-
|
|
272
|
-
Button($r('app.string.dialog_cancel'))
|
|
273
|
-
.width('100%')
|
|
274
|
-
.height(40)
|
|
275
|
-
.backgroundColor('#F0F0F0')
|
|
276
|
-
.fontColor('#333333')
|
|
277
|
-
.margin({ top: 16 })
|
|
278
|
-
.onClick(() => this.onCancel())
|
|
279
|
-
}
|
|
280
|
-
.width(280)
|
|
281
|
-
.padding(20)
|
|
282
|
-
.backgroundColor('#FFFFFF')
|
|
283
|
-
.borderRadius(16)
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
**技术要点总结**:
|
|
289
|
-
|
|
290
|
-
| 技术点 | 代码位置 | 说明 |
|
|
291
|
-
|--------|---------|------|
|
|
292
|
-
| 语言环境检测 | `isChineseLocale():7-16` | 通过中文字符判断语言 |
|
|
293
|
-
| 复数处理 | `getColumnLabel():18-31` | 占位符替换 + 复数规则 |
|
|
294
|
-
| 占位符替换 | `getColumnLabel():23` | `str.replace('%d', value)` |
|
|
295
|
-
| 静态引用 | `build():56` | `$r('app.string.xxx')` |
|
|
296
|
-
| 动态获取 | `getColumnLabel():22` | `resourceManager.getStringByNameSync()` |
|
|
297
|
-
| 错误处理 | `getColumnLabel():28-30` | try-catch + 兜底逻辑 |
|
|
298
|
-
|
|
299
|
-
**文件位置**:
|
|
300
|
-
- 实现代码:`entry/src/main/ets/components/dialogs/ColumnsDialog.ets`
|
|
301
|
-
- 中文资源:`entry/src/main/resources/base/element/string.json`
|
|
302
|
-
- 英文资源:`entry/src/main/resources/en_US/element/string.json`
|
|
1
|
+
# 代码仓验证示例(V2)
|
|
2
|
+
|
|
3
|
+
> 基于代码仓实际实现的完整代码示例。**本项目锁 V2**:所有 struct 用 `@ComponentV2`,状态用 `@Local / @Param / @ObservedV2`。i18n API(`resourceManager.getStringByNameSync` / `$r()` 等)与装饰器版本无关。
|
|
4
|
+
>
|
|
5
|
+
> V1 老项目兼容写法见 [`v1-compat.md`](./v1-compat.md)。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✅ 已验证:获取字符串资源
|
|
10
|
+
|
|
11
|
+
基于 `entry/src/main/ets/components/dialogs/ColumnsDialog.ets:9-22`
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { common } from '@kit.AbilityKit'
|
|
15
|
+
import { hilog } from '@kit.PerformanceAnalysisKit'
|
|
16
|
+
|
|
17
|
+
const DOMAIN = 0xFF00
|
|
18
|
+
const TAG = 'ColumnsDialog'
|
|
19
|
+
|
|
20
|
+
@CustomDialog
|
|
21
|
+
export struct ColumnsDialog {
|
|
22
|
+
dialogController: CustomDialogController
|
|
23
|
+
|
|
24
|
+
aboutToAppear(): void {
|
|
25
|
+
try {
|
|
26
|
+
const context = getContext(this) as common.UIAbilityContext
|
|
27
|
+
const resourceManager = context.resourceManager
|
|
28
|
+
|
|
29
|
+
// ✅ 已验证:同步获取字符串资源
|
|
30
|
+
const str = resourceManager.getStringByNameSync('dialog_column')
|
|
31
|
+
hilog.info(DOMAIN, TAG, `Loaded string: ${str}`)
|
|
32
|
+
} catch (err) {
|
|
33
|
+
const error = err as Error
|
|
34
|
+
hilog.error(DOMAIN, TAG, `Failed to load string: ${error.message}`)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**关键点**:
|
|
41
|
+
1. ✅ 使用 `getContext(this)` 获取 context
|
|
42
|
+
2. ✅ 使用 `resourceManager.getStringByNameSync()` 同步获取字符串
|
|
43
|
+
3. ✅ 使用 try-catch 处理错误
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## ✅ 已验证:语言环境检测
|
|
48
|
+
|
|
49
|
+
> 基于代码仓 `components/dialogs/ColumnsDialog.ets:7-16` 的实际实现
|
|
50
|
+
|
|
51
|
+
**适用场景**:
|
|
52
|
+
- 需要根据语言执行不同逻辑(如复数处理)
|
|
53
|
+
- 不依赖未知 API 的可靠方案
|
|
54
|
+
- 判断当前加载的是中文还是英文资源
|
|
55
|
+
|
|
56
|
+
**完整实现**:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
@ComponentV2
|
|
60
|
+
export struct ColumnsDialog {
|
|
61
|
+
/**
|
|
62
|
+
* 检测当前语言环境是否为中文
|
|
63
|
+
* @returns true 表示中文环境,false 表示非中文环境
|
|
64
|
+
*/
|
|
65
|
+
private isChineseLocale(): boolean {
|
|
66
|
+
const context = getContext(this)
|
|
67
|
+
const resourceManager = context.resourceManager
|
|
68
|
+
try {
|
|
69
|
+
// 获取任意包含中文的字符串
|
|
70
|
+
const str = resourceManager.getStringByNameSync('dialog_column')
|
|
71
|
+
// 检测中文字符(Unicode 范围:0x4e00 - 0x9fa5)
|
|
72
|
+
return /[\u4e00-\u9fa5]/.test(str)
|
|
73
|
+
} catch {
|
|
74
|
+
return false // 默认非中文环境
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**原理解释**:
|
|
81
|
+
- `[\u4e00-\u9fa5]` 匹配中文常用汉字的 Unicode 范围
|
|
82
|
+
- 如果字符串包含中文字符,说明加载了中文资源
|
|
83
|
+
- 无需依赖未知 API,可靠性高
|
|
84
|
+
|
|
85
|
+
**使用示例**:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
if (this.isChineseLocale()) {
|
|
89
|
+
// 中文环境:无需复数处理
|
|
90
|
+
label = `${count} 列`
|
|
91
|
+
} else {
|
|
92
|
+
// 英文环境:需要复数处理
|
|
93
|
+
label = count === 1 ? '1 column' : `${count} columns`
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**注意事项**:
|
|
98
|
+
- ✅ 选择有明显中文特征的字符串(如 'dialog_column'、'app_name')
|
|
99
|
+
- ✅ 提供 try-catch 错误处理
|
|
100
|
+
- ✅ 提供默认返回值(false)
|
|
101
|
+
- ⚠️ 字符串需要包含中文才能准确判断
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## ✅ 已验证:复数处理完整实现
|
|
106
|
+
|
|
107
|
+
> 基于代码仓 `components/dialogs/ColumnsDialog.ets:18-31` 的实际实现
|
|
108
|
+
|
|
109
|
+
**适用场景**:
|
|
110
|
+
- 需要显示带数字的文本(如 "3 列"、"3 columns")
|
|
111
|
+
- 英文环境需要复数处理
|
|
112
|
+
- 需要动态替换占位符
|
|
113
|
+
|
|
114
|
+
**完整代码示例**:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
@ComponentV2
|
|
118
|
+
export struct ColumnsDialog {
|
|
119
|
+
@Param @Once currentColumns: number = 3
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 获取列数标签(带复数处理)
|
|
123
|
+
* @param num 列数
|
|
124
|
+
* @returns 格式化后的字符串
|
|
125
|
+
*/
|
|
126
|
+
private getColumnLabel(num: number): string {
|
|
127
|
+
const context = getContext(this)
|
|
128
|
+
const resourceManager = context.resourceManager
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
// 1. 获取字符串资源(包含占位符 %d)
|
|
132
|
+
let str = resourceManager.getStringByNameSync('dialog_column')
|
|
133
|
+
// str = "%d 列" (中文) 或 "%d column" (英文)
|
|
134
|
+
|
|
135
|
+
// 2. 替换占位符
|
|
136
|
+
str = str.replace('%d', num.toString())
|
|
137
|
+
|
|
138
|
+
// 3. 英文复数处理(大于1时添加 's')
|
|
139
|
+
if (num > 1 && !this.isChineseLocale() && !str.endsWith('s')) {
|
|
140
|
+
str = str + 's' // "2 column" → "2 columns"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return str
|
|
144
|
+
} catch {
|
|
145
|
+
// 4. 兜底逻辑:确保始终有返回值
|
|
146
|
+
return num === 1 ? '1 column' : `${num} columns`
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**处理流程**:
|
|
153
|
+
1. **获取字符串资源**:从 string.json 加载(含占位符 `%d`)
|
|
154
|
+
2. **替换占位符**:`str.replace('%d', value)` 替换为实际数字
|
|
155
|
+
3. **判断复数**:`num > 1 && !isChineseLocale() && !str.endsWith('s')`
|
|
156
|
+
4. **添加复数标记**:英文环境且 num > 1 时添加 's'
|
|
157
|
+
5. **兜底逻辑**:确保异常时也有返回值
|
|
158
|
+
|
|
159
|
+
**关键点**:
|
|
160
|
+
- ✅ 占位符替换:`str.replace('%d', value.toString())`
|
|
161
|
+
- ✅ 复数判断:`num > 1 && !isChineseLocale() && !str.endsWith('s')`
|
|
162
|
+
- ✅ 兜底逻辑:`catch { return num === 1 ? '1 column' : `${num} columns` }`
|
|
163
|
+
|
|
164
|
+
**支持的占位符**:
|
|
165
|
+
|
|
166
|
+
| 占位符 | 类型 | 示例 string.json | 替换结果 |
|
|
167
|
+
|--------|------|-----------------|----------|
|
|
168
|
+
| `%d` | 整数 | `"%d 列"` | `"3 列"` |
|
|
169
|
+
| `%s` | 字符串 | `"文件:%s"` | `"文件:photo.jpg"` |
|
|
170
|
+
| `%f` | 浮点数 | `"大小:%f MB"` | `"大小:1.5 MB"` |
|
|
171
|
+
|
|
172
|
+
**注意事项**:
|
|
173
|
+
- ⚠️ 仅支持规则复数(+s),不规则复数需特殊处理(如 child → children)
|
|
174
|
+
- ⚠️ 中文字符串无需复数处理
|
|
175
|
+
- ✅ 始终提供兜底逻辑,避免崩溃
|
|
176
|
+
- ✅ 使用 `toString()` 转换数字为字符串
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## ✅ 已验证:完整业务场景示例(ColumnsDialog)
|
|
181
|
+
|
|
182
|
+
> 基于代码仓 `components/dialogs/ColumnsDialog.ets` 的完整实现
|
|
183
|
+
|
|
184
|
+
**业务场景**:让用户选择网格列数(1-20),需要:
|
|
185
|
+
- 显示格式化文本("3 列" 或 "3 columns")
|
|
186
|
+
- 根据语言环境处理复数
|
|
187
|
+
- 提供友好的用户界面
|
|
188
|
+
|
|
189
|
+
**完整代码**:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
@ComponentV2
|
|
193
|
+
export struct ColumnsDialog {
|
|
194
|
+
@Param @Once currentColumns: number = 3
|
|
195
|
+
@Event onColumnsChange: (columns: number) => void = () => {}
|
|
196
|
+
@Event onCancel: () => void = () => {}
|
|
197
|
+
|
|
198
|
+
// ✅ 技术点1:语言环境检测
|
|
199
|
+
private isChineseLocale(): boolean {
|
|
200
|
+
const context = getContext(this)
|
|
201
|
+
const resourceManager = context.resourceManager
|
|
202
|
+
try {
|
|
203
|
+
const str = resourceManager.getStringByNameSync('dialog_column')
|
|
204
|
+
return /[\u4e00-\u9fa5]/.test(str)
|
|
205
|
+
} catch {
|
|
206
|
+
return false
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ✅ 技术点2:复数处理 + 占位符替换
|
|
211
|
+
private getColumnLabel(num: number): string {
|
|
212
|
+
const context = getContext(this)
|
|
213
|
+
const resourceManager = context.resourceManager
|
|
214
|
+
try {
|
|
215
|
+
let str = resourceManager.getStringByNameSync('dialog_column')
|
|
216
|
+
str = str.replace('%d', num.toString())
|
|
217
|
+
|
|
218
|
+
// 英文复数处理
|
|
219
|
+
if (num > 1 && !this.isChineseLocale() && !str.endsWith('s')) {
|
|
220
|
+
str = str + 's'
|
|
221
|
+
}
|
|
222
|
+
return str
|
|
223
|
+
} catch {
|
|
224
|
+
// 兜底逻辑
|
|
225
|
+
return num === 1 ? '1 column' : `${num} columns`
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@Builder
|
|
230
|
+
columnOption(num: number) {
|
|
231
|
+
Row() {
|
|
232
|
+
Text(this.currentColumns === num ? '✓ ' : ' ')
|
|
233
|
+
.fontSize(14)
|
|
234
|
+
.fontColor('#007DFF')
|
|
235
|
+
.width(24)
|
|
236
|
+
Text(this.getColumnLabel(num))
|
|
237
|
+
.fontSize(14)
|
|
238
|
+
.fontColor(this.currentColumns === num ? '#007DFF' : '#333333')
|
|
239
|
+
}
|
|
240
|
+
.width('100%')
|
|
241
|
+
.padding({ left: 12, right: 12, top: 10, bottom: 10 })
|
|
242
|
+
.backgroundColor(this.currentColumns === num ? '#E6F0FF' : '#FFFFFF')
|
|
243
|
+
.borderRadius(8)
|
|
244
|
+
.margin({ bottom: 4 })
|
|
245
|
+
.onClick(() => {
|
|
246
|
+
this.onColumnsChange(num)
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
build() {
|
|
251
|
+
Column() {
|
|
252
|
+
// ✅ 技术点3:静态资源引用
|
|
253
|
+
Text($r('app.string.dialog_columns_title'))
|
|
254
|
+
.fontSize(18)
|
|
255
|
+
.fontWeight(FontWeight.Bold)
|
|
256
|
+
.margin({ top: 20, bottom: 16 })
|
|
257
|
+
|
|
258
|
+
Scroll() {
|
|
259
|
+
Column() {
|
|
260
|
+
// 动态生成 1-20 的列选项
|
|
261
|
+
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
|
262
|
+
(num: number) => {
|
|
263
|
+
this.columnOption(num)
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
.width('100%')
|
|
268
|
+
}
|
|
269
|
+
.constraintSize({ maxHeight: 360 })
|
|
270
|
+
.scrollBar(BarState.Auto)
|
|
271
|
+
|
|
272
|
+
Button($r('app.string.dialog_cancel'))
|
|
273
|
+
.width('100%')
|
|
274
|
+
.height(40)
|
|
275
|
+
.backgroundColor('#F0F0F0')
|
|
276
|
+
.fontColor('#333333')
|
|
277
|
+
.margin({ top: 16 })
|
|
278
|
+
.onClick(() => this.onCancel())
|
|
279
|
+
}
|
|
280
|
+
.width(280)
|
|
281
|
+
.padding(20)
|
|
282
|
+
.backgroundColor('#FFFFFF')
|
|
283
|
+
.borderRadius(16)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**技术要点总结**:
|
|
289
|
+
|
|
290
|
+
| 技术点 | 代码位置 | 说明 |
|
|
291
|
+
|--------|---------|------|
|
|
292
|
+
| 语言环境检测 | `isChineseLocale():7-16` | 通过中文字符判断语言 |
|
|
293
|
+
| 复数处理 | `getColumnLabel():18-31` | 占位符替换 + 复数规则 |
|
|
294
|
+
| 占位符替换 | `getColumnLabel():23` | `str.replace('%d', value)` |
|
|
295
|
+
| 静态引用 | `build():56` | `$r('app.string.xxx')` |
|
|
296
|
+
| 动态获取 | `getColumnLabel():22` | `resourceManager.getStringByNameSync()` |
|
|
297
|
+
| 错误处理 | `getColumnLabel():28-30` | try-catch + 兜底逻辑 |
|
|
298
|
+
|
|
299
|
+
**文件位置**:
|
|
300
|
+
- 实现代码:`entry/src/main/ets/components/dialogs/ColumnsDialog.ets`
|
|
301
|
+
- 中文资源:`entry/src/main/resources/base/element/string.json`
|
|
302
|
+
- 英文资源:`entry/src/main/resources/en_US/element/string.json`
|