refactor(i18n): use grammar engine for progress messages
- Replace cli.progress.* keys with i18n.P() dynamic generation
- Remove 7 static progress keys from en_GB.json (building, checking, etc.)
- Add additional core.* intents (format, analyse, link, unlink, fetch, etc.)
- Add grammar helpers: Progress(), ProgressSubject(), ActionResult(), Label()
- Add package-level convenience functions: P(), PS(), L()
- Update commands to use common.prompt.abort instead of cli.confirm.abort
The grammar engine now generates progress messages dynamically:
i18n.P("check") → "Checking..."
i18n.P("fetch") → "Fetching..."
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f85064a954
commit
fc74d4df9c
9 changed files with 259 additions and 29 deletions
|
|
@ -30,7 +30,7 @@ func runCIReleaseInit() error {
|
||||||
response, _ := reader.ReadString('\n')
|
response, _ := reader.ReadString('\n')
|
||||||
response = strings.TrimSpace(strings.ToLower(response))
|
response = strings.TrimSpace(strings.ToLower(response))
|
||||||
if response != "y" && response != "yes" {
|
if response != "y" && response != "yes" {
|
||||||
fmt.Println(i18n.T("cli.confirm.abort"))
|
fmt.Println(i18n.T("common.prompt.abort"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ func runCI(registryPath string, branch string, failedOnly bool) error {
|
||||||
repoList := reg.List()
|
repoList := reg.List()
|
||||||
for i, repo := range repoList {
|
for i, repo := range repoList {
|
||||||
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||||
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("cli.progress.checking")), i+1, len(repoList), repo.Name)
|
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.P("check")), i+1, len(repoList), repo.Name)
|
||||||
|
|
||||||
runs, err := fetchWorkflowRuns(repoFullName, repo.Name, branch)
|
runs, err := fetchWorkflowRuns(repoFullName, repo.Name, branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ func runIssues(registryPath string, limit int, assignee string) error {
|
||||||
repoList := reg.List()
|
repoList := reg.List()
|
||||||
for i, repo := range repoList {
|
for i, repo := range repoList {
|
||||||
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||||
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("cli.progress.fetching")), i+1, len(repoList), repo.Name)
|
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.P("fetch")), i+1, len(repoList), repo.Name)
|
||||||
|
|
||||||
issues, err := fetchIssues(repoFullName, repo.Name, limit, assignee)
|
issues, err := fetchIssues(repoFullName, repo.Name, limit, assignee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ func runReviews(registryPath string, author string, showAll bool) error {
|
||||||
repoList := reg.List()
|
repoList := reg.List()
|
||||||
for i, repo := range repoList {
|
for i, repo := range repoList {
|
||||||
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||||
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("cli.progress.fetching")), i+1, len(repoList), repo.Name)
|
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.P("fetch")), i+1, len(repoList), repo.Name)
|
||||||
|
|
||||||
prs, err := fetchPRs(repoFullName, repo.Name, author)
|
prs, err := fetchPRs(repoFullName, repo.Name, author)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ func runDocsSync(registryPath string, outputDir string, dryRun bool) error {
|
||||||
// Confirm
|
// Confirm
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
if !confirm(i18n.T("cmd.docs.sync.confirm")) {
|
if !confirm(i18n.T("cmd.docs.sync.confirm")) {
|
||||||
fmt.Println(i18n.T("cli.confirm.abort"))
|
fmt.Println(i18n.T("common.prompt.abort"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -503,3 +503,71 @@ func TemplateFuncs() template.FuncMap {
|
||||||
"quote": Quote,
|
"quote": Quote,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Progress returns a progress message for a verb.
|
||||||
|
// Generates "Verbing..." form.
|
||||||
|
//
|
||||||
|
// Progress("build") // "Building..."
|
||||||
|
// Progress("check") // "Checking..."
|
||||||
|
// Progress("fetch") // "Fetching..."
|
||||||
|
func Progress(verb string) string {
|
||||||
|
g := Gerund(verb)
|
||||||
|
if g == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return Title(g) + "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgressSubject returns a progress message with a subject.
|
||||||
|
// Generates "Verbing subject..." form.
|
||||||
|
//
|
||||||
|
// ProgressSubject("build", "project") // "Building project..."
|
||||||
|
// ProgressSubject("check", "config.yaml") // "Checking config.yaml..."
|
||||||
|
func ProgressSubject(verb, subject string) string {
|
||||||
|
g := Gerund(verb)
|
||||||
|
if g == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return Title(g) + " " + subject + "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionResult returns a result message for a completed action.
|
||||||
|
// Generates "Subject verbed" form.
|
||||||
|
//
|
||||||
|
// ActionResult("delete", "file") // "File deleted"
|
||||||
|
// ActionResult("commit", "changes") // "Changes committed"
|
||||||
|
func ActionResult(verb, subject string) string {
|
||||||
|
p := PastTense(verb)
|
||||||
|
if p == "" || subject == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return Title(subject) + " " + p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionFailed returns a failure message for an action.
|
||||||
|
// Generates "Failed to verb subject" form.
|
||||||
|
//
|
||||||
|
// ActionFailed("delete", "file") // "Failed to delete file"
|
||||||
|
// ActionFailed("push", "commits") // "Failed to push commits"
|
||||||
|
func ActionFailed(verb, subject string) string {
|
||||||
|
if verb == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if subject == "" {
|
||||||
|
return "Failed to " + verb
|
||||||
|
}
|
||||||
|
return "Failed to " + verb + " " + subject
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label returns a label with a colon suffix.
|
||||||
|
// Generates "Word:" form with title case.
|
||||||
|
//
|
||||||
|
// Label("status") // "Status:"
|
||||||
|
// Label("version") // "Version:"
|
||||||
|
// Label("app url") // "App Url:"
|
||||||
|
func Label(word string) string {
|
||||||
|
if word == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return Title(word) + ":"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,35 @@ func C(intent string, subject *Subject) *Composed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Grammar convenience functions (package-level) ---
|
||||||
|
// These provide direct access to grammar functions without needing a service instance.
|
||||||
|
|
||||||
|
// P returns a progress message for a verb: "Building...", "Checking..."
|
||||||
|
// Use this instead of T("cli.progress.building") for dynamic progress messages.
|
||||||
|
//
|
||||||
|
// P("build") // "Building..."
|
||||||
|
// P("fetch") // "Fetching..."
|
||||||
|
func P(verb string) string {
|
||||||
|
return Progress(verb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PS returns a progress message with a subject: "Building project...", "Checking config..."
|
||||||
|
//
|
||||||
|
// PS("build", "project") // "Building project..."
|
||||||
|
// PS("check", "config.yaml") // "Checking config.yaml..."
|
||||||
|
func PS(verb, subject string) string {
|
||||||
|
return ProgressSubject(verb, subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
// L returns a label with colon: "Status:", "Version:"
|
||||||
|
// Use this instead of T("common.label.status") for simple labels.
|
||||||
|
//
|
||||||
|
// L("status") // "Status:"
|
||||||
|
// L("version") // "Version:"
|
||||||
|
func L(word string) string {
|
||||||
|
return Label(word)
|
||||||
|
}
|
||||||
|
|
||||||
// _ is the standard gettext-style translation helper.
|
// _ is the standard gettext-style translation helper.
|
||||||
// Alias for T() - use whichever you prefer.
|
// Alias for T() - use whichever you prefer.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -429,6 +429,140 @@ var coreIntents = map[string]Intent{
|
||||||
Success: "Confirmed",
|
Success: "Confirmed",
|
||||||
Failure: "Cancelled",
|
Failure: "Cancelled",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// --- Additional Actions ---
|
||||||
|
|
||||||
|
"core.sync": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "sync",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Sync {{.Subject}}?",
|
||||||
|
Confirm: "Sync {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} synced",
|
||||||
|
Failure: "Failed to sync {{.Subject}}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.boot": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "boot",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Boot {{.Subject}}?",
|
||||||
|
Confirm: "Boot {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} booted",
|
||||||
|
Failure: "Failed to boot {{.Subject}}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.format": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "format",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Format {{.Subject}}?",
|
||||||
|
Confirm: "Format {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} formatted",
|
||||||
|
Failure: "Failed to format {{.Subject}}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.analyse": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "analyse",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Analyse {{.Subject}}?",
|
||||||
|
Confirm: "Analyse {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} analysed",
|
||||||
|
Failure: "Failed to analyse {{.Subject}}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.link": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "link",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Link {{.Subject}}?",
|
||||||
|
Confirm: "Link {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} linked",
|
||||||
|
Failure: "Failed to link {{.Subject}}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.unlink": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "unlink",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Unlink {{.Subject}}?",
|
||||||
|
Confirm: "Unlink {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} unlinked",
|
||||||
|
Failure: "Failed to unlink {{.Subject}}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.fetch": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "fetch",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Fetch {{.Subject}}?",
|
||||||
|
Confirm: "Fetch {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} fetched",
|
||||||
|
Failure: "Failed to fetch {{.Subject}}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.generate": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "generate",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Generate {{.Subject}}?",
|
||||||
|
Confirm: "Generate {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} generated",
|
||||||
|
Failure: "Failed to generate {{.Subject}}",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.validate": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "validate",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Validate {{.Subject}}?",
|
||||||
|
Confirm: "Validate {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} valid",
|
||||||
|
Failure: "{{.Subject | title}} invalid",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.check": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "check",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Check {{.Subject}}?",
|
||||||
|
Confirm: "Check {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} OK",
|
||||||
|
Failure: "{{.Subject | title}} failed",
|
||||||
|
},
|
||||||
|
|
||||||
|
"core.scan": {
|
||||||
|
Meta: IntentMeta{
|
||||||
|
Type: "action",
|
||||||
|
Verb: "scan",
|
||||||
|
Default: "yes",
|
||||||
|
},
|
||||||
|
Question: "Scan {{.Subject}}?",
|
||||||
|
Confirm: "Scan {{.Subject}}?",
|
||||||
|
Success: "{{.Subject | title}} scanned",
|
||||||
|
Failure: "Failed to scan {{.Subject}}",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// getIntent retrieves an intent by its key from the core intents.
|
// getIntent retrieves an intent by its key from the core intents.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
|
"prompt": {
|
||||||
|
"yes": "y",
|
||||||
|
"no": "n",
|
||||||
|
"yes_full": "yes",
|
||||||
|
"no_full": "no",
|
||||||
|
"abort": "Operation aborted",
|
||||||
|
"cancel": "Cancelled",
|
||||||
|
"continue": "Continue?",
|
||||||
|
"proceed": "Proceed?",
|
||||||
|
"confirm": "Are you sure?",
|
||||||
|
"overwrite": "Overwrite?",
|
||||||
|
"delete": "Delete?",
|
||||||
|
"save": "Save?",
|
||||||
|
"discard": "Discard changes?"
|
||||||
|
},
|
||||||
"label": {
|
"label": {
|
||||||
"error": "Error:",
|
"error": "Error:",
|
||||||
"done": "Done:",
|
"done": "Done:",
|
||||||
|
|
@ -87,13 +102,6 @@
|
||||||
"aborted": "Aborted",
|
"aborted": "Aborted",
|
||||||
"cancelled": "Cancelled",
|
"cancelled": "Cancelled",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"confirm": {
|
|
||||||
"abort": "Operation aborted",
|
|
||||||
"continue": "Continue?",
|
|
||||||
"no": "No",
|
|
||||||
"proceed": "Proceed?",
|
|
||||||
"yes": "Yes"
|
|
||||||
},
|
|
||||||
"count": {
|
"count": {
|
||||||
"commits": {
|
"commits": {
|
||||||
"one": "{{.Count}} commit",
|
"one": "{{.Count}} commit",
|
||||||
|
|
@ -116,15 +124,6 @@
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"pass": "PASS",
|
"pass": "PASS",
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"progress": {
|
|
||||||
"building": "Building",
|
|
||||||
"checking": "Checking",
|
|
||||||
"deploying": "Deploying",
|
|
||||||
"fetching": "Fetching",
|
|
||||||
"loading": "Loading",
|
|
||||||
"processing": "Processing",
|
|
||||||
"testing": "Testing"
|
|
||||||
},
|
|
||||||
"skip": "Skipped",
|
"skip": "Skipped",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"time": {
|
"time": {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue