mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-03 15:57:24 +00:00
Add short subcommand aliases and EN/RU CLI localization
Subcommands gain one/two-letter aliases (dl, i, e, v, h) alongside their full names. CLI-owned chrome - top-level usage, per-subcommand usage lines, progress messages, and the license prompt - now goes through internal/i18n, an env-driven message catalog (MSVC_GO_WINE_LANG, falling back to the standard LC_ALL/LC_MESSAGES/LANG locale variables) with English and Russian translations. Deeper error text from internal packages stays in English. Also refreshed the top-level usage text, which hadn't kept up with --with-wdk/--list-workloads/--list-components/--print-deps-tree.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/Cheviiot/msvc-go-wine/internal/download"
|
"github.com/Cheviiot/msvc-go-wine/internal/download"
|
||||||
|
"github.com/Cheviiot/msvc-go-wine/internal/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runDownload(args []string) int {
|
func runDownload(args []string) int {
|
||||||
@@ -79,16 +80,16 @@ func runDownload(args []string) int {
|
|||||||
if opts.HostArch == "" {
|
if opts.HostArch == "" {
|
||||||
opts.HostArch = detectHostArch()
|
opts.HostArch = detectHostArch()
|
||||||
}
|
}
|
||||||
fmt.Println("Install packages for", opts.HostArch, "host architecture")
|
fmt.Println(i18n.T("download.host_arch", opts.HostArch))
|
||||||
|
|
||||||
idx := download.BuildIndex(manifest, opts.HostArch, opts.Language)
|
idx := download.BuildIndex(manifest, opts.HostArch, opts.Language)
|
||||||
|
|
||||||
if *listWorkloads || *listComponents {
|
if *listWorkloads || *listComponents {
|
||||||
if *listWorkloads {
|
if *listWorkloads {
|
||||||
printPackageList("Workload", download.PackagesByType(idx, "Workload"), opts.Language)
|
printPackageList("download.workloads_header", download.PackagesByType(idx, "Workload"), opts.Language)
|
||||||
}
|
}
|
||||||
if *listComponents {
|
if *listComponents {
|
||||||
printPackageList("Component", download.PackagesByType(idx, "Component"), opts.Language)
|
printPackageList("download.components_header", download.PackagesByType(idx, "Component"), opts.Language)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -123,8 +124,8 @@ func runDownload(args []string) int {
|
|||||||
downloadSize += p.DownloadSize()
|
downloadSize += p.DownloadSize()
|
||||||
installSize += p.InstalledSize()
|
installSize += p.InstalledSize()
|
||||||
}
|
}
|
||||||
fmt.Printf("Selected %d packages, for a total download size of %s, install size of %s\n",
|
fmt.Print(i18n.T("download.selected",
|
||||||
len(selected), download.HumanizeBytes(downloadSize), download.HumanizeBytes(installSize))
|
len(selected), download.HumanizeBytes(downloadSize), download.HumanizeBytes(installSize)))
|
||||||
|
|
||||||
cache := *cacheDir
|
cache := *cacheDir
|
||||||
removeCache := false
|
removeCache := false
|
||||||
@@ -148,7 +149,7 @@ func runDownload(args []string) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
*dest = def
|
*dest = def
|
||||||
fmt.Println("--dest not set, using default:", *dest)
|
fmt.Println(i18n.T("download.default_dest", *dest))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := download.FetchPayloads(selected, cache, *onlyDownload); err != nil {
|
if err := download.FetchPayloads(selected, cache, *onlyDownload); err != nil {
|
||||||
@@ -207,7 +208,7 @@ func runDownload(args []string) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Done. Next: msvc-go-wine install", destAbs)
|
fmt.Println(i18n.T("download.done", destAbs))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +227,7 @@ func downloadWDK(opts *download.Options, selected []*download.Package, cache, de
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(archs) == 0 {
|
if len(archs) == 0 {
|
||||||
fmt.Println("--with-wdk: no x64/arm64 target architecture selected, skipping (no WDK package exists for x86/arm)")
|
fmt.Println(i18n.T("download.wdk_skip"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, arch := range archs {
|
for _, arch := range archs {
|
||||||
@@ -238,7 +239,7 @@ func downloadWDK(opts *download.Options, selected []*download.Package, cache, de
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("Installed WDK (%s) %s at %s\n", arch, version, wdkDir)
|
fmt.Print(i18n.T("download.wdk_installed", arch, version, wdkDir))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -254,8 +255,9 @@ func contains(list []string, v string) bool {
|
|||||||
|
|
||||||
// printPackageList prints one line per package: its ID, and (when the
|
// printPackageList prints one line per package: its ID, and (when the
|
||||||
// manifest carries one) its human-readable title in the requested language.
|
// manifest carries one) its human-readable title in the requested language.
|
||||||
func printPackageList(kind string, pkgs []*download.Package, language string) {
|
// headerKey is an i18n catalog key taking the package count as its one arg.
|
||||||
fmt.Printf("Available %ss (%d):\n", kind, len(pkgs))
|
func printPackageList(headerKey string, pkgs []*download.Package, language string) {
|
||||||
|
fmt.Print(i18n.T(headerKey, len(pkgs)))
|
||||||
for _, p := range pkgs {
|
for _, p := range pkgs {
|
||||||
if lr := p.Localized(language); lr != nil && lr.Title != "" {
|
if lr := p.Localized(language); lr != nil && lr.Title != "" {
|
||||||
fmt.Printf(" %-65s %s\n", p.ID, lr.Title)
|
fmt.Printf(" %-65s %s\n", p.ID, lr.Title)
|
||||||
@@ -273,7 +275,7 @@ func detectHostArch() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func promptAcceptLicense(license string) bool {
|
func promptAcceptLicense(license string) bool {
|
||||||
fmt.Printf("Do you accept the license at %s (yes/no)? ", license)
|
fmt.Print(i18n.T("download.license_prompt", license))
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
switch scanner.Text() {
|
switch scanner.Text() {
|
||||||
@@ -282,7 +284,7 @@ func promptAcceptLicense(license string) bool {
|
|||||||
case "no":
|
case "no":
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
fmt.Print("Do you accept the license? Answer \"yes\" or \"no\": ")
|
fmt.Print(i18n.T("download.license_reprompt"))
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Cheviiot/msvc-go-wine/internal/i18n"
|
||||||
"github.com/Cheviiot/msvc-go-wine/internal/wineenv"
|
"github.com/Cheviiot/msvc-go-wine/internal/wineenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ func runEnv(args []string) int {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
if *bin == "" {
|
if *bin == "" {
|
||||||
fmt.Fprintln(os.Stderr, "usage: msvc-go-wine env --bin <dest>/bin/<arch>")
|
fmt.Fprintln(os.Stderr, i18n.T("env.usage"))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ func runEnv(args []string) int {
|
|||||||
|
|
||||||
triple, ok := targetTriples[cfg.Arch]
|
triple, ok := targetTriples[cfg.Arch]
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Fprintf(os.Stderr, "msvc-go-wine env: unknown arch %q\n", cfg.Arch)
|
fmt.Fprint(os.Stderr, i18n.T("env.unknown_arch", cfg.Arch))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/Cheviiot/msvc-go-wine/internal/i18n"
|
||||||
"github.com/Cheviiot/msvc-go-wine/internal/install"
|
"github.com/Cheviiot/msvc-go-wine/internal/install"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runInstall(args []string) int {
|
func runInstall(args []string) int {
|
||||||
if len(args) > 1 || (len(args) == 1 && (args[0] == "-h" || args[0] == "--help")) {
|
if len(args) > 1 || (len(args) == 1 && (args[0] == "-h" || args[0] == "--help")) {
|
||||||
fmt.Fprintln(os.Stderr, "usage: msvc-go-wine install [dest] (default: ~/.msvc-go-wine)")
|
fmt.Fprintln(os.Stderr, i18n.T("install.usage"))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ func runInstall(args []string) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
dest = def
|
dest = def
|
||||||
fmt.Println("No directory given, using default:", dest)
|
fmt.Println(i18n.T("install.default_dir", dest))
|
||||||
}
|
}
|
||||||
|
|
||||||
self, err := os.Executable()
|
self, err := os.Executable()
|
||||||
@@ -36,6 +37,6 @@ func runInstall(args []string) int {
|
|||||||
fmt.Fprintln(os.Stderr, "msvc-go-wine install:", err)
|
fmt.Fprintln(os.Stderr, "msvc-go-wine install:", err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
fmt.Println("Done. Add", dest+"/bin/<arch> to PATH to use cl, link, lib, ...")
|
fmt.Println(i18n.T("install.done", dest+"/bin/<arch>"))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Cheviiot/msvc-go-wine/internal/i18n"
|
||||||
"github.com/Cheviiot/msvc-go-wine/internal/wrapper"
|
"github.com/Cheviiot/msvc-go-wine/internal/wrapper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,38 +41,25 @@ func runCLI(args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "download":
|
case "download", "dl":
|
||||||
return runDownload(args[1:])
|
return runDownload(args[1:])
|
||||||
case "install":
|
case "install", "i":
|
||||||
return runInstall(args[1:])
|
return runInstall(args[1:])
|
||||||
case "env":
|
case "env", "e":
|
||||||
return runEnv(args[1:])
|
return runEnv(args[1:])
|
||||||
case "version":
|
case "version", "v", "--version":
|
||||||
fmt.Println("msvc-go-wine " + version)
|
fmt.Println("msvc-go-wine " + version)
|
||||||
return 0
|
return 0
|
||||||
case "-h", "--help", "help":
|
case "-h", "--help", "help", "h":
|
||||||
printUsage()
|
printUsage()
|
||||||
return 0
|
return 0
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(os.Stderr, "msvc-go-wine: unknown subcommand %q\n\n", args[0])
|
fmt.Fprint(os.Stderr, i18n.T("main.unknown_subcommand", args[0]))
|
||||||
printUsage()
|
printUsage()
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printUsage() {
|
func printUsage() {
|
||||||
fmt.Fprint(os.Stderr, `msvc-go-wine - cross compile with MSVC on Linux via Wine
|
fmt.Fprint(os.Stderr, i18n.T("main.usage"))
|
||||||
|
|
||||||
Usage:
|
|
||||||
msvc-go-wine download --accept-license [--dest <dir>] [options]
|
|
||||||
fetch and unpack MSVC/WinSDK
|
|
||||||
msvc-go-wine install [dir] wire up wrappers for a downloaded MSVC
|
|
||||||
msvc-go-wine env --bin <dir/bin/arch> print INCLUDE/LIB for native clang-cl/lld-link use
|
|
||||||
msvc-go-wine version print the version
|
|
||||||
|
|
||||||
--dest/[dir] default to ~/.msvc-go-wine if omitted.
|
|
||||||
|
|
||||||
Once installed, add <dir>/bin/<arch> to PATH and invoke the tools directly:
|
|
||||||
cl, link, lib, ml, ml64, mc, midl, mt, rc, dumpbin, msbuild, nmake, armasm, armasm64, cmd, findstr
|
|
||||||
`)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
// Package i18n provides minimal message localization for msvc-go-wine's own
|
||||||
|
// CLI chrome (usage text, progress lines, prompts). It does not localize
|
||||||
|
// error messages bubbled up from deeper packages - those stay in English,
|
||||||
|
// matching how most cross-platform dev tools keep diagnostic text technical
|
||||||
|
// regardless of UI language.
|
||||||
|
package i18n
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Lang string
|
||||||
|
|
||||||
|
const (
|
||||||
|
EN Lang = "en"
|
||||||
|
RU Lang = "ru"
|
||||||
|
)
|
||||||
|
|
||||||
|
var current = detect()
|
||||||
|
|
||||||
|
// detect picks the active language from MSVC_GO_WINE_LANG (checked first, so
|
||||||
|
// it always overrides the locale), falling back to the POSIX locale
|
||||||
|
// variables in their usual priority order (LC_ALL, LC_MESSAGES, LANG). Any
|
||||||
|
// value starting with "ru" (case-insensitive) selects Russian; anything else
|
||||||
|
// falls back to English.
|
||||||
|
func detect() Lang {
|
||||||
|
for _, key := range []string{"MSVC_GO_WINE_LANG", "LC_ALL", "LC_MESSAGES", "LANG"} {
|
||||||
|
v := os.Getenv(key)
|
||||||
|
if v == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(strings.ToLower(v), "ru") {
|
||||||
|
return RU
|
||||||
|
}
|
||||||
|
return EN
|
||||||
|
}
|
||||||
|
return EN
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current returns the active language, for callers that need to branch
|
||||||
|
// beyond a simple T() lookup.
|
||||||
|
func Current() Lang { return current }
|
||||||
|
|
||||||
|
// T looks up key in the message catalog and formats it (via fmt.Sprintf)
|
||||||
|
// with args, if any. Keys with no translation for the current language fall
|
||||||
|
// back to English; keys missing from the catalog entirely are returned
|
||||||
|
// as-is, so a missing translation degrades to a visible-but-harmless string
|
||||||
|
// rather than a panic.
|
||||||
|
func T(key string, args ...any) string {
|
||||||
|
msg := key
|
||||||
|
if entry, ok := catalog[key]; ok {
|
||||||
|
if m, ok := entry[current]; ok {
|
||||||
|
msg = m
|
||||||
|
} else {
|
||||||
|
msg = entry[EN]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(args) == 0 {
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var catalog = map[string]map[Lang]string{
|
||||||
|
"main.usage": {
|
||||||
|
EN: `msvc-go-wine - cross compile with MSVC on Linux via Wine
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
msvc-go-wine download (dl) --accept-license [--dest <dir>] [options]
|
||||||
|
fetch and unpack MSVC/WinSDK/WDK
|
||||||
|
msvc-go-wine install (i) [dir] wire up wrappers for a downloaded MSVC
|
||||||
|
msvc-go-wine env (e) --bin <dir/bin/arch> print INCLUDE/LIB for native clang-cl/lld-link use
|
||||||
|
msvc-go-wine version (v) print the version
|
||||||
|
msvc-go-wine help (h) show this message
|
||||||
|
|
||||||
|
Run "msvc-go-wine <command> --help" for that command's own options - download
|
||||||
|
has many, including --with-wdk, --list-workloads, --list-components and
|
||||||
|
--print-deps-tree.
|
||||||
|
|
||||||
|
--dest/[dir] default to ~/.msvc-go-wine if omitted.
|
||||||
|
Language: set MSVC_GO_WINE_LANG=ru (or LANG=ru_RU...) for Russian output.
|
||||||
|
|
||||||
|
Once installed, add <dir>/bin/<arch> to PATH and invoke the tools directly:
|
||||||
|
cl, link, lib, ml, ml64, mc, midl, mt, rc, dumpbin, msbuild, nmake, armasm, armasm64, cmd, findstr
|
||||||
|
`,
|
||||||
|
RU: `msvc-go-wine — кросс-компиляция настоящим MSVC на Linux через Wine
|
||||||
|
|
||||||
|
Использование:
|
||||||
|
msvc-go-wine download (dl) --accept-license [--dest <каталог>] [опции]
|
||||||
|
скачать и распаковать MSVC/WinSDK/WDK
|
||||||
|
msvc-go-wine install (i) [каталог] настроить обёртки для скачанного MSVC
|
||||||
|
msvc-go-wine env (e) --bin <dir/bin/arch> вывести INCLUDE/LIB для clang-cl/lld-link напрямую
|
||||||
|
msvc-go-wine version (v) показать версию
|
||||||
|
msvc-go-wine help (h) показать эту справку
|
||||||
|
|
||||||
|
Запустите «msvc-go-wine <команда> --help» для параметров конкретной команды —
|
||||||
|
у download их много, включая --with-wdk, --list-workloads, --list-components
|
||||||
|
и --print-deps-tree.
|
||||||
|
|
||||||
|
--dest/[каталог] по умолчанию — ~/.msvc-go-wine.
|
||||||
|
Язык: установите MSVC_GO_WINE_LANG=en (или LANG=en_US...) для вывода на английском.
|
||||||
|
|
||||||
|
После установки добавьте <dir>/bin/<arch> в PATH и вызывайте инструменты напрямую:
|
||||||
|
cl, link, lib, ml, ml64, mc, midl, mt, rc, dumpbin, msbuild, nmake, armasm, armasm64, cmd, findstr
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
"main.unknown_subcommand": {
|
||||||
|
EN: "msvc-go-wine: unknown subcommand %q\n\n",
|
||||||
|
RU: "msvc-go-wine: неизвестная подкоманда %q\n\n",
|
||||||
|
},
|
||||||
|
|
||||||
|
"install.usage": {
|
||||||
|
EN: "usage: msvc-go-wine install (i) [dest] (default: ~/.msvc-go-wine)",
|
||||||
|
RU: "использование: msvc-go-wine install (i) [каталог] (по умолчанию: ~/.msvc-go-wine)",
|
||||||
|
},
|
||||||
|
"install.default_dir": {
|
||||||
|
EN: "No directory given, using default: %s",
|
||||||
|
RU: "Каталог не указан, используется значение по умолчанию: %s",
|
||||||
|
},
|
||||||
|
"install.done": {
|
||||||
|
EN: "Done. Add %s to PATH to use cl, link, lib, ...",
|
||||||
|
RU: "Готово. Добавьте %s в PATH, чтобы использовать cl, link, lib и т.д.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"env.usage": {
|
||||||
|
EN: "usage: msvc-go-wine env (e) --bin <dest>/bin/<arch>",
|
||||||
|
RU: "использование: msvc-go-wine env (e) --bin <dest>/bin/<arch>",
|
||||||
|
},
|
||||||
|
"env.unknown_arch": {
|
||||||
|
EN: "msvc-go-wine env: unknown arch %q\n",
|
||||||
|
RU: "msvc-go-wine env: неизвестная архитектура %q\n",
|
||||||
|
},
|
||||||
|
|
||||||
|
"download.host_arch": {
|
||||||
|
EN: "Install packages for %s host architecture",
|
||||||
|
RU: "Установка пакетов для архитектуры хоста %s",
|
||||||
|
},
|
||||||
|
"download.selected": {
|
||||||
|
EN: "Selected %d packages, for a total download size of %s, install size of %s\n",
|
||||||
|
RU: "Выбрано пакетов: %d, общий размер загрузки %s, размер после установки %s\n",
|
||||||
|
},
|
||||||
|
"download.default_dest": {
|
||||||
|
EN: "--dest not set, using default: %s",
|
||||||
|
RU: "--dest не указан, используется значение по умолчанию: %s",
|
||||||
|
},
|
||||||
|
"download.done": {
|
||||||
|
EN: "Done. Next: msvc-go-wine install %s",
|
||||||
|
RU: "Готово. Далее: msvc-go-wine install %s",
|
||||||
|
},
|
||||||
|
"download.wdk_skip": {
|
||||||
|
EN: "--with-wdk: no x64/arm64 target architecture selected, skipping (no WDK package exists for x86/arm)",
|
||||||
|
RU: "--with-wdk: не выбрана целевая архитектура x64/arm64, пропускаем (для x86/arm пакета WDK не существует)",
|
||||||
|
},
|
||||||
|
"download.wdk_installed": {
|
||||||
|
EN: "Installed WDK (%s) %s at %s\n",
|
||||||
|
RU: "WDK (%s) %s установлен в %s\n",
|
||||||
|
},
|
||||||
|
"download.workloads_header": {
|
||||||
|
EN: "Available Workloads (%d):\n",
|
||||||
|
RU: "Доступные рабочие нагрузки (Workload) (%d):\n",
|
||||||
|
},
|
||||||
|
"download.components_header": {
|
||||||
|
EN: "Available Components (%d):\n",
|
||||||
|
RU: "Доступные компоненты (Component) (%d):\n",
|
||||||
|
},
|
||||||
|
"download.license_prompt": {
|
||||||
|
EN: "Do you accept the license at %s (yes/no)? ",
|
||||||
|
RU: "Вы принимаете лицензию по адресу %s (yes/no)? ",
|
||||||
|
},
|
||||||
|
"download.license_reprompt": {
|
||||||
|
EN: "Do you accept the license? Answer \"yes\" or \"no\": ",
|
||||||
|
RU: "Вы принимаете лицензию? Ответьте «yes» или «no»: ",
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user