From bf994fab173add24a386aaba46e40519f6d04bb8 Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 00:28:52 +0000 Subject: [PATCH] feat: embed CLI locale files, wire i18n ExtraFS - Added locales/en.json with 90 translation keys for doctor, pkg commands - Main() embeds CLI locales automatically - MainWithLocales() accepts additional FSSource for consuming binaries - Ecosystem packages can ship their own locale files Co-Authored-By: Virgil --- pkg/cli/app.go | 29 ++++++++- pkg/cli/locales/en.json | 135 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 pkg/cli/locales/en.json diff --git a/pkg/cli/app.go b/pkg/cli/app.go index 577c043..6dd1f50 100644 --- a/pkg/cli/app.go +++ b/pkg/cli/app.go @@ -1,7 +1,9 @@ package cli import ( + "embed" "fmt" + "io/fs" "os" "runtime/debug" @@ -11,6 +13,9 @@ import ( "github.com/spf13/cobra" ) +//go:embed locales/*.json +var cliLocaleFS embed.FS + // AppName is the default CLI application name. // Override with WithAppName before calling Main. var AppName = "core" @@ -65,7 +70,21 @@ func WithAppName(name string) { // ) // // Exits with code 1 on error or panic. +// LocaleSource pairs a filesystem with a directory for loading translations. +type LocaleSource = i18n.FSSource + +// WithLocales returns a locale source for use with MainWithLocales. +func WithLocales(fsys fs.FS, dir string) LocaleSource { + return LocaleSource{FS: fsys, Dir: dir} +} + +// Main initialises and runs the CLI with the framework's built-in translations. func Main(commands ...core.Option) { + MainWithLocales(nil, commands...) +} + +// MainWithLocales initialises and runs the CLI with additional translation sources. +func MainWithLocales(locales []LocaleSource, commands ...core.Option) { // Recovery from panics defer func() { if r := recover(); r != nil { @@ -75,9 +94,17 @@ func Main(commands ...core.Option) { } }() + // Build locale sources: framework built-in + caller's extras + extraFS := []i18n.FSSource{ + {FS: cliLocaleFS, Dir: "locales"}, + } + extraFS = append(extraFS, locales...) + // Core services load first, then command services services := []core.Option{ - core.WithName("i18n", i18n.NewCoreService(i18n.ServiceOptions{})), + core.WithName("i18n", i18n.NewCoreService(i18n.ServiceOptions{ + ExtraFS: extraFS, + })), } services = append(services, commands...) diff --git a/pkg/cli/locales/en.json b/pkg/cli/locales/en.json new file mode 100644 index 0000000..50cc311 --- /dev/null +++ b/pkg/cli/locales/en.json @@ -0,0 +1,135 @@ +{ + "cmd": { + "doctor": { + "short": "Check development environment", + "long": "Diagnose your development environment and report missing tools, configuration issues, and connectivity problems.", + "verbose_flag": "Show detailed output", + "required": "Required tools:", + "optional": "Optional tools:", + "github": "GitHub integration:", + "workspace": "Workspace:", + "ready": "Environment is ready", + "install_missing": "Install missing tools:", + "install_macos": "brew install", + "install_macos_cask": "brew install --cask", + "install_linux_header": "Install on Linux:", + "install_linux_git": "sudo apt install git", + "install_linux_node": "curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt install -y nodejs", + "install_linux_php": "sudo apt install php php-cli php-mbstring php-xml php-curl", + "install_linux_pnpm": "npm install -g pnpm", + "install_linux_gh": "See https://github.com/cli/cli/blob/trunk/docs/install_linux.md", + "install_other": "See tool documentation for installation", + "issues": "Open issues assigned to you:", + "issues_error": "Failed to fetch issues", + "cli_auth": "GitHub CLI authenticated", + "cli_auth_missing": "GitHub CLI not authenticated — run: gh auth login", + "ssh_found": "SSH key found", + "ssh_missing": "SSH key not found — run: ssh-keygen", + "repos_yaml_found": "Workspace registry found: {{.Path}}", + "repos_cloned": "{{.Cloned}}/{{.Total}} repos cloned", + "no_repos_yaml": "No repos.yaml found (run from workspace root)", + "check": { + "git": { "name": "Git", "description": "Version control" }, + "docker": { "name": "Docker", "description": "Container runtime" }, + "node": { "name": "Node.js", "description": "JavaScript runtime" }, + "php": { "name": "PHP", "description": "PHP interpreter" }, + "composer": { "name": "Composer", "description": "PHP package manager" }, + "pnpm": { "name": "pnpm", "description": "Node package manager" }, + "gh": { "name": "GitHub CLI", "description": "GitHub integration" }, + "claude": { "name": "Claude Code", "description": "AI coding assistant" } + } + }, + "pkg": { + "short": "Manage packages", + "long": "Install, list, search, update, and remove packages from the Core ecosystem.", + "no_description": "(no description)", + "error": { + "repo_required": "Repository argument required (e.g., core/go-io)", + "invalid_repo_format": "Invalid format — use org/repo (e.g., core/go-io)", + "no_repos_yaml": "No repos.yaml found", + "no_repos_yaml_workspace": "No repos.yaml found in workspace", + "specify_package": "Specify a package name", + "auth_failed": "Authentication failed", + "gh_not_authenticated": "GitHub CLI not authenticated — run: gh auth login", + "search_failed": "Search failed" + }, + "install": { + "short": "Install a package", + "long": "Clone a package from the Git forge into your workspace.", + "installing_label": "Installing", + "already_exists": "{{.Name}} already exists at {{.Path}}", + "installed": "{{.Name}} installed successfully", + "add_to_registry": "Adding to registry", + "added_to_registry": "Added to repos.yaml", + "flag": { + "dir": "Target directory (default: workspace base path)", + "add": "Add to repos.yaml registry after install" + } + }, + "list": { + "short": "List installed packages", + "long": "Show all packages registered in repos.yaml with their status.", + "title": "Installed packages", + "no_packages": "No packages found", + "summary": "{{.Count}} packages", + "install_missing": "Install missing: core pkg install" + }, + "search": { + "short": "Search available packages", + "long": "Search the forge for available packages by name or pattern.", + "fetching_label": "Searching", + "cache_label": "Cached", + "found_repos": "Found {{.Count}} repositories", + "no_repos_found": "No matching repositories found", + "private_label": "private", + "gh_token_unset": "GITHUB_TOKEN not set", + "gh_token_warning": "Set GITHUB_TOKEN for private repo access", + "flag": { + "org": "Organisation to search (default: core)", + "pattern": "Filter by name pattern", + "type": "Filter by type (package, application, template)", + "limit": "Maximum results", + "refresh": "Refresh cache" + } + }, + "update": { + "short": "Update a package", + "long": "Pull latest changes for a package or all packages.", + "updating": "Updating {{.Name}}", + "not_installed": "{{.Name}} is not installed", + "summary": "{{.Updated}}/{{.Total}} updated", + "flag": { + "all": "Update all packages" + } + }, + "outdated": { + "short": "Show outdated packages", + "long": "Check which packages have unpulled commits.", + "all_up_to_date": "All packages are up to date", + "commits_behind": "{{.Count}} commits behind", + "update_with": "Update with: core pkg update {{.Name}}", + "summary": "{{.Outdated}}/{{.Total}} outdated" + } + } + }, + "common": { + "hint": { + "install_with": "Install with: {{.Command}}" + }, + "progress": { + "checking": "Checking {{.Item}}...", + "checking_updates": "Checking for updates..." + }, + "status": { + "cloning": "Cloning", + "up_to_date": "Up to date" + } + }, + "i18n": { + "fail": { + "create": "Failed to create {{.Item}}", + "load": "Failed to load {{.Item}}", + "parse": "Failed to parse {{.Item}}" + } + } +}