@harmonyos-arkts/d2h 0.0.0-stage → 0.1.0
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 +13 -0
- package/.claude-plugin/plugin.json +7 -0
- package/README.md +168 -2
- package/agents/android-to-hmos-00-orchestrator.md +312 -0
- package/agents/d2h.md +168 -0
- package/bin/install-opencode.mjs +51 -0
- package/install-opencode.sh +160 -0
- package/opencode/agents.json +16 -0
- package/package.json +16 -4
- package/schemas/android-source-manifest.schema.json +25 -0
- package/schemas/checkpoint-provenance.schema.json +67 -0
- package/schemas/final-acceptance.schema.json +29 -0
- package/schemas/managed-evidence-index.schema.json +15 -0
- package/schemas/managed-evidence.schema.json +114 -0
- package/schemas/migration-config.schema.json +51 -0
- package/schemas/migration-report-index.schema.json +39 -0
- package/schemas/migration-report-item.schema.json +51 -0
- package/schemas/migration-report-summary.schema.json +25 -0
- package/schemas/migration-status.schema.json +202 -0
- package/schemas/preflight.schema.json +60 -0
- package/schemas/source-order-audit.schema.json +16 -0
- package/schemas/source-provenance-event.schema.json +19 -0
- package/schemas/spec-app-shard.schema.json +45 -0
- package/schemas/spec-fact-corrections-shard.schema.json +13 -0
- package/schemas/spec-features-shard.schema.json +43 -0
- package/schemas/spec-index.schema.json +26 -0
- package/schemas/spec-interactions-shard.schema.json +40 -0
- package/schemas/spec-page.schema.json +98 -0
- package/schemas/spec-pages-index.schema.json +1 -0
- package/schemas/spec-unresolved-shard.schema.json +1 -0
- package/schemas/task-envelope.schema.json +55 -0
- package/schemas/task-plan.schema.json +123 -0
- package/skills/android2hmos_resources_convert/SKILL.md +162 -0
- package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -0
- package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -0
- package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -0
- package/skills/appgraph-rule-audit/SKILL.md +58 -0
- package/skills/appgraph-rule-audit/references/audit-contract.md +47 -0
- package/skills/appgraph-rule-audit/references/recommendation-schema.md +41 -0
- package/skills/appgraph-rule-audit/schemas/rule-opportunities.schema.json +125 -0
- package/skills/arkts-app-identity/SKILL.md +238 -0
- package/skills/arkts-i18n/SKILL.md +496 -0
- package/skills/arkts-i18n/evals/evals.json +84 -0
- package/skills/arkts-i18n/references/code-examples.md +302 -0
- package/skills/arkts-i18n/references/common-pitfalls.md +391 -0
- package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -0
- package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -0
- package/skills/arkts-i18n/references/language-codes.md +104 -0
- package/skills/arkts-i18n/references/resource-file-structure.md +775 -0
- package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -0
- package/skills/arkts-i18n/references/v1-compat.md +244 -0
- package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -0
- package/skills/arkts-icon-sizing/SKILL.md +211 -0
- package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -0
- package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -0
- package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -0
- package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -0
- package/skills/arkts-mvvm-architecture/SKILL.md +613 -0
- package/skills/harmonyos-migration-playbook/SKILL.md +56 -0
- package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -0
- package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -0
- package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -0
- package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -0
- package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -0
- package/skills/harmonyos-migration-playbook/references/ui-automation.md +52 -0
- package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -0
- package/skills/maintaining-migration-report/SKILL.md +155 -0
- package/skills/native-library-substitution/SKILL.md +385 -0
- package/skills/native-library-substitution/references/native-library-substitution.json +56906 -0
- package/skills/native-library-substitution/references/native-library-substitution.md +163 -0
- package/skills/preparing-migration-workspace/SKILL.md +124 -0
- package/skills/preparing-migration-workspace/toolchain.json +43 -0
- package/skills/reviewing-migration-process/SKILL.md +62 -0
- package/src/install-opencode.mjs +108 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# 硬编码字符串扫描与迁移工具
|
|
2
|
+
|
|
3
|
+
> 辅助扫描项目中硬编码字符串并迁移到 resources 的工具和脚本
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 扫描工具汇总
|
|
8
|
+
|
|
9
|
+
### 1. rg(ripgrep)快速扫描
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 安装:Windows 使用 winget install Andersonby.rg 或 choco install ripgrep
|
|
13
|
+
|
|
14
|
+
# 扫描 Text() 硬编码
|
|
15
|
+
rg "Text\(['\"]" --type ets -n -o
|
|
16
|
+
|
|
17
|
+
# 扫描 Button() 硬编码
|
|
18
|
+
rg "Button\(['\"]" --type ets -n -o
|
|
19
|
+
|
|
20
|
+
# 扫描 showToast() 硬编码
|
|
21
|
+
rg "showToast\(['\"]" --type ets -n -o
|
|
22
|
+
|
|
23
|
+
# 扫描 AlertDialog 硬编码
|
|
24
|
+
rg "AlertDialog" --type ets -n -o
|
|
25
|
+
|
|
26
|
+
# 扫描 promptAction 硬编码
|
|
27
|
+
rg "promptAction\." --type ets -n
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. PowerShell 扫描脚本
|
|
31
|
+
|
|
32
|
+
```powershell
|
|
33
|
+
# 扫描所有 .ets 文件中的硬编码字符串
|
|
34
|
+
Get-ChildItem -Recurse -Filter "*.ets" | ForEach-Object {
|
|
35
|
+
$content = Get-Content $_.FullName -Raw
|
|
36
|
+
# 匹配单引号字符串
|
|
37
|
+
if ($content -match "Text\(['\']([^'\)]+)['\']") {
|
|
38
|
+
Write-Host "$($_.FullName): $($matches[1])"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 3. Node.js 扫描脚本
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// scan-hardcoded-strings.js
|
|
47
|
+
const fs = require('fs');
|
|
48
|
+
const path = require('path');
|
|
49
|
+
|
|
50
|
+
const patterns = [
|
|
51
|
+
/Text\(['"]([^'"]+)['"]\)/g,
|
|
52
|
+
/Button\(['"]([^'"]+)['"]\)/g,
|
|
53
|
+
/\.title\(['"]([^'"]+)['"]\)/g,
|
|
54
|
+
/\.placeholder\(['"]([^'"]+)['"]\)/g,
|
|
55
|
+
/\.promptText\(['"]([^'"]+)['"]\)/g,
|
|
56
|
+
/showToast\(['"]([^'"]+)['"]\)/g,
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
function scanFile(filePath) {
|
|
60
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
61
|
+
const results = [];
|
|
62
|
+
|
|
63
|
+
for (const pattern of patterns) {
|
|
64
|
+
let match;
|
|
65
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
66
|
+
const str = match[1];
|
|
67
|
+
// 过滤
|
|
68
|
+
if (str.length > 1 && /[\u4e00-\u9fa5a-zA-Z]/.test(str)) {
|
|
69
|
+
results.push({
|
|
70
|
+
file: filePath,
|
|
71
|
+
line: content.substring(0, match.index).split('\n').length,
|
|
72
|
+
pattern: match[0],
|
|
73
|
+
value: str
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function scanDirectory(dir) {
|
|
82
|
+
const results = [];
|
|
83
|
+
const files = fs.readdirSync(dir, { withFileTypes: true });
|
|
84
|
+
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
const fullPath = path.join(dir, file.name);
|
|
87
|
+
if (file.isDirectory() && !file.name.startsWith('.')) {
|
|
88
|
+
results.push(...scanDirectory(fullPath));
|
|
89
|
+
} else if (file.name.endsWith('.ets')) {
|
|
90
|
+
results.push(...scanFile(fullPath));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return results;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const results = scanDirectory('./entry/src/main/ets');
|
|
97
|
+
console.log(JSON.stringify(results, null, 2));
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 过滤规则
|
|
103
|
+
|
|
104
|
+
### 必须排除的模式
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
// 排除 $r() 引用
|
|
108
|
+
const filtered = results.filter(r => !r.value.includes('$r'));
|
|
109
|
+
|
|
110
|
+
// 排除纯数字
|
|
111
|
+
const filtered = results.filter(r => !/^\d+$/.test(r.value));
|
|
112
|
+
|
|
113
|
+
// 排除单字符
|
|
114
|
+
const filtered = results.filter(r => r.value.length > 1);
|
|
115
|
+
|
|
116
|
+
// 排除 URL
|
|
117
|
+
const filtered = results.filter(r => !/^https?:\/\//.test(r.value));
|
|
118
|
+
|
|
119
|
+
// 排除技术术语
|
|
120
|
+
const filtered = results.filter(r => !/^<[^>]+>$/.test(r.value)); // HTML 标签
|
|
121
|
+
const filtered = results.filter(r => !/^\{[^}]+\}$/.test(r.value)); // 变量引用
|
|
122
|
+
|
|
123
|
+
// 排除正则表达式
|
|
124
|
+
const filtered = results.filter(r => !/^[\[\]\{\}\(\)\.\?\+\*\^\\]$/.test(r.value));
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## key 生成算法
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
function generateKey(str) {
|
|
133
|
+
// 1. 转小写
|
|
134
|
+
let key = str.toLowerCase();
|
|
135
|
+
|
|
136
|
+
// 2. 移除特殊字符
|
|
137
|
+
key = key.replace(/[^a-z0-9\u4e00-\u9fa5]/g, '_');
|
|
138
|
+
|
|
139
|
+
// 3. 移除连续下划线
|
|
140
|
+
key = key.replace(/_+/g, '_');
|
|
141
|
+
|
|
142
|
+
// 4. 移除首尾下划线
|
|
143
|
+
key = key.replace(/^_|_$/g, '');
|
|
144
|
+
|
|
145
|
+
// 5. 截断过长 key
|
|
146
|
+
if (key.length > 30) {
|
|
147
|
+
key = key.substring(0, 30);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return key || 'unknown';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 示例
|
|
154
|
+
console.log(generateKey('确认')); // confirm
|
|
155
|
+
console.log(generateKey('确定要删除吗?')); // 确定要删除吗
|
|
156
|
+
console.log(generateKey('Search Files')); // search_files
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 迁移报告模板
|
|
162
|
+
|
|
163
|
+
```markdown
|
|
164
|
+
# 硬编码字符串迁移报告
|
|
165
|
+
|
|
166
|
+
## 扫描时间
|
|
167
|
+
2024-xx-xx
|
|
168
|
+
|
|
169
|
+
## 扫描范围
|
|
170
|
+
- entry/src/main/ets/**/*.ets
|
|
171
|
+
|
|
172
|
+
## 发现数量
|
|
173
|
+
- 总计:XX 个硬编码字符串
|
|
174
|
+
- 已迁移:XX 个
|
|
175
|
+
- 待处理:XX 个
|
|
176
|
+
|
|
177
|
+
## 按文件分布
|
|
178
|
+
| 文件 | 数量 |
|
|
179
|
+
|------|------|
|
|
180
|
+
| page1.ets | 10 |
|
|
181
|
+
| page2.ets | 5 |
|
|
182
|
+
| dialog1.ets | 3 |
|
|
183
|
+
|
|
184
|
+
## 待迁移列表
|
|
185
|
+
|
|
186
|
+
| # | 原文本 | 建议 key | 优先级 |
|
|
187
|
+
|---|--------|---------|--------|
|
|
188
|
+
| 1 | 确认 | confirm | P0 |
|
|
189
|
+
| 2 | 取消 | cancel | P0 |
|
|
190
|
+
| 3 | 保存成功 | toast_save_success | P1 |
|
|
191
|
+
|
|
192
|
+
## 已完成迁移
|
|
193
|
+
|
|
194
|
+
| # | 原文本 | key | 文件 |
|
|
195
|
+
|---|--------|-----|------|
|
|
196
|
+
| 1 | 设置 | title_settings | SettingsPage.ets |
|
|
197
|
+
| 2 | 搜索 | hint_search | SearchBar.ets |
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 批量替换脚本
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
// migrate-to-resources.js
|
|
206
|
+
const fs = require('fs');
|
|
207
|
+
const path = require('path');
|
|
208
|
+
|
|
209
|
+
// 迁移映射表(从扫描结果生成)
|
|
210
|
+
const migrationMap = {
|
|
211
|
+
'确认': 'app.string.confirm',
|
|
212
|
+
'取消': 'app.string.cancel',
|
|
213
|
+
'保存': 'app.string.save',
|
|
214
|
+
'删除': 'app.string.delete',
|
|
215
|
+
'设置': 'app.string.title_settings',
|
|
216
|
+
'搜索': 'app.string.hint_search',
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
function migrateFile(filePath) {
|
|
220
|
+
let content = fs.readFileSync(filePath, 'utf-8');
|
|
221
|
+
let modified = false;
|
|
222
|
+
|
|
223
|
+
for (const [oldStr, newRef] of Object.entries(migrationMap)) {
|
|
224
|
+
// 替换 Text('xxx')
|
|
225
|
+
const textPattern = new RegExp(`Text\\(['"]${escapeRegExp(oldStr)}['"]\\)`, 'g');
|
|
226
|
+
if (textPattern.test(content)) {
|
|
227
|
+
content = content.replace(textPattern, `Text(\$r('${newRef}'))`);
|
|
228
|
+
modified = true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 替换 Button('xxx')
|
|
232
|
+
const buttonPattern = new RegExp(`Button\\(['"]${escapeRegExp(oldStr)}['"]\\)`, 'g');
|
|
233
|
+
if (buttonPattern.test(content)) {
|
|
234
|
+
content = content.replace(buttonPattern, `Button(\$r('${newRef}'))`);
|
|
235
|
+
modified = true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (modified) {
|
|
240
|
+
fs.writeFileSync(filePath, content);
|
|
241
|
+
console.log(`Migrated: ${filePath}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function escapeRegExp(string) {
|
|
246
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function migrateDirectory(dir) {
|
|
250
|
+
const files = fs.readdirSync(dir, { withFileTypes: true });
|
|
251
|
+
|
|
252
|
+
for (const file of files) {
|
|
253
|
+
const fullPath = path.join(dir, file.name);
|
|
254
|
+
if (file.isDirectory() && !file.name.startsWith('.')) {
|
|
255
|
+
migrateDirectory(fullPath);
|
|
256
|
+
} else if (file.name.endsWith('.ets')) {
|
|
257
|
+
migrateFile(fullPath);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
migrateDirectory('./entry/src/main/ets');
|
|
263
|
+
console.log('Migration complete!');
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## string.json 更新脚本
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
// update-string-json.js
|
|
272
|
+
const fs = require('fs');
|
|
273
|
+
|
|
274
|
+
// 新增的 key-value 对
|
|
275
|
+
const newEntries = [
|
|
276
|
+
{ name: 'confirm', value: 'Confirm' },
|
|
277
|
+
{ name: 'cancel', value: 'Cancel' },
|
|
278
|
+
{ name: 'save', value: 'Save' },
|
|
279
|
+
{ name: 'delete', value: 'Delete' },
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
function updateStringJson(filePath) {
|
|
283
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
284
|
+
const json = JSON.parse(content);
|
|
285
|
+
|
|
286
|
+
// 检查是否已存在
|
|
287
|
+
const existingNames = json.string.map(s => s.name);
|
|
288
|
+
const toAdd = newEntries.filter(e => !existingNames.includes(e.name));
|
|
289
|
+
|
|
290
|
+
if (toAdd.length > 0) {
|
|
291
|
+
json.string.push(...toAdd);
|
|
292
|
+
fs.writeFileSync(filePath, JSON.stringify(json, null, 2));
|
|
293
|
+
console.log(`Updated: ${filePath} (+${toAdd.length} entries)`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 更新 base 和 en_US
|
|
298
|
+
updateStringJson('./entry/src/main/resources/base/element/string.json');
|
|
299
|
+
updateStringJson('./entry/src/main/resources/en_US/element/string.json');
|
|
300
|
+
|
|
301
|
+
console.log('Done!');
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 使用流程
|
|
307
|
+
|
|
308
|
+
### 完整迁移流程
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
1. 扫描阶段
|
|
312
|
+
├── 运行 scan-hardcoded-strings.js
|
|
313
|
+
├── 生成迁移报告
|
|
314
|
+
└── 人工审核
|
|
315
|
+
|
|
316
|
+
2. 准备阶段
|
|
317
|
+
├── 创建 migrationMap
|
|
318
|
+
└── 验证 key 不冲突
|
|
319
|
+
|
|
320
|
+
3. 迁移阶段
|
|
321
|
+
├── 运行 update-string-json.js(更新资源文件)
|
|
322
|
+
├── 运行 migrate-to-resources.js(替换代码)
|
|
323
|
+
└── 人工检查修改
|
|
324
|
+
|
|
325
|
+
4. 验证阶段
|
|
326
|
+
├── 编译项目
|
|
327
|
+
├── 运行应用检查
|
|
328
|
+
└── 如有问题,回滚修改
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 推荐工具链
|
|
332
|
+
|
|
333
|
+
| 阶段 | 工具 | 说明 |
|
|
334
|
+
|------|------|------|
|
|
335
|
+
| 扫描 | rg / grep | 快速定位 |
|
|
336
|
+
| 分析 | Node.js 脚本 | 生成报告 |
|
|
337
|
+
| 迁移 | VS Code 多文件替换 | 人工确认 |
|
|
338
|
+
| 验证 | DevEco Studio 编译 | 检查错误 |
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## 注意事项
|
|
343
|
+
|
|
344
|
+
1. **先备份**:迁移前先 commit 或备份
|
|
345
|
+
2. **小步快跑**:分批次迁移,每次只处理 10-20 个
|
|
346
|
+
3. **编译验证**:每批次迁移后立即编译检查
|
|
347
|
+
4. **人工审核**:自动化替换后需要人工复核
|
|
348
|
+
5. **key 冲突**:避免重复 key,如已存在则复用
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# 语言代码对照表
|
|
2
|
+
|
|
3
|
+
> HarmonyOS 支持的语言代码规范,包括 ISO 标准和使用示例
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 两种命名方案
|
|
8
|
+
|
|
9
|
+
### 方案 1:仅使用语言代码(推荐用于简单应用)
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
resources/
|
|
13
|
+
├── base/ # 默认资源(fallback)
|
|
14
|
+
├── en/ # 英文
|
|
15
|
+
├── zh/ # 中文
|
|
16
|
+
├── ja/ # 日文
|
|
17
|
+
└── ko/ # 韩文
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**优点**:
|
|
21
|
+
- 简单直观
|
|
22
|
+
- 适配所有区域(en 匹配 en_US, en_GB, en_AU 等)
|
|
23
|
+
|
|
24
|
+
**缺点**:
|
|
25
|
+
- 无法区分 zh_CN(简体)和 zh_TW(繁体)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
### 方案 2:使用语言+区域代码(推荐用于多区域应用)
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
resources/
|
|
33
|
+
├── base/
|
|
34
|
+
├── en_US/ # 美国英语
|
|
35
|
+
├── en_GB/ # 英国英语
|
|
36
|
+
├── zh_CN/ # 简体中文
|
|
37
|
+
├── zh_TW/ # 繁体中文(台湾)
|
|
38
|
+
├── zh_HK/ # 繁体中文(香港)
|
|
39
|
+
└── ja/
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**优点**:
|
|
43
|
+
- 更精确的区域划分
|
|
44
|
+
- 可以针对不同地区提供不同翻译
|
|
45
|
+
|
|
46
|
+
**缺点**:
|
|
47
|
+
- 目录较多
|
|
48
|
+
- 需要维护更多资源文件
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 推荐实践
|
|
53
|
+
|
|
54
|
+
| 场景 | 推荐方案 | 示例 |
|
|
55
|
+
|------|---------|------|
|
|
56
|
+
| 单一市场应用 | 方案 1 | 中国应用:`zh`, `en` |
|
|
57
|
+
| 多区域应用 | 方案 2 | 国际化应用:`zh_CN`, `zh_TW`, `en_US`, `en_GB` |
|
|
58
|
+
| 简单国际化 | 方案 1 | `en`, `zh`, `ja` |
|
|
59
|
+
| 专业国际化 | 方案 2 | 所有主要语言+区域 |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 语言代码对照表
|
|
64
|
+
|
|
65
|
+
| 语言 | 语言代码 | 区域代码 | 完整代码 | 说明 |
|
|
66
|
+
|------|---------|---------|---------|------|
|
|
67
|
+
| 中文 | `zh` | - | `zh` | 通用中文 |
|
|
68
|
+
| 简体中文 | `zh` | `CN` | `zh_CN` 或 `zh-Hans` | 中国大陆 |
|
|
69
|
+
| 繁体中文 | `zh` | `TW` | `zh_TW` 或 `zh-Hant` | 台湾 |
|
|
70
|
+
| 繁体中文 | `zh` | `HK` | `zh_HK` | 香港 |
|
|
71
|
+
| 英文 | `en` | - | `en` | 通用英文 |
|
|
72
|
+
| 美国英文 | `en` | `US` | `en_US` | 美国 |
|
|
73
|
+
| 英国英文 | `en` | `GB` | `en_GB` | 英国 |
|
|
74
|
+
| 日文 | `ja` | - | `ja` | 日本 |
|
|
75
|
+
| 韩文 | `ko` | - | `ko` | 韩国 |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## API 12+ 格式注意
|
|
80
|
+
|
|
81
|
+
HarmonyOS 支持两种格式:
|
|
82
|
+
|
|
83
|
+
| 格式 | 示例 | 说明 |
|
|
84
|
+
|------|------|------|
|
|
85
|
+
| 下划线格式 | `zh_CN`, `en_US` | 传统格式 |
|
|
86
|
+
| 连字符格式 | `zh-Hans`, `zh-Hant` | Unicode 格式,API 12+ 推荐 |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 当前代码仓实践
|
|
91
|
+
|
|
92
|
+
**代码仓使用**:`en_US/`(方案 2)
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
entry/src/main/resources/
|
|
96
|
+
├── base/
|
|
97
|
+
├── en_US/ # 美国英语
|
|
98
|
+
└── dark/ # 深色模式
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**分析**:
|
|
102
|
+
- ✅ 使用了精确的区域划分
|
|
103
|
+
- ⚠️ 只有英文翻译,缺少其他语言
|
|
104
|
+
- 💡 建议:如需支持更多语言,可按相同模式添加
|