core: init i18n

parent b3fa78cf
...@@ -5,6 +5,7 @@ go 1.25.0 ...@@ -5,6 +5,7 @@ go 1.25.0
require ( require (
github.com/fatih/color v1.18.0 github.com/fatih/color v1.18.0
github.com/godbus/dbus/v5 v5.1.0 github.com/godbus/dbus/v5 v5.1.0
github.com/leonelquinteros/gotext v1.7.2
github.com/urfave/cli/v3 v3.4.1 github.com/urfave/cli/v3 v3.4.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
......
...@@ -4,6 +4,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= ...@@ -4,6 +4,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
......
...@@ -2,10 +2,12 @@ package hyprland ...@@ -2,10 +2,12 @@ package hyprland
import ( import (
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"ximperconf/ui" "ximperconf/ui"
"ximperconf/utils" "ximperconf/utils"
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
...@@ -23,7 +25,7 @@ func HyprlandFixConfigCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -23,7 +25,7 @@ func HyprlandFixConfigCommand(ctx context.Context, cmd *cli.Command) error {
manager.FixConfig() manager.FixConfig()
color.Green("Конфигурация исправлена") color.Green(locale.T("Configuration fixed"))
return nil return nil
} }
...@@ -39,7 +41,7 @@ func HyprlandCheckCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -39,7 +41,7 @@ func HyprlandCheckCommand(ctx context.Context, cmd *cli.Command) error {
} }
if len(result) == 0 { if len(result) == 0 {
color.Green("Ошибок нет") color.Green(locale.T("No errors"))
return nil return nil
} }
...@@ -58,7 +60,7 @@ func HyprlandModuleStatusCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -58,7 +60,7 @@ func HyprlandModuleStatusCommand(ctx context.Context, cmd *cli.Command) error {
info := manager.GetModuleInfo(cmd.Args().Get(0), cmd.Bool("user")) info := manager.GetModuleInfo(cmd.Args().Get(0), cmd.Bool("user"))
if !info.Available { if !info.Available {
color.Red("недопустимое название для модуля.") color.Red(locale.T("invalid module name"))
return nil return nil
} }
fmt.Println(info.Status.Label) fmt.Println(info.Status.Label)
...@@ -68,7 +70,7 @@ func HyprlandModuleStatusCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -68,7 +70,7 @@ func HyprlandModuleStatusCommand(ctx context.Context, cmd *cli.Command) error {
func HyprlandModuleCheckCommand(ctx context.Context, cmd *cli.Command) error { func HyprlandModuleCheckCommand(ctx context.Context, cmd *cli.Command) error {
if cmd.Args().Get(0) == "" { if cmd.Args().Get(0) == "" {
return fmt.Errorf("укажите модуль для проверки") return errors.New(locale.T("specify module name"))
} }
manager, err := GetHyprlandManager(ctx) manager, err := GetHyprlandManager(ctx)
...@@ -79,7 +81,7 @@ func HyprlandModuleCheckCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -79,7 +81,7 @@ func HyprlandModuleCheckCommand(ctx context.Context, cmd *cli.Command) error {
moduleinfo := manager.GetModuleInfo(cmd.Args().Get(0), cmd.Bool("user")) moduleinfo := manager.GetModuleInfo(cmd.Args().Get(0), cmd.Bool("user"))
if !moduleinfo.Available { if !moduleinfo.Available {
return fmt.Errorf("недопустимое название для модуля") return errors.New(locale.T("invalid module name"))
} }
result, err := manager.CheckModule(cmd.Args().Get(0), cmd.Bool("user")) result, err := manager.CheckModule(cmd.Args().Get(0), cmd.Bool("user"))
...@@ -89,7 +91,7 @@ func HyprlandModuleCheckCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -89,7 +91,7 @@ func HyprlandModuleCheckCommand(ctx context.Context, cmd *cli.Command) error {
} }
if len(result) == 0 { if len(result) == 0 {
color.Green("Ошибок нет") color.Green(locale.T("No errors"))
return nil return nil
} }
...@@ -182,12 +184,12 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -182,12 +184,12 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
if config.IsJSON(cmd) { if config.IsJSON(cmd) {
return ui.PrintJSON([]ModuleInfoJSON{}) return ui.PrintJSON([]ModuleInfoJSON{})
} }
return fmt.Errorf("нет доступных модулей") return errors.New(locale.T("no available modules"))
} }
type moduleData struct { type moduleData struct {
info HyprModule info HyprModule
errorNum int errorNum int
} }
data := make([]moduleData, 0, len(modules)) data := make([]moduleData, 0, len(modules))
...@@ -222,7 +224,7 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -222,7 +224,7 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
parts = append(parts, d.info.Meta.Summary) parts = append(parts, d.info.Meta.Summary)
} }
if d.errorNum > 0 { if d.errorNum > 0 {
parts = append(parts, fmt.Sprintf("(ошибки: %d)", d.errorNum)) parts = append(parts, fmt.Sprintf(locale.T("(errors: %d)"), d.errorNum))
} }
name := d.info.Name name := d.info.Name
if d.info.Meta != nil && d.info.Meta.Group != "" { if d.info.Meta != nil && d.info.Meta.Group != "" {
...@@ -236,7 +238,7 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -236,7 +238,7 @@ func HyprlandInfoModulesCommand(ctx context.Context, cmd *cli.Command) error {
} }
ui.RenderTree(ui.RenderTreeOptions{ ui.RenderTree(ui.RenderTreeOptions{
Title: "Modules", Title: locale.T("Modules"),
Items: items, Items: items,
Style: ui.DefaultTreeStyle, Style: ui.DefaultTreeStyle,
Color: true, Color: true,
...@@ -258,11 +260,11 @@ func HyprlandModuleEditCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -258,11 +260,11 @@ func HyprlandModuleEditCommand(ctx context.Context, cmd *cli.Command) error {
info := manager.GetModuleInfo(module, user) info := manager.GetModuleInfo(module, user)
if !info.Available { if !info.Available {
return fmt.Errorf("недопустимое название для модуля") return errors.New(locale.T("invalid module name"))
} }
if info.Path == "" { if info.Path == "" {
return fmt.Errorf("модуль '%s' не найден: %s", module, modulefile) return fmt.Errorf(locale.T("module '%s' not found: %s"), module, modulefile)
} }
editor := utils.GetEditor() editor := utils.GetEditor()
...@@ -274,7 +276,7 @@ func HyprlandModuleEditCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -274,7 +276,7 @@ func HyprlandModuleEditCommand(ctx context.Context, cmd *cli.Command) error {
err = editCmd.Run() err = editCmd.Run()
if err != nil { if err != nil {
return fmt.Errorf("не удалось запустить редактор: %s", err.Error()) return fmt.Errorf(locale.T("failed to start editor: %s"), err.Error())
} }
return nil return nil
...@@ -283,7 +285,7 @@ func HyprlandModuleEditCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -283,7 +285,7 @@ func HyprlandModuleEditCommand(ctx context.Context, cmd *cli.Command) error {
func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error { func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error {
module := cmd.Args().Get(0) module := cmd.Args().Get(0)
if module == "" { if module == "" {
return fmt.Errorf("укажите имя модуля") return errors.New(locale.T("specify module name"))
} }
user := cmd.Bool("user") user := cmd.Bool("user")
...@@ -294,7 +296,7 @@ func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -294,7 +296,7 @@ func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error {
info := manager.GetModuleInfo(module, user) info := manager.GetModuleInfo(module, user)
if !info.Available { if !info.Available {
return fmt.Errorf("недопустимое название для модуля") return errors.New(locale.T("invalid module name"))
} }
errors, _ := manager.CheckModule(module, user) errors, _ := manager.CheckModule(module, user)
...@@ -311,30 +313,30 @@ func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -311,30 +313,30 @@ func HyprlandModuleShowCommand(ctx context.Context, cmd *cli.Command) error {
} }
blue := color.New(color.FgBlue).SprintFunc() blue := color.New(color.FgBlue).SprintFunc()
fmt.Printf("%s: %s\n", blue("Модуль"), info.Name) fmt.Printf("%s: %s\n", blue(locale.T("Module")), info.Name)
if info.Meta != nil { if info.Meta != nil {
if info.Meta.Group != "" { if info.Meta.Group != "" {
fmt.Printf("%s: %s\n", blue("Группа"), info.Meta.Group) fmt.Printf("%s: %s\n", blue(locale.T("Group")), info.Meta.Group)
} }
if info.Meta.Summary != "" { if info.Meta.Summary != "" {
fmt.Printf("%s: %s\n", blue("Краткое"), info.Meta.Summary) fmt.Printf("%s: %s\n", blue(locale.T("Summary")), info.Meta.Summary)
} }
if info.Meta.Description != "" { if info.Meta.Description != "" {
fmt.Printf("%s: \n%s\n", blue("Описание"), info.Meta.Description) fmt.Printf("%s: \n%s\n", blue(locale.T("Description")), info.Meta.Description)
} }
} }
fmt.Printf("%s: %s\n", blue("Статус"), info.Status.Color("%s %s", info.Status.Symbol, info.Status.Label)) fmt.Printf("%s: %s\n", blue(locale.T("Status")), info.Status.Color("%s %s", info.Status.Symbol, info.Status.Label))
fmt.Printf("%s: %s\n", blue("Путь"), info.Path) fmt.Printf("%s: %s\n", blue(locale.T("Path")), info.Path)
fmt.Printf("%s: %s\n", blue("Путь в конфиге"), info.ConfPath) fmt.Printf("%s: %s\n", blue(locale.T("Config path")), info.ConfPath)
fmt.Printf("%s: %d\n", blue("Строка в конфиге"), info.LineNumber) fmt.Printf("%s: %d\n", blue(locale.T("Config line")), info.LineNumber)
if info.Available { if info.Available {
fmt.Printf("%s: да\n", blue("Доступен")) fmt.Printf("%s: %s\n", blue(locale.T("Available")), locale.T("yes"))
} else { } else {
fmt.Printf("%s: нет\n", blue("Доступен")) fmt.Printf("%s: %s\n", blue(locale.T("Available")), locale.T("no"))
} }
if len(errors) > 0 { if len(errors) > 0 {
fmt.Printf("\n%s (%d):\n", blue("Ошибки"), len(errors)) fmt.Printf("\n%s (%d):\n", blue(locale.T("Errors")), len(errors))
for _, e := range errors { for _, e := range errors {
fmt.Printf(" %d: %s\n", e.Line, e.Text) fmt.Printf(" %d: %s\n", e.Line, e.Text)
} }
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
...@@ -12,11 +13,11 @@ func CommandList() *cli.Command { ...@@ -12,11 +13,11 @@ func CommandList() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "hyprland", Name: "hyprland",
Hidden: config.Env.Hyprland == nil, Hidden: config.Env.Hyprland == nil,
Usage: "Hyprland Management", Usage: locale.T("Hyprland Management"),
Before: func(ctx context.Context, command *cli.Command) (context.Context, error) { Before: func(ctx context.Context, command *cli.Command) (context.Context, error) {
manager, err := NewHyprlandManager() manager, err := NewHyprlandManager()
if err != nil { if err != nil {
return ctx, fmt.Errorf("не удалось инициализировать HyprlandManager: %w", err) return ctx, fmt.Errorf(locale.T("failed to initialize HyprlandManager: %w"), err)
} }
ctx = context.WithValue(ctx, config.HyprManagerKey, manager) ctx = context.WithValue(ctx, config.HyprManagerKey, manager)
...@@ -25,7 +26,7 @@ func CommandList() *cli.Command { ...@@ -25,7 +26,7 @@ func CommandList() *cli.Command {
After: func(ctx context.Context, command *cli.Command) error { After: func(ctx context.Context, command *cli.Command) error {
manager, err := GetHyprlandManager(ctx) manager, err := GetHyprlandManager(ctx)
if err != nil { if err != nil {
return fmt.Errorf("не удалось инициализировать HyprlandManager: %w", err) return fmt.Errorf(locale.T("failed to initialize HyprlandManager: %w"), err)
} }
manager.Save() manager.Save()
return nil return nil
...@@ -33,22 +34,22 @@ func CommandList() *cli.Command { ...@@ -33,22 +34,22 @@ func CommandList() *cli.Command {
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "check", Name: "check",
Usage: "Check the Hyprland config", Usage: locale.T("Check the Hyprland config"),
Action: HyprlandCheckCommand, Action: HyprlandCheckCommand,
}, },
{ {
Name: "fix", Name: "fix",
Usage: "Fix config: sort modules into sections by order", Usage: locale.T("Fix config: sort modules into sections by order"),
Action: HyprlandFixConfigCommand, Action: HyprlandFixConfigCommand,
}, },
{ {
Name: "sync-xkb-layouts", Name: "sync-xkb-layouts",
Usage: "Sync layouts with xkb", Usage: locale.T("Sync layouts with xkb"),
Action: HyprlandSyncSystemLayouts, Action: HyprlandSyncSystemLayouts,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "force", Name: "force",
Usage: "Forced update", Usage: locale.T("Forced update"),
Aliases: []string{"f"}, Aliases: []string{"f"},
Value: false, Value: false,
}, },
...@@ -56,11 +57,11 @@ func CommandList() *cli.Command { ...@@ -56,11 +57,11 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "module", Name: "module",
Usage: "Hyprland modules", Usage: locale.T("Hyprland modules"),
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "user", Name: "user",
Usage: "use user modules", Usage: locale.T("use user modules"),
Aliases: []string{"u"}, Aliases: []string{"u"},
Value: false, Value: false,
}, },
...@@ -68,28 +69,28 @@ func CommandList() *cli.Command { ...@@ -68,28 +69,28 @@ func CommandList() *cli.Command {
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "check", Name: "check",
Usage: "Check Hyprland module", Usage: locale.T("Check Hyprland module"),
ArgsUsage: "module", ArgsUsage: "module",
Action: HyprlandModuleCheckCommand, Action: HyprlandModuleCheckCommand,
ShellComplete: ShellCompleteModule("all"), ShellComplete: ShellCompleteModule("all"),
}, },
{ {
Name: "edit", Name: "edit",
Usage: "Edit module file", Usage: locale.T("Edit module file"),
ArgsUsage: "module", ArgsUsage: "module",
Action: HyprlandModuleEditCommand, Action: HyprlandModuleEditCommand,
ShellComplete: ShellCompleteModule("all"), ShellComplete: ShellCompleteModule("all"),
}, },
{ {
Name: "status", Name: "status",
Usage: "Hyprland module status", Usage: locale.T("Hyprland module status"),
ArgsUsage: "module", ArgsUsage: "module",
Action: HyprlandModuleStatusCommand, Action: HyprlandModuleStatusCommand,
ShellComplete: ShellCompleteModule("all"), ShellComplete: ShellCompleteModule("all"),
}, },
{ {
Name: "show", Name: "show",
Usage: "Show detailed module info", Usage: locale.T("Show detailed module info"),
ArgsUsage: "module", ArgsUsage: "module",
Flags: []cli.Flag{ Flags: []cli.Flag{
config.FormatFlag, config.FormatFlag,
...@@ -99,11 +100,11 @@ func CommandList() *cli.Command { ...@@ -99,11 +100,11 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "info", Name: "info",
Usage: "Information about modules", Usage: locale.T("Information about modules"),
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "filter", Name: "filter",
Usage: "status filter", Usage: locale.T("status filter"),
Aliases: []string{"f"}, Aliases: []string{"f"},
Value: "all", Value: "all",
}, },
...@@ -113,18 +114,18 @@ func CommandList() *cli.Command { ...@@ -113,18 +114,18 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "enable", Name: "enable",
Usage: "enable module", Usage: locale.T("enable module"),
ArgsUsage: "module", ArgsUsage: "module",
Action: HyprlandModuleEnableCommand, Action: HyprlandModuleEnableCommand,
ShellComplete: ShellCompleteModule("disabled"), ShellComplete: ShellCompleteModule("disabled"),
}, },
{ {
Name: "disable", Name: "disable",
Usage: "disable module", Usage: locale.T("disable module"),
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "remove", Name: "remove",
Usage: "delete module line from config", Usage: locale.T("delete module line from config"),
Aliases: []string{"r"}, Aliases: []string{"r"},
Value: false, Value: false,
}, },
...@@ -135,7 +136,7 @@ func CommandList() *cli.Command { ...@@ -135,7 +136,7 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "toggle", Name: "toggle",
Usage: "toggle module", Usage: locale.T("toggle module"),
ArgsUsage: "module", ArgsUsage: "module",
Action: HyprlandToggleModuleCommand, Action: HyprlandToggleModuleCommand,
ShellComplete: ShellCompleteModule("all"), ShellComplete: ShellCompleteModule("all"),
...@@ -144,11 +145,11 @@ func CommandList() *cli.Command { ...@@ -144,11 +145,11 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "var", Name: "var",
Usage: "Hyprland vars", Usage: locale.T("Hyprland vars"),
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "list", Name: "list",
Usage: "vars list", Usage: locale.T("vars list"),
Flags: []cli.Flag{ Flags: []cli.Flag{
config.FormatFlag, config.FormatFlag,
}, },
...@@ -156,7 +157,7 @@ func CommandList() *cli.Command { ...@@ -156,7 +157,7 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "info", Name: "info",
Usage: "vars info", Usage: locale.T("vars info"),
Flags: []cli.Flag{ Flags: []cli.Flag{
config.FormatFlag, config.FormatFlag,
}, },
...@@ -164,18 +165,18 @@ func CommandList() *cli.Command { ...@@ -164,18 +165,18 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "get", Name: "get",
Usage: "get var value", Usage: locale.T("get var value"),
Action: HyprlandVarGetCommand, Action: HyprlandVarGetCommand,
ShellComplete: ShellCompleteVarList, ShellComplete: ShellCompleteVarList,
}, },
{ {
Name: "set", Name: "set",
Usage: "set var value", Usage: locale.T("set var value"),
Action: HyprlandVarSetCommand, Action: HyprlandVarSetCommand,
}, },
{ {
Name: "unset", Name: "unset",
Usage: "unset var", Usage: locale.T("unset var"),
Action: HyprlandVarUnsetCommand, Action: HyprlandVarUnsetCommand,
ShellComplete: ShellCompleteVarList, ShellComplete: ShellCompleteVarList,
}, },
...@@ -183,11 +184,11 @@ func CommandList() *cli.Command { ...@@ -183,11 +184,11 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "plugin", Name: "plugin",
Usage: "Hyprland plugins", Usage: locale.T("Hyprland plugins"),
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "list", Name: "list",
Usage: "Hyprland plugins list", Usage: locale.T("Hyprland plugins list"),
Flags: []cli.Flag{ Flags: []cli.Flag{
config.FormatFlag, config.FormatFlag,
}, },
...@@ -195,18 +196,18 @@ func CommandList() *cli.Command { ...@@ -195,18 +196,18 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "status", Name: "status",
Usage: "Hyprland plugin status", Usage: locale.T("Hyprland plugin status"),
ArgsUsage: "plugin", ArgsUsage: "plugin",
Action: HyprlandPluginStatusCommand, Action: HyprlandPluginStatusCommand,
ShellComplete: ShellCompletePlugin("all"), ShellComplete: ShellCompletePlugin("all"),
}, },
{ {
Name: "info", Name: "info",
Usage: "Information about plugins", Usage: locale.T("Information about plugins"),
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "filter", Name: "filter",
Usage: "status filter", Usage: locale.T("status filter"),
Aliases: []string{"f"}, Aliases: []string{"f"},
Value: "all", Value: "all",
}, },
...@@ -216,21 +217,21 @@ func CommandList() *cli.Command { ...@@ -216,21 +217,21 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "load", Name: "load",
Usage: "load plugin", Usage: locale.T("load plugin"),
ArgsUsage: "plugin", ArgsUsage: "plugin",
Action: HyprlandPluginLoadCommand, Action: HyprlandPluginLoadCommand,
ShellComplete: ShellCompletePlugin("unloaded"), ShellComplete: ShellCompletePlugin("unloaded"),
}, },
{ {
Name: "unload", Name: "unload",
Usage: "unload plugin", Usage: locale.T("unload plugin"),
ArgsUsage: "plugin", ArgsUsage: "plugin",
Action: HyprlandPluginUnloadCommand, Action: HyprlandPluginUnloadCommand,
ShellComplete: ShellCompletePlugin("loaded"), ShellComplete: ShellCompletePlugin("loaded"),
}, },
{ {
Name: "toggle", Name: "toggle",
Usage: "toggle plugin", Usage: locale.T("toggle plugin"),
ArgsUsage: "plugin", ArgsUsage: "plugin",
Action: HyprlandPluginToggleCommand, Action: HyprlandPluginToggleCommand,
ShellComplete: ShellCompletePlugin("all"), ShellComplete: ShellCompletePlugin("all"),
......
...@@ -2,8 +2,10 @@ package hyprland ...@@ -2,8 +2,10 @@ package hyprland
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5"
...@@ -38,18 +40,18 @@ func HyprlandSyncSystemLayouts(ctx context.Context, cmd *cli.Command) error { ...@@ -38,18 +40,18 @@ func HyprlandSyncSystemLayouts(ctx context.Context, cmd *cli.Command) error {
sysLayouts, err := HyprlandGetKeyboardLayouts() sysLayouts, err := HyprlandGetKeyboardLayouts()
if err != nil { if err != nil {
return fmt.Errorf("не удалось получить системные раскладки: %w", err) return fmt.Errorf(locale.T("failed to get system layouts: %w"), err)
} }
hyprLayouts := manager.GetVar("kb_layout") hyprLayouts := manager.GetVar("kb_layout")
if force || hyprLayouts == "" { if force || hyprLayouts == "" {
if _, err := manager.SetVar("kb_layout", sysLayouts); err != nil { if _, err := manager.SetVar("kb_layout", sysLayouts); err != nil {
return fmt.Errorf("не удалось обновить kb_layout в Hyprland: %w", err) return fmt.Errorf(locale.T("failed to update kb_layout in Hyprland: %w"), err)
} }
color.Green("Раскладка обновлена!") color.Green(locale.T("Layout updated!"))
} else { } else {
return fmt.Errorf("раскладка уже установлена, используйте '--force' для принудительного обновления") return errors.New(locale.T("layout is already set, use '--force' for forced update"))
} }
return nil return nil
......
...@@ -3,6 +3,7 @@ package hyprland ...@@ -3,6 +3,7 @@ package hyprland
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
...@@ -11,6 +12,7 @@ import ( ...@@ -11,6 +12,7 @@ import (
"strings" "strings"
"time" "time"
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"ximperconf/utils" "ximperconf/utils"
) )
...@@ -105,7 +107,7 @@ func NewHyprlandManager() (*HyprlandManager, error) { ...@@ -105,7 +107,7 @@ func NewHyprlandManager() (*HyprlandManager, error) {
func GetHyprlandManager(ctx context.Context) (*HyprlandManager, error) { func GetHyprlandManager(ctx context.Context) (*HyprlandManager, error) {
mgr, ok := ctx.Value(config.HyprManagerKey).(*HyprlandManager) mgr, ok := ctx.Value(config.HyprManagerKey).(*HyprlandManager)
if !ok || mgr == nil { if !ok || mgr == nil {
return nil, fmt.Errorf("HyprlandManager не найден в контексте") return nil, errors.New(locale.T("HyprlandManager not found in context"))
} }
return mgr, nil return mgr, nil
} }
...@@ -136,7 +138,7 @@ func (m *HyprlandManager) Check(configPath string) ([]HyprConfigError, error) { ...@@ -136,7 +138,7 @@ func (m *HyprlandManager) Check(configPath string) ([]HyprConfigError, error) {
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if err != nil { if err != nil {
if _, ok := err.(*exec.ExitError); !ok { if _, ok := err.(*exec.ExitError); !ok {
return nil, fmt.Errorf("проверка не удалась") return nil, errors.New(locale.T("check failed"))
} }
} }
...@@ -151,7 +153,7 @@ func (m *HyprlandManager) CheckModule( ...@@ -151,7 +153,7 @@ func (m *HyprlandManager) CheckModule(
info := m.GetModuleInfo(module, user) info := m.GetModuleInfo(module, user)
if info.Path == "" { if info.Path == "" {
return nil, fmt.Errorf("модуль '%s' не найден", module) return nil, fmt.Errorf(locale.T("module '%s' not found"), module)
} }
tmp, err := os.CreateTemp("", "ximperconf-hypr-check-*.conf") tmp, err := os.CreateTemp("", "ximperconf-hypr-check-*.conf")
...@@ -339,11 +341,11 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) ( ...@@ -339,11 +341,11 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
info := m.GetModuleInfo(module, user) info := m.GetModuleInfo(module, user)
if !info.Available { if !info.Available {
return "", fmt.Errorf("недопустимое название для модуля") return "", errors.New(locale.T("invalid module name"))
} }
if info.Status.IsEqual(config.ModuleStatus.Unknown) { if info.Status.IsEqual(config.ModuleStatus.Unknown) {
return "", fmt.Errorf("модуль '%s' не найден: %s", module, info.Path) return "", fmt.Errorf(locale.T("module '%s' not found: %s"), module, info.Path)
} }
switch action { switch action {
...@@ -351,12 +353,12 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) ( ...@@ -351,12 +353,12 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
// нет файла // нет файла
if info.Path == "" || info.Status.IsEqual(config.ModuleStatus.Unknown) { if info.Path == "" || info.Status.IsEqual(config.ModuleStatus.Unknown) {
return "", fmt.Errorf("нельзя включить данный модуль") return "", errors.New(locale.T("cannot enable this module"))
} }
// уже включён // уже включён
if info.Status.IsEqual(config.ModuleStatus.Enabled) { if info.Status.IsEqual(config.ModuleStatus.Enabled) {
return "", fmt.Errorf("модуль '%s' уже включён", module) return "", fmt.Errorf(locale.T("module '%s' is already enabled"), module)
} }
// отключить другие модули той же группы // отключить другие модули той же группы
...@@ -378,7 +380,7 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) ( ...@@ -378,7 +380,7 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
// был закомментирован // был закомментирован
if info.Status.IsEqual(config.ModuleStatus.Disabled) { if info.Status.IsEqual(config.ModuleStatus.Disabled) {
if onlyNew { if onlyNew {
return "", fmt.Errorf("модуль '%s' уже присутствует в конфиге (закомментирован) — пропущено", module) return "", fmt.Errorf(locale.T("module '%s' already present in config (commented) — skipped"), module)
} }
m.Lines[info.LineNumber] = strings.TrimPrefix(m.Lines[info.LineNumber], "#") m.Lines[info.LineNumber] = strings.TrimPrefix(m.Lines[info.LineNumber], "#")
m.updateSourceCommented(info.LineNumber, false) m.updateSourceCommented(info.LineNumber, false)
...@@ -396,14 +398,14 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) ( ...@@ -396,14 +398,14 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
return m.enableMessage(module, disabledGroup), nil return m.enableMessage(module, disabledGroup), nil
} }
return "", fmt.Errorf("модуль '%s' не изменён", module) return "", fmt.Errorf(locale.T("module '%s' not changed"), module)
case "disable": case "disable":
// Уже выключен // Уже выключен
if info.Status.IsEqual(config.ModuleStatus.Disabled) || if info.Status.IsEqual(config.ModuleStatus.Disabled) ||
info.Status.IsEqual(config.ModuleStatus.Unused) { info.Status.IsEqual(config.ModuleStatus.Unused) {
return "", fmt.Errorf("модуль '%s' уже отключён", module) return "", fmt.Errorf(locale.T("module '%s' is already disabled"), module)
} }
// Включён // Включён
...@@ -412,30 +414,30 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) ( ...@@ -412,30 +414,30 @@ func (m *HyprlandManager) SetModule(action, module string, user, onlyNew bool) (
m.Lines[info.LineNumber] = "#" + m.Lines[info.LineNumber] m.Lines[info.LineNumber] = "#" + m.Lines[info.LineNumber]
m.updateSourceCommented(info.LineNumber, true) m.updateSourceCommented(info.LineNumber, true)
m.Changed = true m.Changed = true
return fmt.Sprintf("Модуль '%s' отключён", module), nil return fmt.Sprintf(locale.T("Module '%s' disabled"), module), nil
} }
case "remove": case "remove":
if info.LineNumber <= 0 { if info.LineNumber <= 0 {
return "", fmt.Errorf("модуль '%s' не найден в конфигурации", module) return "", fmt.Errorf(locale.T("module '%s' not found in config"), module)
} }
m.removeSource(info.LineNumber) m.removeSource(info.LineNumber)
m.removeLine(info.LineNumber) m.removeLine(info.LineNumber)
return fmt.Sprintf("Модуль '%s' удалён", module), nil return fmt.Sprintf(locale.T("Module '%s' removed"), module), nil
} }
return "", nil return "", nil
} }
func (m *HyprlandManager) enableMessage(module string, disabledGroup []string) string { func (m *HyprlandManager) enableMessage(module string, disabledGroup []string) string {
msg := fmt.Sprintf("Модуль '%s' включён", module) msg := fmt.Sprintf(locale.T("Module '%s' enabled"), module)
for _, name := range disabledGroup { for _, name := range disabledGroup {
msg += fmt.Sprintf("\nМодуль '%s' отключён (группа)", name) msg += fmt.Sprintf("\n"+locale.T("Module '%s' disabled (group)"), name)
} }
if m.hasSourcesOutsideSections() { if m.hasSourcesOutsideSections() {
msg += "\n\nВнимание: обнаружены модули вне секций. Выполните 'ximperconf hyprland fix' для исправления." msg += "\n\n" + locale.T("Warning: modules found outside sections. Run 'ximperconf hyprland fix' to fix.")
} }
return msg return msg
} }
...@@ -790,12 +792,12 @@ func (m *HyprlandManager) GetPluginStatus(name string) config.ItemStatus { ...@@ -790,12 +792,12 @@ func (m *HyprlandManager) GetPluginStatus(name string) config.ItemStatus {
func (m *HyprlandManager) GetPluginFile(name string) (string, error) { func (m *HyprlandManager) GetPluginFile(name string) (string, error) {
if name == "" { if name == "" {
return "", fmt.Errorf("плагин не указан") return "", errors.New(locale.T("plugin not specified"))
} }
path := filepath.Join(m.PluginsDir, name+".so") path := filepath.Join(m.PluginsDir, name+".so")
if !utils.FileExists(path) { if !utils.FileExists(path) {
return "", fmt.Errorf("плагин не найден") return "", errors.New(locale.T("plugin not found"))
} }
return path, nil return path, nil
...@@ -818,7 +820,7 @@ func (m *HyprlandManager) GetPluginsList(filter string) []string { ...@@ -818,7 +820,7 @@ func (m *HyprlandManager) GetPluginsList(filter string) []string {
func (m *HyprlandManager) SetPlugin(action string, name string) (string, error) { func (m *HyprlandManager) SetPlugin(action string, name string) (string, error) {
if name == "" { if name == "" {
return "", fmt.Errorf("плагин не указан") return "", errors.New(locale.T("plugin not specified"))
} }
path, err := m.GetPluginFile(name) path, err := m.GetPluginFile(name)
...@@ -831,7 +833,7 @@ func (m *HyprlandManager) SetPlugin(action string, name string) (string, error) ...@@ -831,7 +833,7 @@ func (m *HyprlandManager) SetPlugin(action string, name string) (string, error)
switch action { switch action {
case "load": case "load":
if status.IsEqual(config.PluginStatus.Loaded) { if status.IsEqual(config.PluginStatus.Loaded) {
return "", fmt.Errorf("плагин уже загружен") return "", errors.New(locale.T("plugin is already loaded"))
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
...@@ -839,11 +841,11 @@ func (m *HyprlandManager) SetPlugin(action string, name string) (string, error) ...@@ -839,11 +841,11 @@ func (m *HyprlandManager) SetPlugin(action string, name string) (string, error)
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
return "", fmt.Errorf("%v (%s)", err, out) return "", fmt.Errorf("%v (%s)", err, out)
} }
return fmt.Sprintf("Плагин '%s' загружен", name), nil return fmt.Sprintf(locale.T("Plugin '%s' loaded"), name), nil
case "unload": case "unload":
if status.IsEqual(config.PluginStatus.Unloaded) { if status.IsEqual(config.PluginStatus.Unloaded) {
return "", fmt.Errorf("плагин не загружен") return "", errors.New(locale.T("plugin is not loaded"))
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
...@@ -851,7 +853,7 @@ func (m *HyprlandManager) SetPlugin(action string, name string) (string, error) ...@@ -851,7 +853,7 @@ func (m *HyprlandManager) SetPlugin(action string, name string) (string, error)
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
return "", fmt.Errorf("%v (%s)", err, out) return "", fmt.Errorf("%v (%s)", err, out)
} }
return fmt.Sprintf("Плагин '%s' выгружен", name), nil return fmt.Sprintf(locale.T("Plugin '%s' unloaded"), name), nil
} }
return "", nil return "", nil
...@@ -882,21 +884,21 @@ func (m *HyprlandManager) GetVar(name string) string { ...@@ -882,21 +884,21 @@ func (m *HyprlandManager) GetVar(name string) string {
func (m *HyprlandManager) SetVar(name, value string) (string, error) { func (m *HyprlandManager) SetVar(name, value string) (string, error) {
if name == "" { if name == "" {
return "", fmt.Errorf("укажите имя переменной") return "", errors.New(locale.T("specify variable name"))
} }
if value == "" { if value == "" {
return "", fmt.Errorf("укажите значение переменной") return "", errors.New(locale.T("specify variable value"))
} }
for i, v := range m.Vars { for i, v := range m.Vars {
if v.Name == name { if v.Name == name {
if v.Value == value { if v.Value == value {
return "", fmt.Errorf("переменная '%s' уже '%s'", v.Name, v.Value) return "", fmt.Errorf(locale.T("variable '%s' is already '%s'"), v.Name, v.Value)
} }
m.Lines[v.LineNumber] = fmt.Sprintf("$%s = %s", name, value) m.Lines[v.LineNumber] = fmt.Sprintf("$%s = %s", name, value)
m.Vars[i].Value = value m.Vars[i].Value = value
m.Changed = true m.Changed = true
return fmt.Sprintf("Переменная '%s' обновлена: %s", name, value), nil return fmt.Sprintf(locale.T("Variable '%s' updated: %s"), name, value), nil
} }
} }
...@@ -909,19 +911,19 @@ func (m *HyprlandManager) SetVar(name, value string) (string, error) { ...@@ -909,19 +911,19 @@ func (m *HyprlandManager) SetVar(name, value string) (string, error) {
m.shiftLineNumbers(insertAt-1, 1) m.shiftLineNumbers(insertAt-1, 1)
m.Vars = append(m.Vars, HyprVar{Name: name, Value: value, LineNumber: insertAt}) m.Vars = append(m.Vars, HyprVar{Name: name, Value: value, LineNumber: insertAt})
m.Changed = true m.Changed = true
return fmt.Sprintf("Переменная '%s' установлена: %s", name, value), nil return fmt.Sprintf(locale.T("Variable '%s' set: %s"), name, value), nil
} }
insertAt := len(m.Lines) insertAt := len(m.Lines)
m.Lines = append(m.Lines, "", sectionHeader(sectionVars), newLine) m.Lines = append(m.Lines, "", sectionHeader(sectionVars), newLine)
m.Vars = append(m.Vars, HyprVar{Name: name, Value: value, LineNumber: insertAt + 2}) m.Vars = append(m.Vars, HyprVar{Name: name, Value: value, LineNumber: insertAt + 2})
m.Changed = true m.Changed = true
return fmt.Sprintf("Блок VARS создан, переменная '%s' установлена: %s", name, value), nil return fmt.Sprintf(locale.T("VARS block created, variable '%s' set: %s"), name, value), nil
} }
func (m *HyprlandManager) UnsetVar(name string) error { func (m *HyprlandManager) UnsetVar(name string) error {
if name == "" { if name == "" {
return fmt.Errorf("укажите имя переменной") return errors.New(locale.T("specify variable name"))
} }
for i, v := range m.Vars { for i, v := range m.Vars {
...@@ -931,7 +933,7 @@ func (m *HyprlandManager) UnsetVar(name string) error { ...@@ -931,7 +933,7 @@ func (m *HyprlandManager) UnsetVar(name string) error {
return nil return nil
} }
} }
return fmt.Errorf("переменная %s не найдена", name) return fmt.Errorf(locale.T("variable '%s' not found"), name)
} }
// ==================== // ====================
......
...@@ -2,9 +2,11 @@ package hyprland ...@@ -2,9 +2,11 @@ package hyprland
import ( import (
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"ximperconf/ui" "ximperconf/ui"
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
...@@ -49,7 +51,7 @@ func HyprlandPluginInfoCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -49,7 +51,7 @@ func HyprlandPluginInfoCommand(ctx context.Context, cmd *cli.Command) error {
if config.IsJSON(cmd) { if config.IsJSON(cmd) {
return ui.PrintJSON([]ui.JSONItem{}) return ui.PrintJSON([]ui.JSONItem{})
} }
return fmt.Errorf("нет доступных плагинов") return errors.New(locale.T("no available plugins"))
} }
items := make([]ui.TreeItem, 0, len(plugins)) items := make([]ui.TreeItem, 0, len(plugins))
...@@ -76,7 +78,7 @@ func HyprlandPluginInfoCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -76,7 +78,7 @@ func HyprlandPluginInfoCommand(ctx context.Context, cmd *cli.Command) error {
} }
ui.RenderTree(ui.RenderTreeOptions{ ui.RenderTree(ui.RenderTreeOptions{
Title: "Plugins", Title: locale.T("Plugins"),
Items: items, Items: items,
Style: ui.DefaultTreeStyle, Style: ui.DefaultTreeStyle,
Color: true, Color: true,
......
...@@ -3,6 +3,7 @@ package hyprland ...@@ -3,6 +3,7 @@ package hyprland
import ( import (
"context" "context"
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"ximperconf/ui" "ximperconf/ui"
"ximperconf/utils" "ximperconf/utils"
...@@ -48,7 +49,7 @@ func HyprlandVarInfoCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -48,7 +49,7 @@ func HyprlandVarInfoCommand(ctx context.Context, cmd *cli.Command) error {
if config.IsJSON(cmd) { if config.IsJSON(cmd) {
return ui.PrintJSON([]ui.JSONItem{}) return ui.PrintJSON([]ui.JSONItem{})
} }
color.Yellow("Нет переменных в конфигурации") color.Yellow(locale.T("No variables in configuration"))
return nil return nil
} }
...@@ -73,7 +74,7 @@ func HyprlandVarInfoCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -73,7 +74,7 @@ func HyprlandVarInfoCommand(ctx context.Context, cmd *cli.Command) error {
} }
ui.RenderTree(ui.RenderTreeOptions{ ui.RenderTree(ui.RenderTreeOptions{
Title: "Vars", Title: locale.T("Vars"),
Items: items, Items: items,
Style: ui.DefaultTreeStyle, Style: ui.DefaultTreeStyle,
Sort: true, Sort: true,
...@@ -90,7 +91,7 @@ func HyprlandVarGetCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -90,7 +91,7 @@ func HyprlandVarGetCommand(ctx context.Context, cmd *cli.Command) error {
} }
info := manager.GetVar(v) info := manager.GetVar(v)
if info == "" { if info == "" {
return fmt.Errorf("переменная '%s' не установлена", v) return fmt.Errorf(locale.T("variable '%s' is not set"), v)
} }
fmt.Println(info) fmt.Println(info)
return nil return nil
...@@ -113,15 +114,15 @@ func HyprlandVarSetCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -113,15 +114,15 @@ func HyprlandVarSetCommand(ctx context.Context, cmd *cli.Command) error {
func hyprlandVarUnset(name string) error { func hyprlandVarUnset(name string) error {
if name == "" { if name == "" {
color.Red("Укажите имя переменной") color.Red(locale.T("specify variable name"))
os.Exit(1) os.Exit(1)
} }
if !regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`).MatchString(name) { if !regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`).MatchString(name) {
color.Red("Недопустимое имя переменной: %s", name) color.Red(locale.T("Invalid variable name: %s"), name)
os.Exit(1) os.Exit(1)
} }
if !utils.FileExists(config.Env.Hyprland.Config) { if !utils.FileExists(config.Env.Hyprland.Config) {
color.Red("Конфигурация не найдена: %s", config.Env.Hyprland.Config) color.Red(locale.T("Configuration not found: %s"), config.Env.Hyprland.Config)
os.Exit(1) os.Exit(1)
} }
...@@ -141,11 +142,11 @@ func hyprlandVarUnset(name string) error { ...@@ -141,11 +142,11 @@ func hyprlandVarUnset(name string) error {
} }
if !removed { if !removed {
color.Red("Переменная %s не найдена", name) color.Red(locale.T("variable '%s' not found"), name)
os.Exit(1) os.Exit(1)
} }
color.Green("Переменная %s удалена", name) color.Green(locale.T("Variable '%s' removed"), name)
return os.WriteFile(config.Env.Hyprland.Config, []byte(strings.Join(newLines, "\n")), 0644) return os.WriteFile(config.Env.Hyprland.Config, []byte(strings.Join(newLines, "\n")), 0644)
} }
......
package locale
import (
"os"
"strings"
"github.com/leonelquinteros/gotext"
)
var LocaleDir string
func Init() {
gotext.Configure(LocaleDir, getLocale(), "ximperconf")
}
// getText is a variable to avoid go vet printf wrapper detection.
var getText = gotext.Get
func T(msgid string) string {
return getText(msgid)
}
func TN(msgid, msgidPlural string, n int) string {
return gotext.GetN(msgid, msgidPlural, n)
}
func getLocale() string {
for _, env := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} {
if v := os.Getenv(env); v != "" {
if idx := strings.Index(v, "."); idx != -1 {
return v[:idx]
}
return v
}
}
return "en"
}
...@@ -5,9 +5,11 @@ import ( ...@@ -5,9 +5,11 @@ import (
"os" "os"
"ximperconf/config" "ximperconf/config"
"ximperconf/hyprland" "ximperconf/hyprland"
"ximperconf/locale"
"ximperconf/preset" "ximperconf/preset"
"ximperconf/repo" "ximperconf/repo"
"ximperconf/system" "ximperconf/system"
"ximperconf/ui"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
...@@ -15,12 +17,14 @@ import ( ...@@ -15,12 +17,14 @@ import (
func main() { func main() {
config.InitConfig() config.InitConfig()
locale.Init()
ui.SetupHelpTemplates()
config.InitDBus() config.InitDBus()
defer config.DBusConn.Close() defer config.DBusConn.Close()
rootCommand := &cli.Command{ rootCommand := &cli.Command{
Name: "ximperconf", Name: "ximperconf",
Usage: "Ximper Linux Configuration Tool", Usage: locale.T("Ximper Linux Configuration Tool"),
EnableShellCompletion: true, EnableShellCompletion: true,
Version: config.Env.Version, Version: config.Env.Version,
Commands: []*cli.Command{ Commands: []*cli.Command{
...@@ -31,7 +35,7 @@ func main() { ...@@ -31,7 +35,7 @@ func main() {
{ {
Name: "help", Name: "help",
Aliases: []string{"h"}, Aliases: []string{"h"},
Usage: "show help", Usage: locale.T("show help"),
ArgsUsage: "[command]", ArgsUsage: "[command]",
HideHelp: true, HideHelp: true,
}, },
......
...@@ -7,13 +7,21 @@ project('ximperconf', ...@@ -7,13 +7,21 @@ project('ximperconf',
], ],
) )
i18n = import('i18n')
go_root = run_command('printenv', 'GOROOT', check: false) go_root = run_command('printenv', 'GOROOT', check: false)
go_bin = go_root.returncode() != 0 ? find_program('go') : find_program(join_paths(go_root.stdout().strip(), 'bin', 'go')) go_bin = go_root.returncode() != 0 ? find_program('go') : find_program(join_paths(go_root.stdout().strip(), 'bin', 'go'))
LOCALES_DIR = get_option('prefix') / get_option('localedir')
ldflags = '-X \'ximperconf/locale.LocaleDir=' + LOCALES_DIR + '\''
if get_option('shell_completion').enabled() if get_option('shell_completion').enabled()
subdir('data/completion') subdir('data/completion')
endif endif
subdir('po')
custom_target( custom_target(
'go-build', 'go-build',
build_by_default: true, build_by_default: true,
...@@ -23,7 +31,10 @@ custom_target( ...@@ -23,7 +31,10 @@ custom_target(
install: true, install: true,
install_dir: get_option('bindir'), install_dir: get_option('bindir'),
command: [ command: [
'go', 'build','-o','@OUTPUT@',meson.current_source_dir() 'go', 'build',
'-ldflags', ldflags,
'-o', '@OUTPUT@',
meson.current_source_dir()
], ],
env: [ env: [
'CGO_ENABLED=0' 'CGO_ENABLED=0'
......
hyprland/actions.go
hyprland/commands.go
hyprland/keyboard-actions.go
hyprland/manager.go
hyprland/plugin-actions.go
hyprland/var-actions.go
main.go
preset/actions.go
preset/commands.go
preset/tools.go
repo/actions.go
repo/commands.go
system/commands.go
system/grub-actions.go
ui/help.go
#!/bin/sh
# Should run from project root dir
po/update_potfiles
cat ./po/POTFILES | xargs xgettext --language=C --keyword=T --keyword=TN:1,2 -o po/ximperconf.pot --from-code=UTF-8 --add-comments --package-name=ximperconf
i18n.gettext(meson.project_name(), preset: 'glib')
This diff is collapsed. Click to expand it.
#!/bin/bash
# Should run from project root dir
./po/create_pot
update-lang() {
msgmerge -U --backup=off po/$1.po po/ximperconf.pot
}
if [ "$1" = "all" ]; then
for lang in $(cat po/LINGUAS); do
update-lang $lang
done
else
update-lang $1
fi
#!/bin/sh
# Should run from project root dir
touch ./po/unsort-POTFILES
find ./ -iname "*.go" -not -path "./builddir/*" -type f -exec grep -lrE 'locale\.T\(|locale\.TN\(' {} + | while read file; do echo "${file#./}" >> ./po/unsort-POTFILES; done
cat ./po/unsort-POTFILES | sort | uniq > ./po/POTFILES
rm ./po/unsort-POTFILES
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
...@@ -11,11 +12,11 @@ import ( ...@@ -11,11 +12,11 @@ import (
func CommandList() *cli.Command { func CommandList() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "preset", Name: "preset",
Usage: "Manage preset configuration profiles", Usage: locale.T("Manage preset configuration profiles"),
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "info", Name: "info",
Usage: "Show information about a preset profiles", Usage: locale.T("Show information about preset profiles"),
Flags: []cli.Flag{ Flags: []cli.Flag{
config.FormatFlag, config.FormatFlag,
}, },
...@@ -23,11 +24,11 @@ func CommandList() *cli.Command { ...@@ -23,11 +24,11 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "apply", Name: "apply",
Usage: "Apply a profile", Usage: locale.T("Apply a profile"),
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "dry-run", Name: "dry-run",
Usage: "Show what would be created without making changes.", Usage: locale.T("Show what would be created without making changes"),
Aliases: []string{"d"}, Aliases: []string{"d"},
Value: false, Value: false,
}, },
...@@ -37,11 +38,11 @@ func CommandList() *cli.Command { ...@@ -37,11 +38,11 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "apply-all", Name: "apply-all",
Usage: "Apply all available profiles", Usage: locale.T("Apply all available profiles"),
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "dry-run", Name: "dry-run",
Usage: "Show what would be created without making changes.", Usage: locale.T("Show what would be created without making changes"),
Aliases: []string{"d"}, Aliases: []string{"d"},
Value: false, Value: false,
}, },
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"strings" "strings"
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"ximperconf/ui" "ximperconf/ui"
) )
...@@ -41,13 +42,13 @@ func AppendOpResult(res *Result, r opResult) { ...@@ -41,13 +42,13 @@ func AppendOpResult(res *Result, r opResult) {
switch { switch {
case r.Status.IsEqual(config.OpStatus.Skipped): case r.Status.IsEqual(config.OpStatus.Skipped):
item.Name = fmt.Sprintf("Уже существует: %s", r.Target) item.Name = fmt.Sprintf(locale.T("Already exists: %s"), r.Target)
case r.Status.IsEqual(config.OpStatus.DryRun): case r.Status.IsEqual(config.OpStatus.DryRun):
item.Name = fmt.Sprintf("%s → %s", r.Source, r.Target) item.Name = fmt.Sprintf("%s → %s", r.Source, r.Target)
case r.Status.IsEqual(config.OpStatus.Done): case r.Status.IsEqual(config.OpStatus.Done):
item.Name = fmt.Sprintf("%s → %s", r.Source, r.Target) item.Name = fmt.Sprintf("%s → %s", r.Source, r.Target)
case r.Status.IsEqual(config.OpStatus.Error): case r.Status.IsEqual(config.OpStatus.Error):
item.Name = fmt.Sprintf("Ошибка: %s", r.Reason) item.Name = fmt.Sprintf(locale.T("Error: %s"), r.Reason)
} }
switch r.Kind { switch r.Kind {
...@@ -81,11 +82,11 @@ func expandPath(path, name string) string { ...@@ -81,11 +82,11 @@ func expandPath(path, name string) string {
} }
func renderPresetResult(res *Result) { func renderPresetResult(res *Result) {
ui.RenderTreeItems("Копируем", res.Copies) ui.RenderTreeItems(locale.T("Copying"), res.Copies)
ui.RenderTreeItems("Создаём ссылки", res.Links) ui.RenderTreeItems(locale.T("Creating links"), res.Links)
ui.RenderTreeItems("Обновляем", res.Replaced) ui.RenderTreeItems(locale.T("Updating"), res.Replaced)
ui.RenderTreeItems("Настраиваем систему", res.System) ui.RenderTreeItems(locale.T("Configuring system"), res.System)
ui.RenderTreeItems("Создаём переменные Hyprland", res.HyprVars) ui.RenderTreeItems(locale.T("Creating Hyprland variables"), res.HyprVars)
ui.RenderTreeItems("Подключаем модули Hyprland", res.HyprModules) ui.RenderTreeItems(locale.T("Enabling Hyprland modules"), res.HyprModules)
ui.RenderTreeItems("Синхронизируем раскладку Hyprland", res.SyncSystemLayouts) ui.RenderTreeItems(locale.T("Syncing Hyprland layout"), res.SyncSystemLayouts)
} }
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"os" "os"
"regexp" "regexp"
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"ximperconf/ui" "ximperconf/ui"
"github.com/fatih/color" "github.com/fatih/color"
...@@ -27,14 +28,14 @@ var ( ...@@ -27,14 +28,14 @@ var (
func deferredGetHTML() string { func deferredGetHTML() string {
resp, err := http.Get(config.Env.Repo.DeferredInfoURL) resp, err := http.Get(config.Env.Repo.DeferredInfoURL)
if err != nil { if err != nil {
color.Red("Ошибка получения данных") color.Red(locale.T("Error fetching data"))
os.Exit(1) os.Exit(1)
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := io.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
color.Red("Ошибка получения данных") color.Red(locale.T("Error fetching data"))
os.Exit(1) os.Exit(1)
} }
return string(data) return string(data)
...@@ -86,8 +87,8 @@ func DeferredInfoCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -86,8 +87,8 @@ func DeferredInfoCommand(ctx context.Context, cmd *cli.Command) error {
} }
color.Green("Deferred:") color.Green("Deferred:")
fmt.Printf(" Последнее обновление: %s\n", lastUpdate) fmt.Printf(" %s: %s\n", locale.T("Last update"), lastUpdate)
fmt.Printf(" Дата архива: %s\n", archiveDate) fmt.Printf(" %s: %s\n", locale.T("Archive date"), archiveDate)
return nil return nil
} }
...@@ -2,6 +2,7 @@ package repo ...@@ -2,6 +2,7 @@ package repo
import ( import (
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
...@@ -9,15 +10,15 @@ import ( ...@@ -9,15 +10,15 @@ import (
func CommandList() *cli.Command { func CommandList() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "repo", Name: "repo",
Usage: "Ximper repos", Usage: locale.T("Ximper repos"),
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "deferred", Name: "deferred",
Usage: "Deferred repo", Usage: locale.T("Deferred repo"),
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "info", Name: "info",
Usage: "Deferred repo info", Usage: locale.T("Deferred repo info"),
Flags: []cli.Flag{ Flags: []cli.Flag{
config.FormatFlag, config.FormatFlag,
}, },
...@@ -25,12 +26,12 @@ func CommandList() *cli.Command { ...@@ -25,12 +26,12 @@ func CommandList() *cli.Command {
}, },
{ {
Name: "date", Name: "date",
Usage: "Sisyphus archive date", Usage: locale.T("Sisyphus archive date"),
Action: DeferredArchiveDateCommand, Action: DeferredArchiveDateCommand,
}, },
{ {
Name: "last-update", Name: "last-update",
Usage: "Date of last update", Usage: locale.T("Date of last update"),
Action: DeferredLastUpdateCommand, Action: DeferredLastUpdateCommand,
}, },
}, },
......
...@@ -3,6 +3,7 @@ package system ...@@ -3,6 +3,7 @@ package system
import ( import (
"fmt" "fmt"
"ximperconf/config" "ximperconf/config"
"ximperconf/locale"
"context" "context"
...@@ -13,10 +14,10 @@ func CommandList() *cli.Command { ...@@ -13,10 +14,10 @@ func CommandList() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "system", Name: "system",
Hidden: !config.Env.IsRoot, Hidden: !config.Env.IsRoot,
Usage: "System Management", Usage: locale.T("System Management"),
Before: func(ctx context.Context, command *cli.Command) (context.Context, error) { Before: func(ctx context.Context, command *cli.Command) (context.Context, error) {
if !config.Env.IsRoot { if !config.Env.IsRoot {
fmt.Printf("Эта команда требует root.\n") fmt.Println(locale.T("This command requires root"))
return nil, fmt.Errorf("") return nil, fmt.Errorf("")
} }
return ctx, nil return ctx, nil
...@@ -24,18 +25,18 @@ func CommandList() *cli.Command { ...@@ -24,18 +25,18 @@ func CommandList() *cli.Command {
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
Name: "fix-grub-resolution", Name: "fix-grub-resolution",
Usage: "Fix GRUB resolution", Usage: locale.T("Fix GRUB resolution"),
Action: SystemGrubFixResolutionCommand, Action: SystemGrubFixResolutionCommand,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "update", Name: "update",
Usage: "Automatically run update-grub after fixing", Usage: locale.T("Automatically run update-grub after fixing"),
Aliases: []string{"u"}, Aliases: []string{"u"},
Value: false, Value: false,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "verbose", Name: "verbose",
Usage: "Enable verbose output", Usage: locale.T("Enable verbose output"),
Aliases: []string{"V"}, Aliases: []string{"V"},
Value: false, Value: false,
}, },
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"ximperconf/locale"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
...@@ -19,7 +20,7 @@ func SystemGrubFixResolution(autoUpdate, verbose bool) error { ...@@ -19,7 +20,7 @@ func SystemGrubFixResolution(autoUpdate, verbose bool) error {
content, err := os.ReadFile(grubFile) content, err := os.ReadFile(grubFile)
if err != nil { if err != nil {
return fmt.Errorf("не удалось прочитать %s: %w", grubFile, err) return fmt.Errorf(locale.T("failed to read %s: %w"), grubFile, err)
} }
lines := strings.Split(string(content), "\n") lines := strings.Split(string(content), "\n")
...@@ -46,12 +47,12 @@ func SystemGrubFixResolution(autoUpdate, verbose bool) error { ...@@ -46,12 +47,12 @@ func SystemGrubFixResolution(autoUpdate, verbose bool) error {
} }
if !changed { if !changed {
return fmt.Errorf("GRUB_GFXMODE уже установлен: %s", oldValue) return fmt.Errorf(locale.T("GRUB_GFXMODE is already set: %s"), oldValue)
} }
newContent := strings.Join(lines, "\n") newContent := strings.Join(lines, "\n")
if err := os.WriteFile(grubFile, []byte(newContent), 0644); err != nil { if err := os.WriteFile(grubFile, []byte(newContent), 0644); err != nil {
return fmt.Errorf("не удалось записать %s: %w", grubFile, err) return fmt.Errorf(locale.T("failed to write %s: %w"), grubFile, err)
} }
if verbose { if verbose {
...@@ -62,10 +63,10 @@ func SystemGrubFixResolution(autoUpdate, verbose bool) error { ...@@ -62,10 +63,10 @@ func SystemGrubFixResolution(autoUpdate, verbose bool) error {
cmd := exec.Command("update-grub") cmd := exec.Command("update-grub")
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return fmt.Errorf("ошибка при выполнении update-grub: %v (%s)", err, out) return fmt.Errorf(locale.T("error running update-grub: %v (%s)"), err, out)
} }
if verbose { if verbose {
fmt.Println("Лог update-grub:\n", string(out)) fmt.Println(locale.T("update-grub log:")+" \n", string(out))
} }
} }
...@@ -82,10 +83,10 @@ func SystemGrubFixResolutionCommand(ctx context.Context, cmd *cli.Command) error ...@@ -82,10 +83,10 @@ func SystemGrubFixResolutionCommand(ctx context.Context, cmd *cli.Command) error
} }
if !verbose { if !verbose {
fmt.Println("GRUB_GFXMODE исправлен успешно.") fmt.Println(locale.T("GRUB_GFXMODE fixed"))
} }
if autoUpdate && !verbose { if autoUpdate && !verbose {
fmt.Println("Выполнен update-grub.") fmt.Println(locale.T("update-grub completed"))
} }
return nil return nil
......
package ui
import (
"fmt"
"strings"
"ximperconf/locale"
"github.com/fatih/color"
"github.com/urfave/cli/v3"
)
func SetupHelpTemplates() {
cli.HelpFlag = &cli.BoolFlag{
Name: "help",
Aliases: []string{"h"},
Usage: locale.T("show help"),
HideDefault: true,
Local: true,
}
cli.VersionFlag = &cli.BoolFlag{
Name: "version",
Aliases: []string{"v"},
Usage: locale.T("print the version"),
HideDefault: true,
Local: true,
}
title := color.New(color.Bold, color.FgYellow).SprintFunc()
cli.RootCommandHelpTemplate = fmt.Sprintf(`%s
{{template "helpNameTemplate" .}} {{if .Version}}{{if not .HideVersion}}{{.Version}}{{end}}{{end}}{{if .Description}}
%s
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.FullName}} [command [command options]]{{end}}
%s
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
%s{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
%s{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
%s{{template "visibleFlagTemplate" .}}{{end}}
`,
title(locale.T("Module:")),
title(locale.T("Usage:")),
title(locale.T("Description:")),
title(locale.T("Commands:")),
title(locale.T("Options:")),
title(locale.T("Options:")),
)
cli.CommandHelpTemplate = fmt.Sprintf(`%s
{{template "helpNameTemplate" .}}
%s
{{template "usageTemplate" .}}{{if .Category}}
%s
{{.Category}}{{end}}{{if .Description}}
%s
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
%s{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
%s{{template "visibleFlagTemplate" .}}{{end}}{{if .VisiblePersistentFlags}}
%s{{template "visiblePersistentFlagTemplate" .}}{{end}}
`,
title(locale.T("Module:")),
title(locale.T("Usage:")),
title(locale.T("Category:")),
title(locale.T("Description:")),
title(locale.T("Options:")),
title(locale.T("Options:")),
title(locale.T("Global options:")),
)
cli.SubcommandHelpTemplate = fmt.Sprintf(`%s
{{template "helpNameTemplate" .}}
%s
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.FullName}} [command [command options]]{{end}}{{if .Category}}
%s
{{.Category}}{{end}}{{if .Description}}
%s
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
%s{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
%s{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
%s{{template "visibleFlagTemplate" .}}{{end}}
`,
title(locale.T("Module:")),
title(locale.T("Usage:")),
title(locale.T("Category:")),
title(locale.T("Description:")),
title(locale.T("Commands:")),
title(locale.T("Options:")),
title(locale.T("Options:")),
)
cli.FlagStringer = func(fl cli.Flag) string {
df, ok := fl.(cli.DocGenerationFlag)
if !ok {
return ""
}
placeholder, usage := unquoteUsage(df.GetUsage())
needsPlaceholder := df.TakesValue()
if needsPlaceholder && placeholder == "" {
placeholder = ""
}
defaultValueString := ""
if rf, ok := fl.(cli.RequiredFlag); !ok || !rf.IsRequired() {
isVisible := df.IsDefaultVisible()
if s := df.GetDefaultText(); isVisible && s != "" {
defaultValueString = fmt.Sprintf(" (%s: %s)", locale.T("default"), s)
}
}
usageWithDefault := strings.TrimSpace(usage + defaultValueString)
var prefixed string
names := fl.Names()
for i, name := range names {
if name == "" {
continue
}
if len(name) == 1 {
prefixed += "-" + name
} else {
prefixed += "--" + name
}
if placeholder != "" {
prefixed += " " + placeholder
}
if i < len(names)-1 {
prefixed += ", "
}
}
if sliceFlag, ok := fl.(cli.DocGenerationMultiValueFlag); ok && sliceFlag.IsMultiValueFlag() {
prefixed = prefixed + " [ " + prefixed + " ]"
}
envVars := df.GetEnvVars()
envHint := ""
if len(envVars) > 0 {
envHint = fmt.Sprintf(" [%s]", strings.Join(envVars, ", "))
}
return fmt.Sprintf(" %s\t%s%s", prefixed, usageWithDefault, envHint)
}
}
func unquoteUsage(usage string) (string, string) {
for i := 0; i < len(usage); i++ {
if usage[i] == '`' {
for j := i + 1; j < len(usage); j++ {
if usage[j] == '`' {
name := usage[i+1 : j]
usage = usage[:i] + name + usage[j+1:]
return name, usage
}
}
break
}
}
return "", usage
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment