i18n: fix arg/fmt mismatches + standardise help/banner/docs

Audit found pmm_tr_fmt calls whose arg count didn't match the language pack's
%i placeholders, and a few leftover logic errors:
- main.c selected() stray trailing arg; legacy branch passes os+''.
- install.c checksum-mismatch passed a template string as the first arg.
- bad-entry vs bad-registry-entry split (1-arg vs 2-arg semantics).
- pdm.c checksum-mismatch used the 3-arg key with 2 args -> dedicated
  checksum-mismatch-file key.
- pmm.c removed used the pdm remove key -> unregistered key.
- sha256/sha1 split into inline (1 arg) vs verify (2 arg) keys; downloading
  and lang packs aligned to 2 placeholders.
- Removed duplicate lang keys; STANDARD.md %placeholder docs updated to the
  actual %s convention.

Result: audit ALL MATCH, both packs valid (124 keys) with no duplicates,
all 100 used keys present, bilingual smoke tests clean (no key fallback).
这个提交包含在:
JGZYES
2026-09-04 22:36:58 +08:00
父节点 e3151c9f35
当前提交 95c265cab9
修改 7 个文件,包含 34 行新增21 行删除
+4 -4
查看文件
@@ -112,12 +112,12 @@ web/mirror/lang/<locale>.pjson
``` ```
{ {
"help.banner": "PMM {ver} ({arch})", "help.banner": "PMM %s (%s)",
"cmd.list": "list", "cmd.list": "list",
"cmd.info": "info", "cmd.info": "info",
"desc.list": "根据名称列出软件包", "desc.list": "根据名称列出软件包",
"msg.installed": "已安装 {name}", "msg.installed": "已安装 %s",
"msg.err.notfound": "未找到软件包 {name}" "msg.err.notfound": "未找到软件包 %s"
} }
``` ```
@@ -134,7 +134,7 @@ web/mirror/lang/<locale>.pjson
| `opt.` | 选项说明 | | `opt.` | 选项说明 |
| `cfg.` | 配置项名 | | `cfg.` | 配置项名 |
占位符用 `{name}``{ver}``{arch}` 等(花括号 + 小写字段名),渲染时替换。 占位符用 C `printf` 风格的 `%s` / `%d`(与源码调用 `pmm_tr_fmt("key", args...)` 的参数一一对应),渲染时由 `pmm_tr_fmt` 替换。
键**不得**含空格 / 大写,语义词用小写连字符(如 `msg.err.not-found`)。 键**不得**含空格 / 大写,语义词用小写连字符(如 `msg.err.not-found`)。
## 6. 提交 / PR 约定 ## 6. 提交 / PR 约定
+5 -6
查看文件
@@ -437,9 +437,9 @@ int install_file(const char *url, const char *name) {
/* integrity: sha256 + sha1, verified against sidecar checksums when present */ /* integrity: sha256 + sha1, verified against sidecar checksums when present */
char hex[128]; char hex[128];
if (pmm_sha256_file(path, hex) == 0) if (pmm_sha256_file(path, hex) == 0)
pmm_info("%s", pmm_tr_fmt("msg.sha256", hex)); pmm_info("%s", pmm_tr_fmt("msg.hash-sha256", hex));
if (pmm_sha1_file(path, hex) == 0) if (pmm_sha1_file(path, hex) == 0)
pmm_info("%s", pmm_tr_fmt("msg.sha1", hex)); pmm_info("%s", pmm_tr_fmt("msg.hash-sha1", hex));
if (verify_checksums(url, path, name) != 0) { if (verify_checksums(url, path, name) != 0) {
pmm_error(pmm_tr("msg.err.checksum-refuse")); pmm_error(pmm_tr("msg.err.checksum-refuse"));
remove(path); remove(path);
@@ -831,7 +831,7 @@ int install_from_registry(const char *name, const char *spec, const char *mirror
JsonValue *meta = json_parse(body); JsonValue *meta = json_parse(body);
free(body); free(body);
if (!meta || meta->type != JSON_OBJECT) { if (!meta || meta->type != JSON_OBJECT) {
pmm_error("%s", pmm_tr_fmt("msg.err.bad-entry", name, used_base)); pmm_error("%s", pmm_tr_fmt("msg.err.bad-registry-entry", name, used_base));
json_free(meta); json_free(meta);
mirrors_free(ml); mirrors_free(ml);
return -1; return -1;
@@ -878,7 +878,7 @@ int install_from_registry(const char *name, const char *spec, const char *mirror
/* legacy single-platform entry */ /* legacy single-platform entry */
dl = json_str(meta, "url"); file = json_str(meta, "file"); dl = json_str(meta, "url"); file = json_str(meta, "file");
const char *s = json_str(meta, "sha256"); want_sha = s ? strdup(s) : NULL; const char *s = json_str(meta, "sha256"); want_sha = s ? strdup(s) : NULL;
pmm_info("%s", pmm_tr_fmt("msg.selected", name, json_str(meta, "version")), osn); pmm_info("%s", pmm_tr_fmt("msg.selected", name, json_str(meta, "version"), osn, ""));
} }
if (!dl) { if (!dl) {
pmm_error("%s", pmm_tr_fmt("msg.err.registry-entry-no-url", name)); pmm_error("%s", pmm_tr_fmt("msg.err.registry-entry-no-url", name));
@@ -897,8 +897,7 @@ int install_from_registry(const char *name, const char *spec, const char *mirror
snprintf(path, sizeof(path), "%s/%s", cache, file_cp); snprintf(path, sizeof(path), "%s/%s", cache, file_cp);
if (pmm_sha256_file(path, hex) == 0) { if (pmm_sha256_file(path, hex) == 0) {
if (strcasecmp(want_sha, hex) != 0) { if (strcasecmp(want_sha, hex) != 0) {
pmm_error("%s", pmm_tr_fmt("msg.checksum-mismatch", pmm_error("%s", pmm_tr_fmt("msg.checksum-mismatch", name, want_sha, hex));
" expected: %s\n actual: %s\n", name, want_sha, hex));
remove(path); remove(path);
rc = -1; rc = -1;
} else { } else {
+1 -1
查看文件
@@ -598,7 +598,7 @@ static int cmd_install_git(int argc, char **argv, const char *flag) {
"(tried gitea/gitlab/github shapes)\n", repo); "(tried gitea/gitlab/github shapes)\n", repo);
if (!asset) { if (!asset) {
if (host != HOST_AUTO) if (host != HOST_AUTO)
pmm_error("%s", pmm_tr_fmt("msg.no-suitable-asset", pmm_os_name(os)), repo); pmm_error("%s", pmm_tr_fmt("msg.err.no-suitable-asset", pmm_os_name(os), repo));
repo_close(ctx); repo_close(ctx);
return 1; return 1;
} }
+1 -1
查看文件
@@ -422,7 +422,7 @@ int pdm_install_file(const char *pdmfile) {
char mp[1200]; char mp[1200];
snprintf(mp, sizeof(mp), "installed/.stage/%s", fname); snprintf(mp, sizeof(mp), "installed/.stage/%s", fname);
if (pmm_sha256_file(mp, hex) != 0 || strcasecmp(hex, expect) != 0) { if (pmm_sha256_file(mp, hex) != 0 || strcasecmp(hex, expect) != 0) {
pmm_error("%s", pmm_tr_fmt("msg.checksum-mismatch", fname, pdmfile)); pmm_error("%s", pmm_tr_fmt("msg.err.checksum-mismatch-file", fname, pdmfile));
fclose(sf); chdir_restore(); remove(tmpname); return -1; fclose(sf); chdir_restore(); remove(tmpname); return -1;
} }
} }
+1 -1
查看文件
@@ -532,6 +532,6 @@ void pmm_reg_uninstall_clear(const char *pkg) {
"%%SystemRoot%%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NoProfile -Command \"Remove-Item -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s' -Recurse -Force -ErrorAction SilentlyContinue\" 2>nul", "%%SystemRoot%%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -NoProfile -Command \"Remove-Item -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s' -Recurse -Force -ErrorAction SilentlyContinue\" 2>nul",
pkg); pkg);
system(cmd); system(cmd);
pmm_success("%s", pmm_tr_fmt("msg.removed", pkg)); pmm_success("%s", pmm_tr_fmt("msg.unregistered", pkg));
#endif #endif
} }
+11 -4
查看文件
@@ -32,10 +32,12 @@
"opt.verbose": "print extra detail", "opt.verbose": "print extra detail",
"msg.looking-up": "looking up %s in mirror %s", "msg.looking-up": "looking up %s in mirror %s",
"msg.selected": "selected %s@%s (os=%s arch=%s)", "msg.selected": "selected %s@%s (os=%s arch=%s)",
"msg.downloading": "downloading %s", "msg.downloading": "downloading %s%s",
"msg.downloaded": "downloaded %s", "msg.downloaded": "downloaded %s",
"msg.sha256": "sha256: %s", "msg.hash-sha256": "sha256: %s",
"msg.sha1": "sha1: %s", "msg.hash-sha1": "sha1: %s",
"msg.sha256": "sha256: %s %s",
"msg.sha1": "sha1: %s %s",
"msg.installing": "installing %s ...", "msg.installing": "installing %s ...",
"msg.installing-file": "installing local file %s", "msg.installing-file": "installing local file %s",
"msg.installed": "installed %s", "msg.installed": "installed %s",
@@ -50,6 +52,7 @@
"msg.upgrading": "%s: %s -> %s", "msg.upgrading": "%s: %s -> %s",
"msg.upgraded": "upgraded %d package(s).", "msg.upgraded": "upgraded %d package(s).",
"msg.no-installed": "no installed packages.", "msg.no-installed": "no installed packages.",
"msg.registry-empty": "registry index is empty.",
"msg.registry-updated": "updated registry index: %d/%d packages cached.", "msg.registry-updated": "updated registry index: %d/%d packages cached.",
"msg.skipping-no-registry": "skipping %s: no registry entry", "msg.skipping-no-registry": "skipping %s: no registry entry",
"msg.no-match": "no package matches '%s'", "msg.no-match": "no package matches '%s'",
@@ -73,6 +76,9 @@
"msg.err.usage": "usage:", "msg.err.usage": "usage:",
"msg.err.not-found": "package '%s' not found", "msg.err.not-found": "package '%s' not found",
"msg.err.not-installed": "package '%s' is not installed", "msg.err.not-installed": "package '%s' is not installed",
"msg.err.empty-path": "empty file path",
"msg.err.registry-not-found": "package '%s' not found in any registry mirror",
"msg.err.no-suitable-asset": "no suitable %s asset found in latest release of %s",
"msg.err.unknown-cmd": "unknown command '%s' (try 'pmm help')", "msg.err.unknown-cmd": "unknown command '%s' (try 'pmm help')",
"msg.err.failed-install": "failed to install %s", "msg.err.failed-install": "failed to install %s",
"msg.err.cannot-write": "cannot write %s", "msg.err.cannot-write": "cannot write %s",
@@ -86,6 +92,8 @@
"msg.err.no-registry-index": "no registry index (packages.json) available", "msg.err.no-registry-index": "no registry index (packages.json) available",
"msg.err.bad-registry-index": "bad registry index", "msg.err.bad-registry-index": "bad registry index",
"msg.err.bad-entry": "bad registry entry '%s'", "msg.err.bad-entry": "bad registry entry '%s'",
"msg.err.bad-registry-entry": "bad registry entry for '%s' (%s)",
"msg.err.checksum-mismatch-file": "checksum mismatch for %s in %s",
"msg.err.no-control": "%s has no pdm-control file", "msg.err.no-control": "%s has no pdm-control file",
"msg.err.no-package": "pdm-control missing 'Package:' field", "msg.err.no-package": "pdm-control missing 'Package:' field",
"msg.err.tar-control": "tar (control) failed", "msg.err.tar-control": "tar (control) failed",
@@ -98,7 +106,6 @@
"msg.err.oom": "out of memory", "msg.err.oom": "out of memory",
"msg.err.json-parse": "JSON parse error: %s", "msg.err.json-parse": "JSON parse error: %s",
"msg.err.unknown-host": "unknown host '%s'", "msg.err.unknown-host": "unknown host '%s'",
"msg.err.no-suitable-asset": "no suitable %s asset found in latest release of %s",
"msg.err.checksum-refuse": "refusing to install: checksum verification failed", "msg.err.checksum-refuse": "refusing to install: checksum verification failed",
"msg.err.registry-entry-no-url": "registry entry for '%s' has no url", "msg.err.registry-entry-no-url": "registry entry for '%s' has no url",
"msg.err.rpm-stage": "cannot create rpm stage dir: %s", "msg.err.rpm-stage": "cannot create rpm stage dir: %s",
+11 -4
查看文件
@@ -32,10 +32,12 @@
"opt.verbose": "打印更多细节", "opt.verbose": "打印更多细节",
"msg.looking-up": "在镜像 %s 中查找 %s", "msg.looking-up": "在镜像 %s 中查找 %s",
"msg.selected": "已选择 %s@%s (os=%s arch=%s)", "msg.selected": "已选择 %s@%s (os=%s arch=%s)",
"msg.downloading": "下载 %s", "msg.downloading": "下载 %s%s",
"msg.downloaded": "已下载 %s", "msg.downloaded": "已下载 %s",
"msg.sha256": "sha256: %s", "msg.hash-sha256": "sha256: %s",
"msg.sha1": "sha1: %s", "msg.hash-sha1": "sha1: %s",
"msg.sha256": "sha256: %s %s",
"msg.sha1": "sha1: %s %s",
"msg.installing": "正在安装 %s ...", "msg.installing": "正在安装 %s ...",
"msg.installing-file": "正在安装本地文件 %s", "msg.installing-file": "正在安装本地文件 %s",
"msg.installed": "已安装 %s", "msg.installed": "已安装 %s",
@@ -50,6 +52,7 @@
"msg.upgrading": "%s: %s -> %s", "msg.upgrading": "%s: %s -> %s",
"msg.upgraded": "已升级 %d 个软件包。", "msg.upgraded": "已升级 %d 个软件包。",
"msg.no-installed": "没有已安装的软件包。", "msg.no-installed": "没有已安装的软件包。",
"msg.registry-empty": "注册表索引为空。",
"msg.registry-updated": "注册表索引已更新: %d/%d 个软件包已缓存。", "msg.registry-updated": "注册表索引已更新: %d/%d 个软件包已缓存。",
"msg.skipping-no-registry": "跳过 %s: 没有注册表条目", "msg.skipping-no-registry": "跳过 %s: 没有注册表条目",
"msg.no-match": "没有匹配 '%s' 的软件包", "msg.no-match": "没有匹配 '%s' 的软件包",
@@ -73,6 +76,9 @@
"msg.err.usage": "用法:", "msg.err.usage": "用法:",
"msg.err.not-found": "找不到软件包 '%s'", "msg.err.not-found": "找不到软件包 '%s'",
"msg.err.not-installed": "软件包 '%s' 未安装", "msg.err.not-installed": "软件包 '%s' 未安装",
"msg.err.empty-path": "文件路径为空",
"msg.err.registry-not-found": "在任一注册表镜像中找不到软件包 '%s'",
"msg.err.no-suitable-asset": "在最新发布中找不到适用于 %s 的资源 %s",
"msg.err.unknown-cmd": "未知命令 '%s'(试试 'pmm help'", "msg.err.unknown-cmd": "未知命令 '%s'(试试 'pmm help'",
"msg.err.failed-install": "安装 %s 失败", "msg.err.failed-install": "安装 %s 失败",
"msg.err.cannot-write": "无法写入 %s", "msg.err.cannot-write": "无法写入 %s",
@@ -86,6 +92,8 @@
"msg.err.no-registry-index": "没有可用注册表索引 (packages.json)", "msg.err.no-registry-index": "没有可用注册表索引 (packages.json)",
"msg.err.bad-registry-index": "注册表索引格式错误", "msg.err.bad-registry-index": "注册表索引格式错误",
"msg.err.bad-entry": "软件包 '%s' 的注册表条目错误", "msg.err.bad-entry": "软件包 '%s' 的注册表条目错误",
"msg.err.bad-registry-entry": "软件包 '%s' 的注册表条目错误 (%s)",
"msg.err.checksum-mismatch-file": "文件 %s 与 %s 校验不匹配",
"msg.err.no-control": "没有 pdm-control 文件: %s", "msg.err.no-control": "没有 pdm-control 文件: %s",
"msg.err.no-package": "pdm-control 缺少 'Package:' 字段", "msg.err.no-package": "pdm-control 缺少 'Package:' 字段",
"msg.err.tar-control": "tarcontrol)失败", "msg.err.tar-control": "tarcontrol)失败",
@@ -98,7 +106,6 @@
"msg.err.oom": "内存不足", "msg.err.oom": "内存不足",
"msg.err.json-parse": "JSON 解析错误: %s", "msg.err.json-parse": "JSON 解析错误: %s",
"msg.err.unknown-host": "未知的主机 '%s'", "msg.err.unknown-host": "未知的主机 '%s'",
"msg.err.no-suitable-asset": "在最新发布中找不到适用于 %s 的资源 %s",
"msg.err.checksum-refuse": "拒绝安装: 校验和验证失败", "msg.err.checksum-refuse": "拒绝安装: 校验和验证失败",
"msg.err.registry-entry-no-url": "软件包 '%s' 的注册表条目没有 url", "msg.err.registry-entry-no-url": "软件包 '%s' 的注册表条目没有 url",
"msg.err.rpm-stage": "无法创建 rpm 解包目录: %s", "msg.err.rpm-stage": "无法创建 rpm 解包目录: %s",