@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,174 +1,174 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# audit_i18n_completeness.sh — i18n cross-locale key consistency audit.
|
|
3
|
-
#
|
|
4
|
-
# 单一职责:检测跨 locale 的 string.json / plural.json / strarray.json 的 key 一致性。
|
|
5
|
-
# base 的全部 key 必须在每个 language locale(zh_CN / en_US / ...)都存在;
|
|
6
|
-
# mode locale(dark / horizontal / vertical 等)按差异覆盖即可,豁免。
|
|
7
|
-
#
|
|
8
|
-
# 资源 value 内的 [TODO: translate] / 裸 TODO 检测不在本脚本范围;调用者必须在
|
|
9
|
-
# 当前请求范围收尾时另行扫描并把发现项返回为 unresolved。本脚本只管跨 locale 完整性。
|
|
10
|
-
#
|
|
11
|
-
# 用法(由 arkts-i18n 任务完成后自调,不被其他 skill 跨引用):
|
|
12
|
-
# bash audit_i18n_completeness.sh \
|
|
13
|
-
# --project-root <abs> \
|
|
14
|
-
# --output-json docs/i18n-audit.json
|
|
15
|
-
#
|
|
16
|
-
# 退出码:
|
|
17
|
-
# 0 全 PASS(key 一致)
|
|
18
|
-
# 1 有 key_missing FAIL 项
|
|
19
|
-
# 2 脚本错误(参数缺失等)
|
|
20
|
-
#
|
|
21
|
-
# 输出 JSON 结构:
|
|
22
|
-
# {
|
|
23
|
-
# "locales": ["base", "zh_CN", ...],
|
|
24
|
-
# "language_locales": ["zh_CN", ...], # 参与 key 一致性校验的 locale
|
|
25
|
-
# "mode_locales": ["dark", ...], # 豁免 locale
|
|
26
|
-
# "key_count_per_locale": { "base": N, "zh_CN": N, ... },
|
|
27
|
-
# "findings": [
|
|
28
|
-
# { "type": "key-missing", "locale": "zh_CN", "key": "home_feed", "base_locale": "base", "suggested_action": "..." }
|
|
29
|
-
# ],
|
|
30
|
-
# "summary": { "key_missing": N, "FAIL": N }
|
|
31
|
-
# }
|
|
32
|
-
|
|
33
|
-
set -euo pipefail
|
|
34
|
-
|
|
35
|
-
PROJECT_ROOT=""
|
|
36
|
-
OUTPUT_JSON=""
|
|
37
|
-
|
|
38
|
-
while [[ $# -gt 0 ]]; do
|
|
39
|
-
case "$1" in
|
|
40
|
-
--project-root) PROJECT_ROOT="$2"; shift 2 ;;
|
|
41
|
-
--output-json) OUTPUT_JSON="$2"; shift 2 ;;
|
|
42
|
-
--help|-h)
|
|
43
|
-
grep '^#' "$0" | head -40
|
|
44
|
-
exit 0
|
|
45
|
-
;;
|
|
46
|
-
*)
|
|
47
|
-
echo "unknown arg: $1" >&2
|
|
48
|
-
exit 2
|
|
49
|
-
;;
|
|
50
|
-
esac
|
|
51
|
-
done
|
|
52
|
-
|
|
53
|
-
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
54
|
-
echo "error: --project-root required" >&2
|
|
55
|
-
exit 2
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
# Resolve to absolute path; on Git Bash use `pwd -W` to get Windows-style for Python interop
|
|
59
|
-
if pwd -W >/dev/null 2>&1; then
|
|
60
|
-
PROJECT_ROOT="$(cd "$PROJECT_ROOT" && pwd -W)"
|
|
61
|
-
else
|
|
62
|
-
PROJECT_ROOT="$(cd "$PROJECT_ROOT" && pwd)"
|
|
63
|
-
fi
|
|
64
|
-
OUTPUT_JSON="${OUTPUT_JSON:-/dev/stdout}"
|
|
65
|
-
|
|
66
|
-
RESOURCES_ROOT="$PROJECT_ROOT/entry/src/main/resources"
|
|
67
|
-
|
|
68
|
-
if [[ ! -d "$RESOURCES_ROOT" ]]; then
|
|
69
|
-
echo "error: resources root not found: $RESOURCES_ROOT" >&2
|
|
70
|
-
exit 2
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
# ─── Python helper: cross-locale key consistency only ───────────────────────
|
|
74
|
-
PY_HELPER=$(cat <<'PYEOF'
|
|
75
|
-
import json, os, sys
|
|
76
|
-
|
|
77
|
-
project_root = sys.argv[1]
|
|
78
|
-
output_path = sys.argv[2]
|
|
79
|
-
resources_root = os.path.join(project_root, 'entry', 'src', 'main', 'resources')
|
|
80
|
-
|
|
81
|
-
# 仅对翻译类 element 文件做校验
|
|
82
|
-
TRANSLATION_ELEMENT_FILES = {'string.json', 'plural.json', 'strarray.json'}
|
|
83
|
-
|
|
84
|
-
# Mode locale 豁免(按差异覆盖即可)
|
|
85
|
-
MODE_LOCALES = {'dark', 'light', 'horizontal', 'vertical', 'land', 'port'}
|
|
86
|
-
|
|
87
|
-
def is_language_locale(name: str) -> bool:
|
|
88
|
-
if name in MODE_LOCALES:
|
|
89
|
-
return False
|
|
90
|
-
if '_' in name:
|
|
91
|
-
return True
|
|
92
|
-
if 2 <= len(name) <= 3 and name.isalpha():
|
|
93
|
-
return True
|
|
94
|
-
return False
|
|
95
|
-
|
|
96
|
-
# Collect locales + key sets
|
|
97
|
-
locales = []
|
|
98
|
-
key_per_locale = {}
|
|
99
|
-
for entry in sorted(os.listdir(resources_root)):
|
|
100
|
-
locale_dir = os.path.join(resources_root, entry)
|
|
101
|
-
elem_dir = os.path.join(locale_dir, 'element')
|
|
102
|
-
if not os.path.isdir(elem_dir):
|
|
103
|
-
continue
|
|
104
|
-
if entry in ('rawfile', 'profile'):
|
|
105
|
-
continue
|
|
106
|
-
locales.append(entry)
|
|
107
|
-
key_per_locale[entry] = set()
|
|
108
|
-
for fname in os.listdir(elem_dir):
|
|
109
|
-
if fname not in TRANSLATION_ELEMENT_FILES:
|
|
110
|
-
continue
|
|
111
|
-
fpath = os.path.join(elem_dir, fname)
|
|
112
|
-
try:
|
|
113
|
-
with open(fpath, encoding='utf-8') as f:
|
|
114
|
-
data = json.load(f)
|
|
115
|
-
except Exception:
|
|
116
|
-
continue
|
|
117
|
-
for items in data.values():
|
|
118
|
-
if not isinstance(items, list):
|
|
119
|
-
continue
|
|
120
|
-
for item in items:
|
|
121
|
-
if isinstance(item, dict) and 'name' in item:
|
|
122
|
-
key_per_locale[entry].add(item['name'])
|
|
123
|
-
|
|
124
|
-
# Language vs mode locales
|
|
125
|
-
language_locales = [l for l in locales if is_language_locale(l)]
|
|
126
|
-
mode_locales = [l for l in locales if l in MODE_LOCALES]
|
|
127
|
-
|
|
128
|
-
# Key consistency check: base keys must exist in every language locale
|
|
129
|
-
findings = []
|
|
130
|
-
base_locale = 'base' if 'base' in key_per_locale else (locales[0] if locales else None)
|
|
131
|
-
if base_locale:
|
|
132
|
-
base_keys = key_per_locale[base_locale]
|
|
133
|
-
for locale in language_locales:
|
|
134
|
-
if locale == base_locale:
|
|
135
|
-
continue
|
|
136
|
-
missing = base_keys - key_per_locale[locale]
|
|
137
|
-
for k in sorted(missing):
|
|
138
|
-
findings.append({
|
|
139
|
-
"type": "key-missing",
|
|
140
|
-
"locale": locale,
|
|
141
|
-
"key": k,
|
|
142
|
-
"base_locale": base_locale,
|
|
143
|
-
"suggested_action": f"Add key `{k}` to {locale}/element/<file>.json with locale-appropriate value.",
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
summary = {
|
|
147
|
-
"key_missing": len(findings),
|
|
148
|
-
"FAIL": len(findings),
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
out = {
|
|
152
|
-
"locales": locales,
|
|
153
|
-
"language_locales": language_locales,
|
|
154
|
-
"mode_locales": mode_locales,
|
|
155
|
-
"key_count_per_locale": {l: len(k) for l, k in key_per_locale.items()},
|
|
156
|
-
"summary": summary,
|
|
157
|
-
"findings": findings,
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if output_path == '/dev/stdout':
|
|
161
|
-
print(json.dumps(out, ensure_ascii=False, indent=2))
|
|
162
|
-
else:
|
|
163
|
-
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
164
|
-
with open(output_path, 'w', encoding='utf-8') as f:
|
|
165
|
-
json.dump(out, f, ensure_ascii=False, indent=2)
|
|
166
|
-
print(f"i18n audit complete: {output_path}")
|
|
167
|
-
print(f" locales={locales} language={language_locales} mode={mode_locales}")
|
|
168
|
-
print(f" FAIL={summary['FAIL']} (key_missing={summary['key_missing']})")
|
|
169
|
-
|
|
170
|
-
sys.exit(1 if summary["FAIL"] > 0 else 0)
|
|
171
|
-
PYEOF
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
python3 -c "$PY_HELPER" "$PROJECT_ROOT" "$OUTPUT_JSON"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# audit_i18n_completeness.sh — i18n cross-locale key consistency audit.
|
|
3
|
+
#
|
|
4
|
+
# 单一职责:检测跨 locale 的 string.json / plural.json / strarray.json 的 key 一致性。
|
|
5
|
+
# base 的全部 key 必须在每个 language locale(zh_CN / en_US / ...)都存在;
|
|
6
|
+
# mode locale(dark / horizontal / vertical 等)按差异覆盖即可,豁免。
|
|
7
|
+
#
|
|
8
|
+
# 资源 value 内的 [TODO: translate] / 裸 TODO 检测不在本脚本范围;调用者必须在
|
|
9
|
+
# 当前请求范围收尾时另行扫描并把发现项返回为 unresolved。本脚本只管跨 locale 完整性。
|
|
10
|
+
#
|
|
11
|
+
# 用法(由 arkts-i18n 任务完成后自调,不被其他 skill 跨引用):
|
|
12
|
+
# bash audit_i18n_completeness.sh \
|
|
13
|
+
# --project-root <abs> \
|
|
14
|
+
# --output-json docs/i18n-audit.json
|
|
15
|
+
#
|
|
16
|
+
# 退出码:
|
|
17
|
+
# 0 全 PASS(key 一致)
|
|
18
|
+
# 1 有 key_missing FAIL 项
|
|
19
|
+
# 2 脚本错误(参数缺失等)
|
|
20
|
+
#
|
|
21
|
+
# 输出 JSON 结构:
|
|
22
|
+
# {
|
|
23
|
+
# "locales": ["base", "zh_CN", ...],
|
|
24
|
+
# "language_locales": ["zh_CN", ...], # 参与 key 一致性校验的 locale
|
|
25
|
+
# "mode_locales": ["dark", ...], # 豁免 locale
|
|
26
|
+
# "key_count_per_locale": { "base": N, "zh_CN": N, ... },
|
|
27
|
+
# "findings": [
|
|
28
|
+
# { "type": "key-missing", "locale": "zh_CN", "key": "home_feed", "base_locale": "base", "suggested_action": "..." }
|
|
29
|
+
# ],
|
|
30
|
+
# "summary": { "key_missing": N, "FAIL": N }
|
|
31
|
+
# }
|
|
32
|
+
|
|
33
|
+
set -euo pipefail
|
|
34
|
+
|
|
35
|
+
PROJECT_ROOT=""
|
|
36
|
+
OUTPUT_JSON=""
|
|
37
|
+
|
|
38
|
+
while [[ $# -gt 0 ]]; do
|
|
39
|
+
case "$1" in
|
|
40
|
+
--project-root) PROJECT_ROOT="$2"; shift 2 ;;
|
|
41
|
+
--output-json) OUTPUT_JSON="$2"; shift 2 ;;
|
|
42
|
+
--help|-h)
|
|
43
|
+
grep '^#' "$0" | head -40
|
|
44
|
+
exit 0
|
|
45
|
+
;;
|
|
46
|
+
*)
|
|
47
|
+
echo "unknown arg: $1" >&2
|
|
48
|
+
exit 2
|
|
49
|
+
;;
|
|
50
|
+
esac
|
|
51
|
+
done
|
|
52
|
+
|
|
53
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
54
|
+
echo "error: --project-root required" >&2
|
|
55
|
+
exit 2
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Resolve to absolute path; on Git Bash use `pwd -W` to get Windows-style for Python interop
|
|
59
|
+
if pwd -W >/dev/null 2>&1; then
|
|
60
|
+
PROJECT_ROOT="$(cd "$PROJECT_ROOT" && pwd -W)"
|
|
61
|
+
else
|
|
62
|
+
PROJECT_ROOT="$(cd "$PROJECT_ROOT" && pwd)"
|
|
63
|
+
fi
|
|
64
|
+
OUTPUT_JSON="${OUTPUT_JSON:-/dev/stdout}"
|
|
65
|
+
|
|
66
|
+
RESOURCES_ROOT="$PROJECT_ROOT/entry/src/main/resources"
|
|
67
|
+
|
|
68
|
+
if [[ ! -d "$RESOURCES_ROOT" ]]; then
|
|
69
|
+
echo "error: resources root not found: $RESOURCES_ROOT" >&2
|
|
70
|
+
exit 2
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# ─── Python helper: cross-locale key consistency only ───────────────────────
|
|
74
|
+
PY_HELPER=$(cat <<'PYEOF'
|
|
75
|
+
import json, os, sys
|
|
76
|
+
|
|
77
|
+
project_root = sys.argv[1]
|
|
78
|
+
output_path = sys.argv[2]
|
|
79
|
+
resources_root = os.path.join(project_root, 'entry', 'src', 'main', 'resources')
|
|
80
|
+
|
|
81
|
+
# 仅对翻译类 element 文件做校验
|
|
82
|
+
TRANSLATION_ELEMENT_FILES = {'string.json', 'plural.json', 'strarray.json'}
|
|
83
|
+
|
|
84
|
+
# Mode locale 豁免(按差异覆盖即可)
|
|
85
|
+
MODE_LOCALES = {'dark', 'light', 'horizontal', 'vertical', 'land', 'port'}
|
|
86
|
+
|
|
87
|
+
def is_language_locale(name: str) -> bool:
|
|
88
|
+
if name in MODE_LOCALES:
|
|
89
|
+
return False
|
|
90
|
+
if '_' in name:
|
|
91
|
+
return True
|
|
92
|
+
if 2 <= len(name) <= 3 and name.isalpha():
|
|
93
|
+
return True
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
# Collect locales + key sets
|
|
97
|
+
locales = []
|
|
98
|
+
key_per_locale = {}
|
|
99
|
+
for entry in sorted(os.listdir(resources_root)):
|
|
100
|
+
locale_dir = os.path.join(resources_root, entry)
|
|
101
|
+
elem_dir = os.path.join(locale_dir, 'element')
|
|
102
|
+
if not os.path.isdir(elem_dir):
|
|
103
|
+
continue
|
|
104
|
+
if entry in ('rawfile', 'profile'):
|
|
105
|
+
continue
|
|
106
|
+
locales.append(entry)
|
|
107
|
+
key_per_locale[entry] = set()
|
|
108
|
+
for fname in os.listdir(elem_dir):
|
|
109
|
+
if fname not in TRANSLATION_ELEMENT_FILES:
|
|
110
|
+
continue
|
|
111
|
+
fpath = os.path.join(elem_dir, fname)
|
|
112
|
+
try:
|
|
113
|
+
with open(fpath, encoding='utf-8') as f:
|
|
114
|
+
data = json.load(f)
|
|
115
|
+
except Exception:
|
|
116
|
+
continue
|
|
117
|
+
for items in data.values():
|
|
118
|
+
if not isinstance(items, list):
|
|
119
|
+
continue
|
|
120
|
+
for item in items:
|
|
121
|
+
if isinstance(item, dict) and 'name' in item:
|
|
122
|
+
key_per_locale[entry].add(item['name'])
|
|
123
|
+
|
|
124
|
+
# Language vs mode locales
|
|
125
|
+
language_locales = [l for l in locales if is_language_locale(l)]
|
|
126
|
+
mode_locales = [l for l in locales if l in MODE_LOCALES]
|
|
127
|
+
|
|
128
|
+
# Key consistency check: base keys must exist in every language locale
|
|
129
|
+
findings = []
|
|
130
|
+
base_locale = 'base' if 'base' in key_per_locale else (locales[0] if locales else None)
|
|
131
|
+
if base_locale:
|
|
132
|
+
base_keys = key_per_locale[base_locale]
|
|
133
|
+
for locale in language_locales:
|
|
134
|
+
if locale == base_locale:
|
|
135
|
+
continue
|
|
136
|
+
missing = base_keys - key_per_locale[locale]
|
|
137
|
+
for k in sorted(missing):
|
|
138
|
+
findings.append({
|
|
139
|
+
"type": "key-missing",
|
|
140
|
+
"locale": locale,
|
|
141
|
+
"key": k,
|
|
142
|
+
"base_locale": base_locale,
|
|
143
|
+
"suggested_action": f"Add key `{k}` to {locale}/element/<file>.json with locale-appropriate value.",
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
summary = {
|
|
147
|
+
"key_missing": len(findings),
|
|
148
|
+
"FAIL": len(findings),
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
out = {
|
|
152
|
+
"locales": locales,
|
|
153
|
+
"language_locales": language_locales,
|
|
154
|
+
"mode_locales": mode_locales,
|
|
155
|
+
"key_count_per_locale": {l: len(k) for l, k in key_per_locale.items()},
|
|
156
|
+
"summary": summary,
|
|
157
|
+
"findings": findings,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if output_path == '/dev/stdout':
|
|
161
|
+
print(json.dumps(out, ensure_ascii=False, indent=2))
|
|
162
|
+
else:
|
|
163
|
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
164
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
165
|
+
json.dump(out, f, ensure_ascii=False, indent=2)
|
|
166
|
+
print(f"i18n audit complete: {output_path}")
|
|
167
|
+
print(f" locales={locales} language={language_locales} mode={mode_locales}")
|
|
168
|
+
print(f" FAIL={summary['FAIL']} (key_missing={summary['key_missing']})")
|
|
169
|
+
|
|
170
|
+
sys.exit(1 if summary["FAIL"] > 0 else 0)
|
|
171
|
+
PYEOF
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
python3 -c "$PY_HELPER" "$PROJECT_ROOT" "$OUTPUT_JSON"
|