feat: 帮助信息中文化并把命令分组说明写直白
- 根命令增加简介与常用示例,分组标题由「disk 相关命令」改为用途说明 (如「磁盘与文件占用:查看哪个目录、哪个文件最占空间」) - cobra 固定文案改为中文:用法 / 可用的子命令 / 选项、[flags] → [选项]、 help for xxx → 显示帮助;completion 与 help 也改为中文说明 - disk usage 摘要改为「看哪个目录或文件最占空间」
This commit is contained in:
+144
-11
@@ -59,9 +59,18 @@ func (a *App) Execute(args []string) int {
|
||||
// Root builds the full cobra command tree.
|
||||
func (a *App) Root() *cobra.Command {
|
||||
root := &cobra.Command{
|
||||
Use: "kopsc",
|
||||
Short: "跨平台命令助手:用统一的意图命令在各平台执行对应命令",
|
||||
Long: "kopsc 用平台无关的意图命令(如 docker clearsafe),根据操作系统、发行版与已安装的运行时,选择并执行对应的底层命令。",
|
||||
Use: "kopsc",
|
||||
Short: "跨平台命令助手:用统一的意图命令在各平台执行对应命令",
|
||||
Long: `kopsc 用平台无关的意图命令(如 docker clearsafe),根据操作系统、发行版与已安装的运行时,
|
||||
自动选择并执行对应的底层命令。你只需记住要做什么,不必记住每个平台的写法。
|
||||
|
||||
常用示例:
|
||||
kopsc docker clearsafe 安全清理未使用的容器、网络与悬空镜像
|
||||
kopsc diagnose web 诊断容器或 Pod 为什么起不来(自动识别 Docker / K8s)
|
||||
kopsc net ports 列出本机正在监听的端口
|
||||
kopsc disk usage 看当前目录里谁最占空间
|
||||
kopsc proc nohup ./server 后台运行并写日志(等价 nohup ./server > server.out 2>&1 &)
|
||||
kopsc list 列出全部命令及在本平台实际会执行的命令`,
|
||||
Version: a.Version,
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
@@ -72,15 +81,35 @@ func (a *App) Root() *cobra.Command {
|
||||
root.SetOut(a.Stdout)
|
||||
root.SetErr(a.Stderr)
|
||||
root.SetIn(a.Stdin)
|
||||
localizeCobra(root)
|
||||
|
||||
for i := range a.Registry.Commands {
|
||||
a.addRegistryCommand(root, &a.Registry.Commands[i])
|
||||
}
|
||||
|
||||
root.AddCommand(a.newListCmd(), a.newExplainCmd(), a.newPlatformCmd(), a.newVersionCmd())
|
||||
localizeBuiltins(root)
|
||||
localizeHelpFlags(root)
|
||||
return root
|
||||
}
|
||||
|
||||
// localizeBuiltins gives cobra's generated commands Chinese descriptions.
|
||||
// cobra creates completion and help lazily, so they are initialised here
|
||||
// explicitly and then renamed.
|
||||
func localizeBuiltins(root *cobra.Command) {
|
||||
root.InitDefaultCompletionCmd()
|
||||
root.InitDefaultHelpCmd()
|
||||
|
||||
if cmd := findChild(root, "completion"); cmd != nil {
|
||||
cmd.Short = "生成 shell 补全脚本(bash / zsh / fish / powershell)"
|
||||
cmd.Long = "为 bash、zsh、fish 或 powershell 生成 kopsc 的命令补全脚本。"
|
||||
}
|
||||
if cmd := findChild(root, "help"); cmd != nil {
|
||||
cmd.Short = "查看任意命令的帮助"
|
||||
cmd.Long = "查看任意命令的帮助,例如 kopsc help disk,效果等同 kopsc disk --help。"
|
||||
}
|
||||
}
|
||||
|
||||
// addRegistryCommand turns "docker.clearsafe" into `kopsc docker clearsafe`,
|
||||
// reusing intermediate commands for shared ID prefixes.
|
||||
func (a *App) addRegistryCommand(root *cobra.Command, cmd *registry.Command) {
|
||||
@@ -90,14 +119,7 @@ func (a *App) addRegistryCommand(root *cobra.Command, cmd *registry.Command) {
|
||||
for _, group := range parts[:len(parts)-1] {
|
||||
child := findChild(parent, group)
|
||||
if child == nil {
|
||||
groupName := group
|
||||
child = &cobra.Command{
|
||||
Use: groupName,
|
||||
Short: groupName + " 相关命令",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
return c.Help()
|
||||
},
|
||||
}
|
||||
child = newGroup(group)
|
||||
parent.AddCommand(child)
|
||||
}
|
||||
parent = child
|
||||
@@ -107,6 +129,117 @@ func (a *App) addRegistryCommand(root *cobra.Command, cmd *registry.Command) {
|
||||
parent.AddCommand(leaf)
|
||||
}
|
||||
|
||||
// localizeCobra replaces cobra's built-in English strings with Chinese ones.
|
||||
//
|
||||
// cobra renders the text itself (its templates use cobra's own functions such
|
||||
// as rpad), so kopsc swaps the templates for Chinese headings and pre-registers
|
||||
// the help and version flags: cobra only generates its English descriptions
|
||||
// ("help for kopsc") for flags it has not seen yet.
|
||||
func localizeCobra(root *cobra.Command) {
|
||||
cobra.AddTemplateFunc("kopscOptions", func(line string) string {
|
||||
return strings.ReplaceAll(line, "[flags]", "[选项]")
|
||||
})
|
||||
|
||||
root.Use = "kopsc"
|
||||
root.SetUsageTemplate(usageTemplate)
|
||||
root.SetHelpTemplate(helpTemplate)
|
||||
|
||||
root.InitDefaultHelpFlag()
|
||||
root.InitDefaultVersionFlag()
|
||||
root.Flags().Lookup("help").Usage = "显示帮助"
|
||||
if flag := root.Flags().Lookup("version"); flag != nil {
|
||||
flag.Usage = "显示版本"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// localizeHelpFlags pre-registers cobra's help flag on every command with a
|
||||
// Chinese description. It must run after the command tree is complete, since
|
||||
// cobra only generates the English default for commands it has not seen.
|
||||
func localizeHelpFlags(root *cobra.Command) {
|
||||
queue := []*cobra.Command{root}
|
||||
for len(queue) > 0 {
|
||||
c := queue[0]
|
||||
queue = queue[1:]
|
||||
c.InitDefaultHelpFlag()
|
||||
if flag := c.Flags().Lookup("help"); flag != nil {
|
||||
flag.Usage = "显示帮助"
|
||||
}
|
||||
queue = append(queue, c.Commands()...)
|
||||
}
|
||||
}
|
||||
|
||||
// usageTemplate is cobra's default usage layout with Chinese headings.
|
||||
const usageTemplate = `用法:{{if .Runnable}}
|
||||
{{.UseLine | kopscOptions}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
{{.CommandPath}} [命令]{{end}}{{if gt (len .Aliases) 0}}
|
||||
|
||||
别名:
|
||||
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||
|
||||
示例:
|
||||
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
|
||||
可用的子命令:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||
|
||||
选项:
|
||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||
|
||||
全局选项:
|
||||
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
||||
|
||||
更多帮助:
|
||||
{{.CommandPath}} [命令] --help{{end}}
|
||||
`
|
||||
|
||||
// helpTemplate is cobra's default help layout.
|
||||
const helpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
|
||||
|
||||
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
||||
|
||||
// helpTextReplacements rewrites the few English fragments cobra still emits.
|
||||
var helpTextReplacements = []struct{ old, new string }{
|
||||
{"[flags]", "[选项]"},
|
||||
{"help for ", "查看 "},
|
||||
{"Generate the autocompletion script for the specified shell", "生成 shell 补全脚本(bash / zsh / fish / powershell)"},
|
||||
{"Help about any command", "查看任意命令的帮助"},
|
||||
}
|
||||
|
||||
// translateHelp applies helpTextReplacements.
|
||||
func translateHelp(text string) string {
|
||||
for _, r := range helpTextReplacements {
|
||||
text = strings.ReplaceAll(text, r.old, r.new)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// groupDescriptions explains what each command group is for. Without them
|
||||
// cobra would show "disk 相关命令", which tells a user nothing.
|
||||
var groupDescriptions = map[string]string{
|
||||
"docker": "Docker 容器与镜像管理(查看、启停、日志、清理、导出/导入、Compose)",
|
||||
"disk": "磁盘与文件占用:查看哪个目录、哪个文件最占空间",
|
||||
"net": "网络与端口:端口被谁占用、本机监听了哪些端口",
|
||||
"pkg": "系统软件包:刷新包管理器(dnf/apt/apk/brew/winget)的软件源索引",
|
||||
"proc": "本机进程:在后台运行命令并写日志,关掉终端也不退出",
|
||||
}
|
||||
|
||||
// newGroup builds an intermediate command such as `kopsc disk`.
|
||||
func newGroup(name string) *cobra.Command {
|
||||
short, ok := groupDescriptions[name]
|
||||
if !ok {
|
||||
short = name + " 相关命令"
|
||||
}
|
||||
return &cobra.Command{
|
||||
Use: name,
|
||||
Short: short,
|
||||
Long: short + "。\n\n运行 " + "kopsc " + name + " --help 查看该组的全部子命令。",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
return c.Help()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func findChild(parent *cobra.Command, name string) *cobra.Command {
|
||||
for _, child := range parent.Commands() {
|
||||
if child.Name() == name {
|
||||
|
||||
@@ -748,3 +748,55 @@ func readCalls(path string) []string {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestRootHelpIsChineseAndExplainsGroups(t *testing.T) {
|
||||
app, stdout, stderr := testApp(centos("docker"))
|
||||
if code := app.Execute(nil); code != 0 {
|
||||
t.Fatalf("exit code = %d, stderr=%s", code, stderr.String())
|
||||
}
|
||||
out := stdout.String()
|
||||
|
||||
// The framing cobra would otherwise print in English.
|
||||
for _, want := range []string{"用法:", "可用的子命令:", "选项:", "显示帮助"} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Errorf("root help missing %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
for _, unwanted := range []string{"Usage:", "Available Commands:", "Flags:", "help for ", "[flags]", "Help about any command"} {
|
||||
if strings.Contains(out, unwanted) {
|
||||
t.Errorf("root help still contains English %q:\n%s", unwanted, out)
|
||||
}
|
||||
}
|
||||
|
||||
// Every group is described in plain words instead of "<name> 相关命令".
|
||||
for _, want := range []string{
|
||||
"磁盘与文件占用",
|
||||
"网络与端口",
|
||||
"本机进程",
|
||||
"系统软件包",
|
||||
"Docker 容器与镜像管理",
|
||||
} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Errorf("root help missing group description %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
if strings.Contains(out, "相关命令") {
|
||||
t.Errorf("groups still use the vague \"相关命令\" wording:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupHelpIsChinese(t *testing.T) {
|
||||
app, stdout, stderr := testApp(centos("docker"))
|
||||
if code := app.Execute([]string{"disk"}); code != 0 {
|
||||
t.Fatalf("exit code = %d, stderr=%s", code, stderr.String())
|
||||
}
|
||||
out := stdout.String()
|
||||
for _, want := range []string{"磁盘与文件占用", "用法:", "可用的子命令:", "显示帮助", "kopsc disk --help"} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Errorf("group help missing %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
if strings.Contains(out, "help for ") {
|
||||
t.Errorf("group help still says \"help for\":\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ func Builtin() *Registry {
|
||||
ID: "disk.usage",
|
||||
Aliases: []string{"disk.du"},
|
||||
Group: "disk",
|
||||
Summary: "列出目录下各直接子项的占用空间,按大小降序",
|
||||
Summary: "看哪个目录或文件最占空间(按大小从大到小排)",
|
||||
Action: "等价于 du -x --max-depth=1 -h <路径> | sort -hr:统计每个直接子项(文件或目录递归)的占用," +
|
||||
"不跨越文件系统边界,末尾输出该路径的合计。路径可省略(默认当前目录),也可作位置参数或用 --path 指定;" +
|
||||
"由于 Windows 没有等价的 du/sort,这里使用 kopsc 内置实现以保证三平台输出一致",
|
||||
|
||||
Reference in New Issue
Block a user