镜像自地址
https://github.com/JGZYES/ParlzPackageManger.git
已同步 2026-09-11 05:42:44 +08:00
i18n: .pjson language-pack system + PMM output standard
- docs/STANDARD.md: unified banner (PMM <ver> (<arch>)), command wording,
help layout, CHANGELOG and .pjson key-naming conventions.
- src/i18n.c/.h: pmm_tr / pmm_tr_fmt / pmm_lang_load(_active) / set_locale with
LANG|LC_ALL auto-detect, loading a flattened key->value .pjson from
~/.pmm/lang/<locale>.pjson.
- pmm setting lang -l | <locale>: list installed packs; download <locale>.pjson
from the registry mirror into ~/.pmm/lang/, persist language=<locale> in
pmm.conf and reload. PmmConfig gains a language field (read from json/ini/conf).
- Banner + version now standardised: PMM 0.3.5 (amd64) (no OS name / brackets).
- web/mirror/lang/{zh-CN,en-US}.pjson built-in packs; web/translate.php lists
packs (keys + download) and explains community contribution; nav now has a
翻译 item.
- Makefile/build.bat/release.yml include src/i18n.c.
Verified on WSL: compiles; version/help banner -> 'PMM 0.3.5 (amd64)';
pmm setting lang -l lists the packs dir.
这个提交包含在:
@@ -45,7 +45,7 @@ jobs:
|
||||
VER=${VER#v}
|
||||
${{ matrix.cc }} -O2 -Wall -static -std=c11 -D_FORTIFY_SOURCE=0 -U_FORTIFY_SOURCE -DPMM_VERSION=\"$VER\" -o out/pmm${{ matrix.ext }} \
|
||||
src/main.c src/json.c src/ini.c src/pmm.c src/http.c src/repo.c src/install.c \
|
||||
src/sha256.c src/sha1.c src/mirrors.c src/pdm.c src/out.c
|
||||
src/sha256.c src/sha1.c src/mirrors.c src/pdm.c src/out.c src/i18n.c
|
||||
|
||||
- name: Package installers
|
||||
run: |
|
||||
|
||||
@@ -3,7 +3,7 @@ CFLAGS ?= -O2 -Wall -static -Wextra -std=c11
|
||||
PREFIX ?= /usr/local
|
||||
|
||||
SRC = src/main.c src/json.c src/ini.c src/pmm.c src/http.c src/repo.c \
|
||||
src/install.c src/sha256.c src/sha1.c src/mirrors.c src/pdm.c src/out.c
|
||||
src/install.c src/sha256.c src/sha1.c src/mirrors.c src/pdm.c src/out.c src/i18n.c
|
||||
|
||||
all: pmm
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@echo off
|
||||
rem One-shot Windows build: produces pmm.exe (needs MinGW gcc + curl in PATH)
|
||||
set SRC=src\main.c src\json.c src\ini.c src\pmm.c src\http.c src\repo.c src\install.c src\sha256.c src\sha1.c src\mirrors.c src\pdm.c src\out.c
|
||||
set SRC=src\main.c src\json.c src\ini.c src\pmm.c src\http.c src\repo.c src\install.c src\sha256.c src\sha1.c src\mirrors.c src\pdm.c src\out.c src\i18n.c
|
||||
gcc -O2 -Wall -static -Wextra -std=c11 -o pmm.exe %SRC%
|
||||
if errorlevel 1 (echo pmm build failed & exit /b 1)
|
||||
echo built pmm.exe
|
||||
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
# PMM 统一标准 (STANDARD.md)
|
||||
|
||||
> Parlz Package Manager (PMM)。核心理念:**敲即得,装即跑** —— 在命令行上安装软件。
|
||||
> 本文件是 PMM 的**文案、输出、版本、CHANGELOG、语言包**的统一规范,所有改动须遵循。
|
||||
|
||||
---
|
||||
|
||||
## 1. 版本 Banner 格式
|
||||
|
||||
所有版本信息一律使用:
|
||||
|
||||
```
|
||||
PMM <版本号> (<架构>)
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```
|
||||
PMM 0.3.5 (amd64)
|
||||
PMM 0.3.5 (aarch64)
|
||||
PMM 0.1.0 (x86)
|
||||
```
|
||||
|
||||
- 大写的 `PMM` + 空格 + 版本号 + 空格 + `(架构)`。
|
||||
- 架构由 `pmm_detect_arch()` 提供(`amd64` / `aarch64` / `x86` / `any`)。
|
||||
- `pmm version` / `pmm -v` / `--version` 输出这一行,**不带** OS 名,不带方括号。
|
||||
- Bash/脚本中可用 `pmm version` 解析版本。
|
||||
|
||||
## 2. 命令文案措辞表
|
||||
|
||||
命令描述统一用**动词开头**的祈使句;选项描述统一用 `动词 + 宾语`。中文 / 英文措辞见下表(也是 `.pjson` 的标准译文):
|
||||
|
||||
| 命令 | 中文 | English |
|
||||
|---|---|---|
|
||||
| `list` | 根据名称列出软件包 | list packages by name |
|
||||
| `info` | 根据名称列出软件包的详细信息 | show detailed info for a package |
|
||||
| `search` | 根据关键词搜索软件包 | search packages by keyword |
|
||||
| `install` | 根据名称安装指定软件包 | install a package by name |
|
||||
| `remove` | 根据名称卸载指定软件包 | uninstall a package by name |
|
||||
| `update` | 刷新镜像源索引(apt 风格) | refresh the registry index (apt-style) |
|
||||
| `upgrade` | 升级已安装的软件包 | upgrade all installed packages |
|
||||
| `mirror` | 列出 / 管理镜像源 | list / manage mirrors |
|
||||
| `self-update` | 更新 PMM 工具本身 | update the pmm tool itself |
|
||||
| `version` / `help` | 查看版本 / 帮助 | show version / help |
|
||||
|
||||
> 命令正文为**小写**;`help` 输出里每条命令后跟该措辞。
|
||||
|
||||
## 3. Help 排版规范
|
||||
|
||||
`pmm help` 输出遵循固定排版:
|
||||
|
||||
```
|
||||
PMM 0.3.5 (amd64)
|
||||
|
||||
usage:
|
||||
pmm <command> <args...>
|
||||
|
||||
commands:
|
||||
list - 根据名称列出软件包
|
||||
info - 根据名称列出软件包的详细信息
|
||||
...
|
||||
|
||||
options:
|
||||
-p<drive> install under <DRIVE>:\.pmm
|
||||
--no-color omit ANSI colours on a terminal
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- **第一行**必须是 banner(见 §1),后接空行。
|
||||
- `commands:` 段落:每行 ` <command> - <描述>`,命令与描述间**对齐**(列宽 ≥ 22)。
|
||||
- `options:` 段落:每行 ` <flag> <解释>`,flag 与解释间对齐。
|
||||
- 描述文本可含中英文(由语言包决定),但**排版(空格/对齐)不随语言变化**。
|
||||
- 帮助靠**语言包**渲染;无语言包时回退内置中文。
|
||||
|
||||
## 4. CHANGELOG 格式
|
||||
|
||||
`CHANGELOG/<版本>.md`,每版本一个文件,模板:
|
||||
|
||||
```md
|
||||
# PMM v<版本>
|
||||
|
||||
## 新增
|
||||
- <功能>
|
||||
|
||||
## 修复
|
||||
- <bug>
|
||||
|
||||
## 变更
|
||||
- <行为变化>
|
||||
|
||||
## 说明
|
||||
- <注意事项>
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 文件名为 `CHANGELOG/<版本>.md`(如 `CHANGELOG/0.3.5.md`)。
|
||||
- 章节顺序固定:`## 新增` → `## 修复` → `## 变更` → `## 说明`;无内容的章节可省略。
|
||||
- 每条款以 `- ` 开头,动词开头,**不含**句号。
|
||||
- 版本号与 `PMM_VERSION` 一致。
|
||||
|
||||
## 5. `.pjson` 语言包键命名规范
|
||||
|
||||
语言包为扁平 JSON,一个文件一种语言,后缀 `.pjson`:
|
||||
|
||||
```
|
||||
web/mirror/lang/<locale>.pjson
|
||||
```
|
||||
|
||||
`<locale>` 采用 BCP-47:`zh-CN` / `en-US` / `zh-TW` / `ja-JP` …
|
||||
|
||||
```
|
||||
{
|
||||
"help.banner": "PMM {ver} ({arch})",
|
||||
"cmd.list": "list",
|
||||
"cmd.info": "info",
|
||||
"desc.list": "根据名称列出软件包",
|
||||
"msg.installed": "已安装 {name}",
|
||||
"msg.err.notfound": "未找到软件包 {name}"
|
||||
}
|
||||
```
|
||||
|
||||
键命名规则:**`<区域>.<语义>`**,用`.`分隔。
|
||||
|
||||
| 前缀 | 含义 |
|
||||
|---|---|
|
||||
| `help.` | 帮助 / banner |
|
||||
| `cmd.` | 命令名本身 |
|
||||
| `desc.` | 命令描述 |
|
||||
| `msg.` | 运行期消息 |
|
||||
| `msg.err.` | 错误消息 |
|
||||
| `msg.warn.` | 警告消息 |
|
||||
| `opt.` | 选项说明 |
|
||||
| `cfg.` | 配置项名 |
|
||||
|
||||
占位符用 `{name}`、`{ver}`、`{arch}` 等(花括号 + 小写字段名),渲染时替换。
|
||||
键**不得**含空格 / 大写,语义词用小写连字符(如 `msg.err.not-found`)。
|
||||
|
||||
## 6. 提交 / PR 约定
|
||||
|
||||
- 每个新功能或修复:`CHANGELOG/` 新增对应版本文件(仅当进入发布版本时)。
|
||||
- 改 `src/` 下任何用户可见字符串时,**同步更新** `web/mirror/lang/zh-CN.pjson` 与 `en-US.pjson` 对应键。
|
||||
- 新增 `.pjson` 键必须同时出现在 `zh-CN`、`en-US`(中文为基准,英文为对齐)。
|
||||
- 提交信息:`type(scope): description`,`type` ∈ `feature|fix|docs|i18n|ci|refactor`。
|
||||
|
||||
---
|
||||
|
||||
## 本项目的落地版本
|
||||
|
||||
- `src/main.c` 的 `print_help()` 与 `pmm version` 输出严格遵循 §1、§3。
|
||||
- 翻译字符串通过 `pmm_tr(key)` / `pmm_tr_fmt(key, ...)` 取用,见 `src/i18n.h`。
|
||||
- 版本常量:`PMM_VERSION`(构建时 `-DPMM_VERSION="..."` 注入),默认 `0.1.0`。
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/* i18n.c - .pjson language-pack (translation) support */
|
||||
#include "i18n.h"
|
||||
#include "json.h"
|
||||
#include "pmm.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
static JsonValue *g_lang = NULL;
|
||||
static char g_locale[64] = "";
|
||||
|
||||
/* Detect locale from LANG / LC_ALL env, else default "zh-CN". */
|
||||
static void detect_locale(void) {
|
||||
const char *lc = getenv("LC_ALL");
|
||||
if (!lc || !*lc) lc = getenv("LANG");
|
||||
if (!lc || !*lc) lc = "zh-CN";
|
||||
/* Normalise: 'en_US.UTF-8' -> 'en-US' */
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%s", lc);
|
||||
char *dot = strchr(buf, '.');
|
||||
if (dot) *dot = '\0';
|
||||
char *underscore = strchr(buf, '_');
|
||||
if (underscore) *underscore = '-';
|
||||
if (g_locale[0] == '\0') snprintf(g_locale, sizeof(g_locale), "%s", buf);
|
||||
}
|
||||
|
||||
void pmm_lang_set_locale(const char *locale) {
|
||||
if (!locale || !*locale) { g_locale[0] = '\0'; return; }
|
||||
snprintf(g_locale, sizeof(g_locale), "%s", locale);
|
||||
}
|
||||
|
||||
const char *pmm_lang_locale(void) {
|
||||
if (g_locale[0] == '\0') detect_locale();
|
||||
return g_locale;
|
||||
}
|
||||
|
||||
int pmm_lang_load(const char *path) {
|
||||
JsonValue *root = json_parse_file(path);
|
||||
if (!root) return -1;
|
||||
if (g_lang) json_free(g_lang);
|
||||
g_lang = root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pmm_lang_load_active(void) {
|
||||
char dir[1024];
|
||||
pmm_config_dir(dir, sizeof(dir));
|
||||
char path[1400];
|
||||
snprintf(path, sizeof(path), "%s/lang/%s.pjson", dir, pmm_lang_locale());
|
||||
if (pmm_lang_load(path) == 0) return 0;
|
||||
return 1; /* no pack on disk */
|
||||
}
|
||||
|
||||
const char *pmm_tr(const char *key) {
|
||||
if (!g_lang) return key;
|
||||
const char *t = json_str(g_lang, key);
|
||||
return t ? t : key;
|
||||
}
|
||||
|
||||
const char *pmm_tr_fmt(const char *key, ...) {
|
||||
/* pmm_tr returns a pointer into the parsed JSON (or the key). We safe-copy
|
||||
* the format string into a static buffer, then vsnprintf + args. */
|
||||
static char out[2048];
|
||||
const char *fmt = pmm_tr(key);
|
||||
/* copy fmt into a local buffer so the static out doesn't alias it */
|
||||
char fmtbuf[2048];
|
||||
snprintf(fmtbuf, sizeof(fmtbuf), "%s", fmt);
|
||||
va_list ap; va_start(ap, key);
|
||||
vsnprintf(out, sizeof(out), fmtbuf, ap);
|
||||
va_end(ap);
|
||||
return out;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/* i18n.h - .pjson language-pack (translation) support
|
||||
*
|
||||
* PMM loads a flattened key->value JSON file (".pjson") for the current locale
|
||||
* and resolves user-facing strings through pmm_tr()/pmm_tr_fmt(). Keys follow
|
||||
* docs/STANDARD.md ("区域.语义", e.g. "msg.installed"). Falls back to the key
|
||||
* itself when a translation is absent.
|
||||
*/
|
||||
#ifndef PMM_I18N_H
|
||||
#define PMM_I18N_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Set the active locale (e.g. "zh-CN"). If non-NULL/empty it overrides the
|
||||
* locale auto-detected from LANG/LC_ALL. Copies the value. */
|
||||
void pmm_lang_set_locale(const char *locale);
|
||||
|
||||
/* Current locale name (static buffer, e.g. "zh-CN"). */
|
||||
const char *pmm_lang_locale(void);
|
||||
|
||||
/* Load a .pjson file (flattened key->value object) into the active translation
|
||||
* table. Returns 0 on success. On failure the previous table stays. */
|
||||
int pmm_lang_load(const char *path);
|
||||
|
||||
/* Load the active locale's pack from ~/.pmm/lang/<locale>.pjson, if present.
|
||||
* Returns 0 if a pack was loaded, 1 if not found (no translation available). */
|
||||
int pmm_lang_load_active(void);
|
||||
|
||||
/* Translate a key to the current locale's text; returns key if unknown/missing.
|
||||
* The returned pointer is valid until the next pmm_lang_load*(). */
|
||||
const char *pmm_tr(const char *key);
|
||||
|
||||
/* Translate + format: pmm_tr_fmt("msg.err.not-found", name) -> formatted text.
|
||||
* Uses pmm_tr() then printf-style %s/%d replacement. */
|
||||
const char *pmm_tr_fmt(const char *key, ...);
|
||||
|
||||
#endif
|
||||
+107
-2
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "pmm.h"
|
||||
#include "out.h"
|
||||
#include "i18n.h"
|
||||
#include "json.h"
|
||||
#include "ini.h"
|
||||
#include "http.h"
|
||||
@@ -50,6 +51,7 @@
|
||||
typedef struct {
|
||||
char *registry_url; /* package registry base URL */
|
||||
char *mirror_name; /* active mirror name ("" = none) */
|
||||
char *language; /* active locale / language pack ("" = default) */
|
||||
} PmmConfig;
|
||||
|
||||
typedef struct {
|
||||
@@ -74,6 +76,8 @@ static void load_config(PmmConfig *cfg) {
|
||||
if (u) cfg->registry_url = xstrdup(u);
|
||||
const char *m = json_str(root, "mirror");
|
||||
if (m) cfg->mirror_name = xstrdup(m);
|
||||
const char *l = json_str(root, "language");
|
||||
if (l) cfg->language = xstrdup(l);
|
||||
json_free(root);
|
||||
}
|
||||
} else {
|
||||
@@ -85,6 +89,9 @@ static void load_config(PmmConfig *cfg) {
|
||||
const char *m = ini_get(ini, NULL, "mirror");
|
||||
if (!m) m = ini_get(ini, "pmm", "mirror");
|
||||
if (m) cfg->mirror_name = xstrdup(m);
|
||||
const char *l = ini_get(ini, NULL, "language");
|
||||
if (!l) l = ini_get(ini, "pmm", "language");
|
||||
if (l) cfg->language = xstrdup(l);
|
||||
ini_free(ini);
|
||||
}
|
||||
}
|
||||
@@ -736,7 +743,7 @@ static int cmd_mirror(int argc, char **argv) {
|
||||
}
|
||||
|
||||
static void print_help(void) {
|
||||
printf("ParlzPackageManger (PMM) v%s\n\n", PMM_VERSION);
|
||||
printf("PMM %s (%s)\n\n", PMM_VERSION, pmm_detect_arch());
|
||||
printf("usage:\n");
|
||||
printf(" pmm install <pkg> install from registry mirrors (apt-style)\n");
|
||||
printf(" pmm install <file.pdm> install a local .pdm package\n");
|
||||
@@ -774,6 +781,95 @@ static void print_help(void) {
|
||||
printf("asset mapping: windows=exe/msi/zip/7z linux=deb/rpm/appimage/tar.* macos=dmg/pkg\n");
|
||||
}
|
||||
|
||||
/* Make every directory component of `path` using PMM_MKDIR (mkdir -p). */
|
||||
static void mkdir_p_local(char *path) {
|
||||
for (char *p = path + 1; *p; p++)
|
||||
if (*p == '/' || *p == '\\') { char c = *p; *p = '\0'; PMM_MKDIR(path); *p = c; }
|
||||
PMM_MKDIR(path);
|
||||
}
|
||||
|
||||
/* pmm setting lang ... */
|
||||
static int cmd_setting(int argc, char **argv) {
|
||||
if (argc < 1 || strcmp(argv[0], "lang") != 0) {
|
||||
pmm_error("usage: pmm setting lang -l | <locale>\n");
|
||||
return 1;
|
||||
}
|
||||
char dir[1024];
|
||||
pmm_config_dir(dir, sizeof(dir));
|
||||
char langdir[1200];
|
||||
snprintf(langdir, sizeof(langdir), "%s/lang", dir);
|
||||
|
||||
/* -l : list installed language packs */
|
||||
if (argc == 2 && strcmp(argv[1], "-l") == 0) {
|
||||
printf("language packs in %s:\n", langdir);
|
||||
#ifdef _WIN32
|
||||
char pat[1300]; snprintf(pat, sizeof(pat), "%s\\*.pjson", langdir);
|
||||
WIN32_FIND_DATAA fd; HANDLE h = FindFirstFileA(pat, &fd);
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
do { printf(" %s\n", fd.cFileName); } while (FindNextFileA(h, &fd));
|
||||
FindClose(h);
|
||||
}
|
||||
#else
|
||||
DIR *dd = opendir(langdir);
|
||||
if (dd) {
|
||||
struct dirent *ee;
|
||||
while ((ee = readdir(dd)) != NULL) {
|
||||
size_t ln = strlen(ee->d_name);
|
||||
if (ln > 6 && strcmp(ee->d_name + ln - 6, ".pjson") == 0) printf(" %s\n", ee->d_name);
|
||||
}
|
||||
closedir(dd);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* install / activate a specific locale: requires a value */
|
||||
if (argc < 2) { pmm_error("usage: pmm setting lang <locale>\n"); return 1; }
|
||||
const char *loc = argv[1];
|
||||
if (strchr(loc, '/') || strchr(loc, '\\') || strstr(loc, "..")) { pmm_error("invalid locale: %s\n", loc); return 1; }
|
||||
|
||||
/* pick a registry base for the language pack */
|
||||
MirrorList *ml = mirrors_load();
|
||||
const char *base = NULL;
|
||||
for (int i = 0; i < ml->count; i++)
|
||||
if (ml->items[i].registry && *ml->items[i].registry) { base = ml->items[i].registry; break; }
|
||||
if (!base) { pmm_error("no registry mirror configured\n"); mirrors_free(ml); return 1; }
|
||||
|
||||
char url[2048];
|
||||
snprintf(url, sizeof(url), "%s/lang/%s.pjson", base, loc);
|
||||
mkdir_p_local(langdir);
|
||||
char out[1400];
|
||||
snprintf(out, sizeof(out), "%s/%s.pjson", langdir, loc);
|
||||
if (http_download(url, out) != 0) {
|
||||
pmm_error("failed to download language pack %s\n", loc);
|
||||
mirrors_free(ml); return 1;
|
||||
}
|
||||
mirrors_free(ml);
|
||||
|
||||
/* persist language=<loc> in pmm.conf (always-writable text) */
|
||||
char cfgpath[1200];
|
||||
snprintf(cfgpath, sizeof(cfgpath), "%s/pmm.conf", dir);
|
||||
char lines[256][1024]; int nn = 0;
|
||||
FILE *rf = fopen(cfgpath, "r");
|
||||
if (rf) {
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), rf) && nn < 256) {
|
||||
if (strncmp(line, "language", 8) != 0) strncpy(lines[nn++], line, sizeof(lines[0]) - 1);
|
||||
}
|
||||
fclose(rf);
|
||||
}
|
||||
FILE *wf = fopen(cfgpath, "w");
|
||||
if (!wf) { pmm_error("cannot write %s\n", cfgpath); return 1; }
|
||||
for (int i = 0; i < nn; i++) fputs(lines[i], wf);
|
||||
fprintf(wf, "language = %s\n", loc);
|
||||
fclose(wf);
|
||||
|
||||
pmm_lang_set_locale(loc);
|
||||
pmm_lang_load(out);
|
||||
pmm_success("language set to '%s'\n", loc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse "-p<drive>" / "-p <drive>" flags (e.g. -pd -> D:\.pmm) anywhere in
|
||||
* argv, record the drive, and compact the argument list so subcommands don't
|
||||
* see the flag. Returns new argc. */
|
||||
@@ -862,15 +958,24 @@ int main(int argc, char **argv) {
|
||||
argc = consume_global_flags(argc, argv);
|
||||
if (argc < 2) { print_help(); return 0; }
|
||||
|
||||
/* initialise i18n: use configured language (if any), then load its pack */
|
||||
{
|
||||
PmmConfig cfg; load_config(&cfg);
|
||||
if (cfg.language && *cfg.language) pmm_lang_set_locale(cfg.language);
|
||||
free(cfg.registry_url); free(cfg.mirror_name); free(cfg.language);
|
||||
pmm_lang_load_active();
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
|
||||
print_help(); return 0;
|
||||
}
|
||||
if (strcmp(argv[1], "version") == 0 || strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) {
|
||||
pmm_info("pmm %s (%s) [%s]\n", PMM_VERSION, pmm_os_name(pmm_detect_os()), pmm_detect_arch());
|
||||
pmm_info("PMM %s (%s)\n", PMM_VERSION, pmm_detect_arch());
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[1], "list") == 0) { pdm_list_installed(); return 0; }
|
||||
if (strcmp(argv[1], "mirror") == 0) return cmd_mirror(argc - 2, argv + 2);
|
||||
if (strcmp(argv[1], "setting") == 0) return cmd_setting(argc - 2, argv + 2);
|
||||
/* pmm remove <pkg> (also used by the Windows "Uninstall" registration) */
|
||||
if (strcmp(argv[1], "remove") == 0) {
|
||||
if (argc < 3) { pmm_error("usage: pmm remove <pkg>\n"); return 1; }
|
||||
|
||||
@@ -13,6 +13,7 @@ function pmm_nav_links(string $active): string {
|
||||
'install' => ['install.php', '安装'],
|
||||
'download' => ['download.php', '下载'],
|
||||
'status' => ['servers.php', '服务状态'],
|
||||
'translate'=> ['translate.php', '翻译'],
|
||||
'source' => ['git.php', '源码'],
|
||||
];
|
||||
$html = '';
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"help.banner": "PMM {ver} ({arch})",
|
||||
"help.usage": "usage:",
|
||||
"help.commands": "commands:",
|
||||
"help.options": "options:",
|
||||
"help.common": "common commands:",
|
||||
"cmd.list": "list",
|
||||
"cmd.info": "info",
|
||||
"cmd.search": "search",
|
||||
"cmd.install": "install",
|
||||
"cmd.remove": "remove",
|
||||
"cmd.update": "update",
|
||||
"cmd.upgrade": "upgrade",
|
||||
"cmd.mirror": "mirror",
|
||||
"cmd.self-update": "self-update",
|
||||
"cmd.version": "version",
|
||||
"cmd.help": "help",
|
||||
"desc.list": "list packages by name",
|
||||
"desc.info": "show detailed info for a package",
|
||||
"desc.search": "search packages by keyword",
|
||||
"desc.install": "install a package by name",
|
||||
"desc.remove": "uninstall a package by name",
|
||||
"desc.update": "refresh the registry index (apt-style)",
|
||||
"desc.upgrade": "upgrade all installed packages",
|
||||
"desc.mirror": "list / manage mirrors",
|
||||
"desc.self-update": "update the pmm tool itself",
|
||||
"desc.version": "show version",
|
||||
"desc.help": "show help",
|
||||
"opt.p-drive": "install under <DRIVE>:\\\\.pmm",
|
||||
"opt.no-color": "omit ANSI colours on a terminal (PMM_NO_COLOR=1 too)",
|
||||
"opt.quiet": "only print errors/warnings (suppress info/success)",
|
||||
"opt.verbose": "print extra detail",
|
||||
"msg.looking-up": "looking up {name} in mirror {mirror}",
|
||||
"msg.selected": "selected {name}@{version} (os={os} arch={arch})",
|
||||
"msg.downloading": "downloading {url}",
|
||||
"msg.downloaded": "downloaded {path}",
|
||||
"msg.sha256": "sha256: {hex}",
|
||||
"msg.sha1": "sha1: {hex}",
|
||||
"msg.installing": "installing {name} ...",
|
||||
"msg.installed": "installed {name}",
|
||||
"msg.removed": "removed {name} ({count} files)",
|
||||
"msg.checksum-ok": "{algo} checksum OK: {hex}",
|
||||
"msg.checksum-mismatch": "CHECKSUM MISMATCH ({algo})!\n expected: {expected}\n actual: {actual}",
|
||||
"msg.registry-ok": "registry sha256 OK: {hex}",
|
||||
"msg.registry-not-found": "package '{name}' not found in any registry mirror",
|
||||
"msg.no-version": "no version of '{name}' for os={os} arch={arch} satisfying '{spec}'",
|
||||
"msg.up-to-date": "{name} {cur} is up to date (latest {latest})",
|
||||
"msg.upgrading": "{name}: {cur} -> {latest}",
|
||||
"msg.upgraded": "upgraded {count} package(s).",
|
||||
"msg.no-installed": "no installed packages.",
|
||||
"msg.registry-updated": "updated registry index: {ok}/{total} packages cached.",
|
||||
"msg.skipping-no-registry": "skipping {name}: no registry entry",
|
||||
"msg.err.not-found": "package '{name}' not found",
|
||||
"msg.err.usage": "usage: {usage}",
|
||||
"msg.err.unknown-cmd": "unknown command '{cmd}' (try 'pmm help')",
|
||||
"msg.err.failed-install": "failed to install {name}",
|
||||
"msg.err.failed-remove": "failed to uninstall {name}",
|
||||
"msg.err.no-registry-mirror": "no registry mirror configured. Add to ~/.pmm/mirror.ini",
|
||||
"msg.err.cannot-write": "cannot write {path}",
|
||||
"msg.err.cannot-open": "cannot open file: {path}",
|
||||
"msg.err.empty-path": "empty file path",
|
||||
"msg.err.invalid-locale": "invalid language pack: {locale}",
|
||||
"msg.err.download-failed": "failed to download language pack {locale}",
|
||||
"msg.lang-set": "language set to '{locale}'",
|
||||
"msg.api-unreachable": "could not reach a known release API for {repo}",
|
||||
"msg.no-suitable-asset": "no suitable {os} asset found in latest release of {repo}",
|
||||
"msg.retrying-asset": "retrying with {name} ({os})",
|
||||
"mirror.sources": "mirrors:",
|
||||
"mirror.checking": "checking registry mirrors...",
|
||||
"mirror.result": "result: {ok}/{total} sources reachable",
|
||||
"lang.packs-in": "language packs in {dir}:"
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"help.banner": "PMM {ver} ({arch})",
|
||||
"help.usage": "用法:",
|
||||
"help.commands": "命令:",
|
||||
"help.options": "选项:",
|
||||
"help.common": "常用命令:",
|
||||
"cmd.list": "list",
|
||||
"cmd.info": "info",
|
||||
"cmd.search": "search",
|
||||
"cmd.install": "install",
|
||||
"cmd.remove": "remove",
|
||||
"cmd.update": "update",
|
||||
"cmd.upgrade": "upgrade",
|
||||
"cmd.mirror": "mirror",
|
||||
"cmd.self-update": "self-update",
|
||||
"cmd.version": "version",
|
||||
"cmd.help": "help",
|
||||
"desc.list": "根据名称列出软件包",
|
||||
"desc.info": "根据名称列出软件包的详细信息",
|
||||
"desc.search": "根据关键词搜索软件包",
|
||||
"desc.install": "根据名称安装指定软件包",
|
||||
"desc.remove": "根据名称卸载指定软件包",
|
||||
"desc.update": "刷新镜像源索引(apt 风格)",
|
||||
"desc.upgrade": "升级已安装的软件包",
|
||||
"desc.mirror": "列出 / 管理镜像源",
|
||||
"desc.self-update": "更新 PMM 工具本身",
|
||||
"desc.version": "查看版本",
|
||||
"desc.help": "查看帮助",
|
||||
"opt.p-drive": "安装到 <DRIVE>:\\\\.pmm",
|
||||
"opt.no-color": "在终端上省略 ANSI 颜色(PMM_NO_COLOR=1 也可)",
|
||||
"opt.quiet": "只打印错误 / 警告(抑制 info / success)",
|
||||
"opt.verbose": "打印更多细节",
|
||||
"msg.looking-up": "在镜像 {mirror} 中查找 {name}",
|
||||
"msg.selected": "已选择 {name}@{version} (os={os} arch={arch})",
|
||||
"msg.downloading": "下载 {url}",
|
||||
"msg.downloaded": "已下载 {path}",
|
||||
"msg.sha256": "sha256: {hex}",
|
||||
"msg.sha1": "sha1: {hex}",
|
||||
"msg.installing": "正在安装 {name} ...",
|
||||
"msg.installed": "已安装 {name}",
|
||||
"msg.removed": "已卸载 {name} ({count} 个文件)",
|
||||
"msg.checksum-ok": "{algo} 校验 OK: {hex}",
|
||||
"msg.checksum-mismatch": "校验不匹配 ({algo})!\n 期望: {expected}\n 实际: {actual}",
|
||||
"msg.registry-ok": "注册表 sha256 OK: {hex}",
|
||||
"msg.registry-not-found": "在任一注册表镜像中找不到软件包 '{name}'",
|
||||
"msg.no-version": "没有满足 '{spec}' 的 {name} 版本 (os={os} arch={arch})",
|
||||
"msg.up-to-date": "{name} {cur} 已是最新 (最新 {latest})",
|
||||
"msg.upgrading": "{name}: {cur} -> {latest}",
|
||||
"msg.upgraded": "已升级 {count} 个软件包。",
|
||||
"msg.no-installed": "没有已安装的软件包。",
|
||||
"msg.registry-updated": "注册表索引已更新: {ok}/{total} 个软件包已缓存。",
|
||||
"msg.skipping-no-registry": "跳过 {name}: 没有注册表条目",
|
||||
"msg.err.not-found": "找不到软件包 '{name}'",
|
||||
"msg.err.usage": "用法: {usage}",
|
||||
"msg.err.unknown-cmd": "未知命令 '{cmd}'(试试 'pmm help')",
|
||||
"msg.err.failed-install": "安装 {name} 失败",
|
||||
"msg.err.failed-remove": "卸载 {name} 失败",
|
||||
"msg.err.no-registry-mirror": "没有配置注册表镜像。请添加到 ~/.pmm/mirror.ini",
|
||||
"msg.err.cannot-write": "无法写入 {path}",
|
||||
"msg.err.cannot-open": "无法打开文件: {path}",
|
||||
"msg.err.empty-path": "文件路径为空",
|
||||
"msg.err.invalid-locale": "无效的语言: {locale}",
|
||||
"msg.err.download-failed": "下载语言包 {locale} 失败",
|
||||
"msg.lang-set": "语言已设为 '{locale}'",
|
||||
"msg.api-unreachable": "无法访问已知的发布 API: {repo}",
|
||||
"msg.no-suitable-asset": "在最新的 {repo} 发布中没找到合适的 {os} 资源",
|
||||
"msg.retrying-asset": "改用 {name} ({os}) 重试",
|
||||
"mirror.sources": "镜像源:",
|
||||
"mirror.checking": "正在检查注册表镜像源...",
|
||||
"mirror.result": "结果: {ok}/{total} 个镜像源可达",
|
||||
"lang.packs-in": "{dir} 中的语言包:"
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
define('PMM_SITE', 1);
|
||||
require __DIR__ . '/_common.php';
|
||||
|
||||
/* List available .pjson language packs under web/mirror/lang/. */
|
||||
$langDir = __DIR__ . '/mirror/lang';
|
||||
$packs = [];
|
||||
if (is_dir($langDir)) {
|
||||
foreach (glob($langDir . '/*.pjson') as $f) {
|
||||
$locale = basename($f, '.pjson');
|
||||
$keys = 0;
|
||||
$data = json_decode((string)file_get_contents($f), true);
|
||||
if (is_array($data)) $keys = count($data);
|
||||
$packs[] = ['locale' => $locale, 'keys' => $keys, 'file' => 'mirror/lang/' . basename($f)];
|
||||
}
|
||||
}
|
||||
usort($packs, function ($a, $b) { return strcmp($a['locale'], $b['locale']); });
|
||||
|
||||
pmm_header('translate', '翻译 · Translate');
|
||||
pmm_page_open('TRANSLATE', '翻译 / Language');
|
||||
?>
|
||||
<section class="section"><div class="wrap">
|
||||
<p>PMM 的界面文案由 <code>.pjson</code> 语言包驱动。默认内置 <b>zh-CN</b> 与 <b>en-US</b>,社区可以提交新的语言包或修正译文。</p>
|
||||
|
||||
<table class="dl-card" style="width:100%; border-collapse:collapse; line-height:1.8;">
|
||||
<thead><tr><th style="text-align:left;">Locale</th><th style="text-align:left;">键数</th><th style="text-align:left;">下载</th></tr></thead>
|
||||
<tbody>
|
||||
<?php if (!$packs): ?>
|
||||
<tr><td colspan="3">(暂无语言包)</td></tr>
|
||||
<?php else: foreach ($packs as $p): ?>
|
||||
<tr>
|
||||
<td><code><?php echo htmlspecialchars($p['locale']); ?></code></td>
|
||||
<td><?php echo (int)$p['keys']; ?></td>
|
||||
<td><a class="copy-btn" href="<?php echo htmlspecialchars($p['file']); ?>" download>下载 .pjson ↓</a></td>
|
||||
</tr>
|
||||
<?php endforeach; endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>如何贡献翻译</h2>
|
||||
<p>复制 <code>zh-CN.pjson</code> 为你的语言(如 <code>ja-JP.pjson</code>),键名保持不变、只改值,然后提交到仓库的
|
||||
<code>web/mirror/lang/</code> 目录,或在 GitHub 提交 Issue / PR。社区翻译会即时更新镜像,用户即可通过
|
||||
<code>pmm setting lang <locale></code> 安装。</p>
|
||||
|
||||
<p>安装语言包:</p>
|
||||
<pre><code>pmm setting lang ja-JP
|
||||
pmm setting lang -l</code></pre>
|
||||
</div></section>
|
||||
<?php pmm_footer(); ?>
|
||||
在新工单中引用
屏蔽一个用户