From 383aeefaf638d3e5aad98e987641f9a051c0ebf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BF=AE=E9=9B=A8?= <47820304+PeterGuy326@users.noreply.github.com> Date: Mon, 20 Apr 2026 20:51:07 +0800 Subject: [PATCH] fix(cli): localize plugin/help command strings via i18n (closes #118) (#134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire plugin module + help command + OAuth client-id/secret flags through the existing i18n catalog so --help is consistent Chinese under zh locale and English under en locale, instead of mixing the two. - internal/app/plugin_cmd.go: wrap 16 Short strings with i18n.T - internal/app/flags.go: wrap --client-id/--client-secret descriptions - internal/app/root_help.go: override cobra default help command with a localized one so "dws help --help" and the utility-commands listing share the same catalog - internal/i18n/locales/{en,zh}.json: add 20 new catalog entries Verified under DWS_LANG=zh (all Chinese), DWS_LANG=en (original English preserved), and LANG-based auto-routing. go test ./internal/app/... pass. Co-authored-by: 修雨 Co-authored-by: Claude Opus 4.7 --- internal/app/flags.go | 5 +++-- internal/app/plugin_cmd.go | 33 +++++++++++++++++---------------- internal/app/root_help.go | 21 +++++++++++++++++++++ internal/i18n/locales/en.json | 22 +++++++++++++++++++++- internal/i18n/locales/zh.json | 22 +++++++++++++++++++++- 5 files changed, 83 insertions(+), 20 deletions(-) diff --git a/internal/app/flags.go b/internal/app/flags.go index bcdc3f05..c9a15164 100644 --- a/internal/app/flags.go +++ b/internal/app/flags.go @@ -14,6 +14,7 @@ package app import ( + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/i18n" "github.com/spf13/cobra" ) @@ -35,8 +36,8 @@ type GlobalFlags struct { } func bindPersistentFlags(cmd *cobra.Command, flags *GlobalFlags) { - cmd.PersistentFlags().StringVar(&flags.ClientID, "client-id", "", "Override OAuth client ID (DingTalk AppKey)") - cmd.PersistentFlags().StringVar(&flags.ClientSecret, "client-secret", "", "Override OAuth client secret (DingTalk AppSecret)") + cmd.PersistentFlags().StringVar(&flags.ClientID, "client-id", "", i18n.T("覆盖 OAuth 客户端 ID (钉钉 AppKey)")) + cmd.PersistentFlags().StringVar(&flags.ClientSecret, "client-secret", "", i18n.T("覆盖 OAuth 客户端密钥 (钉钉 AppSecret)")) cmd.PersistentFlags().BoolVar(&flags.Debug, "debug", false, "显示调试日志") cmd.PersistentFlags().BoolVar(&flags.DryRun, "dry-run", false, "预览操作内容,不实际执行") cmd.PersistentFlags().StringVar(&flags.Fields, "fields", "", "筛选输出字段 (逗号分隔, 如: name,id,status)") diff --git a/internal/app/plugin_cmd.go b/internal/app/plugin_cmd.go index b0a2880a..36a3539b 100644 --- a/internal/app/plugin_cmd.go +++ b/internal/app/plugin_cmd.go @@ -20,13 +20,14 @@ import ( "strings" apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors" + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/i18n" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/output" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/plugin" "github.com/spf13/cobra" ) func newPluginCommand() *cobra.Command { - pluginCmd := newPlaceholderParent("plugin", "Manage plugins") + pluginCmd := newPlaceholderParent("plugin", i18n.T("插件管理")) pluginCmd.AddCommand( newPluginListCommand(), @@ -48,7 +49,7 @@ func newPluginCommand() *cobra.Command { func newPluginListCommand() *cobra.Command { cmd := &cobra.Command{ Use: "list", - Short: "List installed plugins", + Short: i18n.T("列出已安装的插件"), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { loader := plugin.NewLoader(RawVersion()) @@ -82,7 +83,7 @@ func newPluginListCommand() *cobra.Command { func newPluginInstallCommand() *cobra.Command { cmd := &cobra.Command{ Use: "install", - Short: "Install a plugin", + Short: i18n.T("安装插件"), Example: ` dws plugin install --dir ./conference dws plugin install --git https://github.com/DingTalk-Real-AI/conference.git`, DisableAutoGenTag: true, @@ -122,7 +123,7 @@ func newPluginInstallCommand() *cobra.Command { func newPluginInfoCommand() *cobra.Command { return &cobra.Command{ Use: "info ", - Short: "Show plugin details", + Short: i18n.T("查看插件详情"), Args: cobra.ExactArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -152,7 +153,7 @@ func newPluginInfoCommand() *cobra.Command { func newPluginEnableCommand() *cobra.Command { return &cobra.Command{ Use: "enable ", - Short: "Enable a plugin", + Short: i18n.T("启用插件"), Args: cobra.ExactArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -169,7 +170,7 @@ func newPluginEnableCommand() *cobra.Command { func newPluginDisableCommand() *cobra.Command { return &cobra.Command{ Use: "disable ", - Short: "Disable a plugin", + Short: i18n.T("禁用插件"), Args: cobra.ExactArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -186,7 +187,7 @@ func newPluginDisableCommand() *cobra.Command { func newPluginRemoveCommand() *cobra.Command { cmd := &cobra.Command{ Use: "remove ", - Short: "Remove an installed plugin", + Short: i18n.T("卸载已安装的插件"), Args: cobra.ExactArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -208,7 +209,7 @@ func newPluginRemoveCommand() *cobra.Command { func newPluginValidateCommand() *cobra.Command { return &cobra.Command{ Use: "validate ", - Short: "Validate a plugin.json", + Short: i18n.T("校验 plugin.json"), Args: cobra.ExactArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -229,7 +230,7 @@ func newPluginValidateCommand() *cobra.Command { func newPluginCreateCommand() *cobra.Command { cmd := &cobra.Command{ Use: "create ", - Short: "Scaffold a new plugin directory", + Short: i18n.T("脚手架生成新插件目录"), Example: ` dws plugin create my-tool dws plugin create my-tool --description "My awesome tool"`, Args: cobra.ExactArgs(1), @@ -351,7 +352,7 @@ Use this skill when the user mentions: func newPluginDevCommand() *cobra.Command { cmd := &cobra.Command{ Use: "dev ", - Short: "Register a local directory as a dev plugin", + Short: i18n.T("将本地目录注册为开发态插件"), Long: `Registers a plugin from a local source directory for development. The plugin is loaded directly from the source directory on next CLI invocation, without copying files to ~/.dws/plugins/. Use 'dws plugin dev --off ' @@ -405,7 +406,7 @@ to unregister.`, } func newPluginConfigCommand() *cobra.Command { - configCmd := newPlaceholderParent("config", "Manage plugin configuration") + configCmd := newPlaceholderParent("config", i18n.T("管理插件配置")) configCmd.AddCommand( newPluginConfigSetCommand(), newPluginConfigGetCommand(), @@ -418,7 +419,7 @@ func newPluginConfigCommand() *cobra.Command { func newPluginConfigSetCommand() *cobra.Command { return &cobra.Command{ Use: "set ", - Short: "Set a plugin config value", + Short: i18n.T("设置插件配置项"), Long: `Persistently set a configuration value for a plugin. The value is stored in ~/.dws/settings.json and automatically injected as an environment variable when the plugin is loaded. @@ -456,7 +457,7 @@ over values stored in settings.json.`, func newPluginConfigGetCommand() *cobra.Command { return &cobra.Command{ Use: "get ", - Short: "Get a plugin config value", + Short: i18n.T("读取插件配置项"), Args: cobra.ExactArgs(2), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -477,7 +478,7 @@ func newPluginConfigGetCommand() *cobra.Command { func newPluginConfigListCommand() *cobra.Command { cmd := &cobra.Command{ Use: "list ", - Short: "List all config values for a plugin", + Short: i18n.T("列出插件所有配置项"), Args: cobra.ExactArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -562,7 +563,7 @@ func newPluginConfigListCommand() *cobra.Command { func newPluginConfigUnsetCommand() *cobra.Command { return &cobra.Command{ Use: "unset ", - Short: "Remove a plugin config value", + Short: i18n.T("删除插件配置项"), Args: cobra.ExactArgs(2), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -606,7 +607,7 @@ func maskSensitiveValue(value string) string { func newPluginBuildCommand() *cobra.Command { return &cobra.Command{ Use: "build ", - Short: "Build plugin's stdio server into a native binary", + Short: i18n.T("将插件 stdio server 编译为原生二进制"), Long: `Runs the build command declared in plugin.json to compile the plugin's server into a single executable. This ensures plugin users don't need any language runtime (Node.js, Python, etc.) installed. diff --git a/internal/app/root_help.go b/internal/app/root_help.go index e55ed7f5..2380dea7 100644 --- a/internal/app/root_help.go +++ b/internal/app/root_help.go @@ -5,6 +5,7 @@ import ( "strings" "text/tabwriter" + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/i18n" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition" "github.com/spf13/cobra" ) @@ -14,6 +15,26 @@ func configureRootHelp(root *cobra.Command) { return } + // Replace the cobra-default English help command with a localized one so + // that both its listing short (shown in `dws --help`) and its own + // `dws help --help` long text follow the active locale. + root.SetHelpCommand(&cobra.Command{ + Use: "help [command]", + Short: i18n.T("查看任意命令的帮助信息"), + Long: i18n.T("显示任意命令的帮助文案。\n" + + "用法:dws help [命令路径] 查看完整说明。"), + DisableAutoGenTag: true, + Run: func(c *cobra.Command, args []string) { + target, _, err := c.Root().Find(args) + if target == nil || err != nil { + c.Root().HelpFunc()(c.Root(), args) + return + } + target.InitDefaultHelpFlag() + _ = target.Help() + }, + }) + defaultHelpFunc := root.HelpFunc() root.SetHelpFunc(func(cmd *cobra.Command, args []string) { if cmd != root { diff --git a/internal/i18n/locales/en.json b/internal/i18n/locales/en.json index 972d3433..df9e6550 100644 --- a/internal/i18n/locales/en.json +++ b/internal/i18n/locales/en.json @@ -170,5 +170,25 @@ "等待提交申请中": "Waiting to submit request", "操作超时,请重新登录": "Operation timed out, please re-login", "检查组织 CLI 授权状态...": "Checking organization CLI auth status...", - "🔐 登录钉钉": "🔐 Login to DingTalk" + "🔐 登录钉钉": "🔐 Login to DingTalk", + "插件管理": "Manage plugins", + "列出已安装的插件": "List installed plugins", + "安装插件": "Install a plugin", + "查看插件详情": "Show plugin details", + "启用插件": "Enable a plugin", + "禁用插件": "Disable a plugin", + "卸载已安装的插件": "Remove an installed plugin", + "校验 plugin.json": "Validate a plugin.json", + "脚手架生成新插件目录": "Scaffold a new plugin directory", + "将本地目录注册为开发态插件": "Register a local directory as a dev plugin", + "管理插件配置": "Manage plugin configuration", + "设置插件配置项": "Set a plugin config value", + "读取插件配置项": "Get a plugin config value", + "列出插件所有配置项": "List all config values for a plugin", + "删除插件配置项": "Remove a plugin config value", + "将插件 stdio server 编译为原生二进制": "Build plugin's stdio server into a native binary", + "覆盖 OAuth 客户端 ID (钉钉 AppKey)": "Override OAuth client ID (DingTalk AppKey)", + "覆盖 OAuth 客户端密钥 (钉钉 AppSecret)": "Override OAuth client secret (DingTalk AppSecret)", + "查看任意命令的帮助信息": "Help about any command", + "显示任意命令的帮助文案。\n用法:dws help [命令路径] 查看完整说明。": "Help provides help for any command in the application.\nSimply type dws help [path to command] for full details." } diff --git a/internal/i18n/locales/zh.json b/internal/i18n/locales/zh.json index 6143a97e..e4b933dd 100644 --- a/internal/i18n/locales/zh.json +++ b/internal/i18n/locales/zh.json @@ -170,5 +170,25 @@ "等待提交申请中": "等待提交申请中", "操作超时,请重新登录": "操作超时,请重新登录", "检查组织 CLI 授权状态...": "检查组织 CLI 授权状态...", - "🔐 登录钉钉": "🔐 登录钉钉" + "🔐 登录钉钉": "🔐 登录钉钉", + "插件管理": "插件管理", + "列出已安装的插件": "列出已安装的插件", + "安装插件": "安装插件", + "查看插件详情": "查看插件详情", + "启用插件": "启用插件", + "禁用插件": "禁用插件", + "卸载已安装的插件": "卸载已安装的插件", + "校验 plugin.json": "校验 plugin.json", + "脚手架生成新插件目录": "脚手架生成新插件目录", + "将本地目录注册为开发态插件": "将本地目录注册为开发态插件", + "管理插件配置": "管理插件配置", + "设置插件配置项": "设置插件配置项", + "读取插件配置项": "读取插件配置项", + "列出插件所有配置项": "列出插件所有配置项", + "删除插件配置项": "删除插件配置项", + "将插件 stdio server 编译为原生二进制": "将插件 stdio server 编译为原生二进制", + "覆盖 OAuth 客户端 ID (钉钉 AppKey)": "覆盖 OAuth 客户端 ID (钉钉 AppKey)", + "覆盖 OAuth 客户端密钥 (钉钉 AppSecret)": "覆盖 OAuth 客户端密钥 (钉钉 AppSecret)", + "查看任意命令的帮助信息": "查看任意命令的帮助信息", + "显示任意命令的帮助文案。\n用法:dws help [命令路径] 查看完整说明。": "显示任意命令的帮助文案。\n用法:dws help [命令路径] 查看完整说明。" }