fix(i18n): fall back to built-in zh table when a pack misses a key

A stale .pjson (e.g. downloaded before new keys existed) showed raw 'msg.xxx'
because pmm_tr looked only in the loaded pack. Now a missing key falls back to
the built-in zh-CN table, so any pack stays readable.

Also sync the mirror language packs (zh-CN/en-US) with the new keys
(desc.clean, msg.dep-satisfied, msg.err.conflict, msg.cache-cleared,
msg.self-up-to-date); users just re-run 'pmm setting lang <loc>' to pick it up.
这个提交包含在:
JGZYES
2026-09-05 20:36:51 +08:00
父节点 e839886ee5
当前提交 57500e8f0d
修改 3 个文件,包含 25 行新增5 行删除
+13 -3
查看文件
@@ -8,6 +8,7 @@
#include <stdarg.h> #include <stdarg.h>
static JsonValue *g_lang = NULL; static JsonValue *g_lang = NULL;
static JsonValue *g_builtin = NULL;
static char g_locale[64] = ""; static char g_locale[64] = "";
/* Detect locale from LANG / LC_ALL env, else default "zh-CN". */ /* Detect locale from LANG / LC_ALL env, else default "zh-CN". */
@@ -80,9 +81,18 @@ int pmm_lang_load_active(void) {
} }
const char *pmm_tr(const char *key) { const char *pmm_tr(const char *key) {
if (!g_lang) return key; /* Look up in the loaded pack first, then fall back to the built-in zh-CN
const char *t = json_str(g_lang, key); * table so a stale .pjson never shows a raw "msg.xxx" key. */
return t ? t : key; if (g_lang) {
const char *t = json_str(g_lang, key);
if (t) return t;
}
if (!g_builtin) g_builtin = json_parse(kBuiltinZh);
if (g_builtin) {
const char *b = json_str(g_builtin, key);
if (b) return b;
}
return key;
} }
const char *pmm_tr_fmt(const char *key, ...) { const char *pmm_tr_fmt(const char *key, ...) {
+6 -1
查看文件
@@ -123,5 +123,10 @@
"mirror.sources": "mirrors:", "mirror.sources": "mirrors:",
"mirror.checking": "checking registry mirrors...", "mirror.checking": "checking registry mirrors...",
"mirror.result": "result: %d/%d sources reachable", "mirror.result": "result: %d/%d sources reachable",
"lang.packs-in": "language packs in %s:" "lang.packs-in": "language packs in %s:",
"desc.clean": "clean cache",
"msg.dep-satisfied": "dependency %s%s%s already installed/provided, skipped",
"msg.err.conflict": "cannot install %s: conflicts with installed %s",
"msg.cache-cleared": "cache cleaned: %d files, freed %.1f KB",
"msg.self-up-to-date": "already up to date (v%s, latest v%s), skipping upgrade"
} }
+6 -1
查看文件
@@ -123,5 +123,10 @@
"mirror.sources": "镜像源:", "mirror.sources": "镜像源:",
"mirror.checking": "正在检查注册表镜像源...", "mirror.checking": "正在检查注册表镜像源...",
"mirror.result": "结果: %d/%d 个镜像源可达", "mirror.result": "结果: %d/%d 个镜像源可达",
"lang.packs-in": "%s 中的语言包:" "lang.packs-in": "%s 中的语言包:",
"desc.clean": "清理缓存",
"msg.dep-satisfied": "依赖 %s%s%s 已装/被提供,跳过",
"msg.err.conflict": "无法安装 %s:与已装的 %s 冲突",
"msg.cache-cleared": "已清理缓存: %d 个文件,释放 %.1f KB",
"msg.self-up-to-date": "当前 v%s 已是最新(v%s),跳过升级"
} }