2
0
mirror of https://github.com/acepanel/helper.git synced 2026-02-03 20:20:43 +08:00

fix: i18n

This commit is contained in:
2026-01-17 23:12:33 +08:00
parent 3799eaae4b
commit b26ea8a13d
11 changed files with 132 additions and 546 deletions

View File

@@ -51,16 +51,16 @@ func (i *installer) Install(ctx context.Context, cfg *types.InstallConfig, progr
weight float64
fn func(ctx context.Context, cfg *types.InstallConfig) error
}{
{i18n.T().Get("Checking system requirements"), 0.05, i.checkSystem},
{i18n.T().Get("Creating www user"), 0.02, i.createUser},
{i18n.T().Get("Optimizing system settings"), 0.08, i.optimizeSystem},
{i18n.T().Get("Installing dependencies"), 0.20, i.installDeps},
{i18n.T().Get("Creating swap file"), 0.05, i.createSwap},
{i18n.T().Get("Downloading panel"), 0.30, i.downloadPanel},
{i18n.T().Get("Configuring firewall"), 0.10, i.configureFirewall},
{i18n.T().Get("Creating systemd service"), 0.10, i.createService},
{i18n.T().Get("Initializing panel"), 0.08, i.initPanel},
{i18n.T().Get("Detecting installed apps"), 0.02, i.detectApps},
{i18n.T.Get("Checking system requirements"), 0.05, i.checkSystem},
{i18n.T.Get("Creating www user"), 0.02, i.createUser},
{i18n.T.Get("Optimizing system settings"), 0.08, i.optimizeSystem},
{i18n.T.Get("Installing dependencies"), 0.20, i.installDeps},
{i18n.T.Get("Creating swap file"), 0.05, i.createSwap},
{i18n.T.Get("Downloading panel"), 0.30, i.downloadPanel},
{i18n.T.Get("Configuring firewall"), 0.10, i.configureFirewall},
{i18n.T.Get("Creating systemd service"), 0.10, i.createService},
{i18n.T.Get("Initializing panel"), 0.08, i.initPanel},
{i18n.T.Get("Detecting installed apps"), 0.02, i.detectApps},
}
var currentProgress float64
@@ -75,7 +75,7 @@ func (i *installer) Install(ctx context.Context, cfg *types.InstallConfig, progr
progress <- types.Progress{
Step: step.name,
Percent: currentProgress,
Message: fmt.Sprintf("%s: %v", i18n.T().Get("Error"), err),
Message: fmt.Sprintf("%s: %v", i18n.T.Get("Error"), err),
IsError: true,
Error: err,
}
@@ -86,14 +86,14 @@ func (i *installer) Install(ctx context.Context, cfg *types.InstallConfig, progr
progress <- types.Progress{
Step: step.name,
Percent: currentProgress,
Message: step.name + " " + i18n.T().Get("completed"),
Message: step.name + " " + i18n.T.Get("completed"),
}
}
progress <- types.Progress{
Step: i18n.T().Get("Installation complete"),
Step: i18n.T.Get("Installation complete"),
Percent: 1.0,
Message: i18n.T().Get("Panel installed successfully"),
Message: i18n.T.Get("Panel installed successfully"),
}
return nil
@@ -113,12 +113,12 @@ func (i *installer) checkSystem(ctx context.Context, cfg *types.InstallConfig) e
// 检查OS
if info.OS == types.OSUnknown {
return errors.New(i18n.T().Get("Unsupported operating system"))
return errors.New(i18n.T.Get("Unsupported operating system"))
}
// 检查架构
if info.Arch == types.ArchUnknown {
return errors.New(i18n.T().Get("Unsupported CPU architecture"))
return errors.New(i18n.T.Get("Unsupported CPU architecture"))
}
// 检查CPU特性
@@ -133,19 +133,19 @@ func (i *installer) checkSystem(ctx context.Context, cfg *types.InstallConfig) e
major := 0
_, _ = fmt.Sscanf(parts[0], "%d", &major)
if major < 4 {
return errors.New(i18n.T().Get("Kernel version too old, requires 4.x or above"))
return errors.New(i18n.T.Get("Kernel version too old, requires 4.x or above"))
}
}
}
// 检查是否64位
if !info.Is64Bit {
return errors.New(i18n.T().Get("Requires 64-bit system"))
return errors.New(i18n.T.Get("Requires 64-bit system"))
}
// 检查是否已安装
if i.detector.CheckPanelInstalled(cfg.SetupPath) {
return errors.New(i18n.T().Get("Panel is already installed"))
return errors.New(i18n.T.Get("Panel is already installed"))
}
// 保存系统信息到配置
@@ -201,7 +201,7 @@ func (i *installer) installDeps(ctx context.Context, cfg *types.InstallConfig) e
info, _ := i.detector.Detect(ctx)
pkgMgr := system.NewPackageManager(info.OS, i.executor)
if pkgMgr == nil {
return errors.New(i18n.T().Get("Unsupported operating system"))
return errors.New(i18n.T.Get("Unsupported operating system"))
}
// 设置镜像源
@@ -274,7 +274,7 @@ func (i *installer) downloadPanel(ctx context.Context, cfg *types.InstallConfig)
SetContext(ctx).
Get("https://api.acepanel.net/version/latest")
if err != nil {
return fmt.Errorf("%s: %w", i18n.T().Get("Failed to get version info"), err)
return fmt.Errorf("%s: %w", i18n.T.Get("Failed to get version info"), err)
}
var versionResp struct {
@@ -289,7 +289,7 @@ func (i *installer) downloadPanel(ctx context.Context, cfg *types.InstallConfig)
}
if err := json.Unmarshal(resp.Body(), &versionResp); err != nil {
return fmt.Errorf("%s: %w", i18n.T().Get("Failed to parse version info"), err)
return fmt.Errorf("%s: %w", i18n.T.Get("Failed to parse version info"), err)
}
// 根据架构选择下载链接
@@ -308,23 +308,23 @@ func (i *installer) downloadPanel(ctx context.Context, cfg *types.InstallConfig)
}
if downloadURL == "" {
return errors.New(i18n.T().Get("No download URL found for architecture %s", arch))
return errors.New(i18n.T.Get("No download URL found for architecture %s", arch))
}
// 下载面板
zipPath := cfg.SetupPath + "/panel/panel.zip"
resp, err = client.R().
_, err = client.R().
SetContext(ctx).
SetOutput(zipPath).
Get(downloadURL)
if err != nil {
return fmt.Errorf("%s: %w", i18n.T().Get("Failed to download panel"), err)
return fmt.Errorf("%s: %w", i18n.T.Get("Failed to download panel"), err)
}
// 解压
result, err := i.executor.Run(ctx, "unzip", "-o", zipPath, "-d", cfg.SetupPath+"/panel")
if err != nil || result.ExitCode != 0 {
return errors.New(i18n.T().Get("Failed to unzip panel"))
return errors.New(i18n.T.Get("Failed to unzip panel"))
}
// 删除zip文件
@@ -412,13 +412,13 @@ func (i *installer) initPanel(ctx context.Context, cfg *types.InstallConfig) err
// 初始化面板
result, err := i.executor.Run(ctx, "/usr/local/sbin/acepanel", "init")
if err != nil || result.ExitCode != 0 {
return errors.New(i18n.T().Get("Failed to initialize panel"))
return errors.New(i18n.T.Get("Failed to initialize panel"))
}
// 同步
result, err = i.executor.Run(ctx, "/usr/local/sbin/acepanel", "sync")
if err != nil || result.ExitCode != 0 {
return errors.New(i18n.T().Get("Failed to sync panel"))
return errors.New(i18n.T.Get("Failed to sync panel"))
}
return nil

View File

@@ -49,16 +49,16 @@ func (m *mounter) Mount(ctx context.Context, cfg *types.MountConfig, progress Pr
// 检查磁盘是否存在
if !m.detector.CheckDiskExists(cfg.Disk) {
return errors.New(i18n.T().Get("Disk not found"))
return errors.New(i18n.T.Get("Disk not found"))
}
// 检查是否为系统盘
if m.detector.IsSystemDisk(cfg.Disk) {
return errors.New(i18n.T().Get("Cannot operate on system disk"))
return errors.New(i18n.T.Get("Cannot operate on system disk"))
}
// 安装分区工具
progress(i18n.T().Get("Installing partition tools"), i18n.T().Get("Installing partition tools..."))
progress(i18n.T.Get("Installing partition tools"), i18n.T.Get("Installing partition tools..."))
info, _ := m.detector.Detect(ctx)
pkgMgr := system.NewPackageManager(info.OS, m.executor)
if pkgMgr != nil {
@@ -74,7 +74,7 @@ func (m *mounter) Mount(ctx context.Context, cfg *types.MountConfig, progress Pr
}
// 创建挂载点
progress(i18n.T().Get("Creating mount point"), i18n.T().Get("Creating %s...", cfg.MountPoint))
progress(i18n.T.Get("Creating mount point"), i18n.T.Get("Creating %s...", cfg.MountPoint))
if err := os.MkdirAll(cfg.MountPoint, 0755); err != nil {
return err
}
@@ -82,14 +82,14 @@ func (m *mounter) Mount(ctx context.Context, cfg *types.MountConfig, progress Pr
// 检查挂载点是否为空
entries, err := os.ReadDir(cfg.MountPoint)
if err == nil && len(entries) > 0 {
return errors.New(i18n.T().Get("Mount point is not empty"))
return errors.New(i18n.T.Get("Mount point is not empty"))
}
// 卸载已有分区
_, _ = m.executor.Run(ctx, "umount", "/dev/"+cfg.Disk+"1")
// 删除所有分区
progress(i18n.T().Get("Deleting existing partitions"), i18n.T().Get("Deleting existing partitions..."))
progress(i18n.T.Get("Deleting existing partitions"), i18n.T.Get("Deleting existing partitions..."))
fdiskInput := ""
// 获取现有分区数
result, _ := m.executor.Run(ctx, "lsblk", "-no", "NAME", "/dev/"+cfg.Disk)
@@ -104,42 +104,42 @@ func (m *mounter) Mount(ctx context.Context, cfg *types.MountConfig, progress Pr
_, _ = m.executor.RunWithInput(ctx, fdiskInput, "fdisk", "/dev/"+cfg.Disk)
// 创建新分区
progress(i18n.T().Get("Creating partition"), i18n.T().Get("Creating partition on /dev/%s...", cfg.Disk))
progress(i18n.T.Get("Creating partition"), i18n.T.Get("Creating partition on /dev/%s...", cfg.Disk))
partitionInput := "g\nn\n1\n\n\nw\n"
result, err = m.executor.RunWithInput(ctx, partitionInput, "fdisk", "/dev/"+cfg.Disk)
_, err = m.executor.RunWithInput(ctx, partitionInput, "fdisk", "/dev/"+cfg.Disk)
if err != nil {
return fmt.Errorf("%s: %w", i18n.T().Get("Failed to create partition"), err)
return fmt.Errorf("%s: %w", i18n.T.Get("Failed to create partition"), err)
}
// 格式化
progress(i18n.T().Get("Formatting partition"), i18n.T().Get("Formatting /dev/%s1 as %s...", cfg.Disk, cfg.FSType))
progress(i18n.T.Get("Formatting partition"), i18n.T.Get("Formatting /dev/%s1 as %s...", cfg.Disk, cfg.FSType))
switch cfg.FSType {
case types.FSTypeExt4:
result, err = m.executor.Run(ctx, "mkfs.ext4", "-F", "/dev/"+cfg.Disk+"1")
case types.FSTypeXFS:
result, err = m.executor.Run(ctx, "mkfs.xfs", "-f", "/dev/"+cfg.Disk+"1")
default:
return errors.New(i18n.T().Get("Unsupported filesystem type: %s", cfg.FSType))
return errors.New(i18n.T.Get("Unsupported filesystem type: %s", cfg.FSType))
}
if err != nil || (result != nil && result.ExitCode != 0) {
return errors.New(i18n.T().Get("Format failed"))
return errors.New(i18n.T.Get("Format failed"))
}
// 重载systemd
_, _ = m.executor.Run(ctx, "systemctl", "daemon-reload")
// 挂载
progress(i18n.T().Get("Mounting partition"), i18n.T().Get("Mounting /dev/%s1 to %s...", cfg.Disk, cfg.MountPoint))
progress(i18n.T.Get("Mounting partition"), i18n.T.Get("Mounting /dev/%s1 to %s...", cfg.Disk, cfg.MountPoint))
result, err = m.executor.Run(ctx, "mount", "/dev/"+cfg.Disk+"1", cfg.MountPoint)
if err != nil || (result != nil && result.ExitCode != 0) {
return errors.New(i18n.T().Get("Mount failed"))
return errors.New(i18n.T.Get("Mount failed"))
}
// 获取UUID
progress(i18n.T().Get("Updating fstab"), i18n.T().Get("Updating /etc/fstab for auto-mount..."))
progress(i18n.T.Get("Updating fstab"), i18n.T.Get("Updating /etc/fstab for auto-mount..."))
result, err = m.executor.Run(ctx, "blkid", "-s", "UUID", "-o", "value", "/dev/"+cfg.Disk+"1")
if err != nil || result == nil || result.ExitCode != 0 {
return errors.New(i18n.T().Get("Failed to get UUID"))
return errors.New(i18n.T.Get("Failed to get UUID"))
}
uuid := strings.TrimSpace(result.Stdout)
@@ -164,9 +164,9 @@ func (m *mounter) Mount(ctx context.Context, cfg *types.MountConfig, progress Pr
_, _ = m.executor.Run(ctx, "systemctl", "daemon-reload")
result, err = m.executor.Run(ctx, "mount", "-a")
if err != nil || (result != nil && result.ExitCode != 0) {
return errors.New(i18n.T().Get("fstab configuration error"))
return errors.New(i18n.T.Get("fstab configuration error"))
}
progress(i18n.T().Get("Mount complete"), i18n.T().Get("Disk partition and mount successful"))
progress(i18n.T.Get("Mount complete"), i18n.T.Get("Disk partition and mount successful"))
return nil
}

View File

@@ -2,7 +2,7 @@ package service
import (
"context"
"fmt"
"errors"
"os"
"github.com/acepanel/helper/internal/system"
@@ -44,25 +44,25 @@ func (u *uninstaller) Uninstall(ctx context.Context, setupPath string, progress
// 检查是否已安装
if !u.detector.CheckPanelInstalled(setupPath) {
return fmt.Errorf(i18n.T().Get("Panel is not installed"))
return errors.New(i18n.T.Get("Panel is not installed"))
}
// 停止服务
progress(i18n.T().Get("Stopping panel service"), i18n.T().Get("Stopping acepanel service..."))
progress(i18n.T.Get("Stopping panel service"), i18n.T.Get("Stopping acepanel service..."))
_ = u.systemd.Stop(ctx, "acepanel")
_ = u.systemd.Disable(ctx, "acepanel")
// 删除服务文件
progress(i18n.T().Get("Removing service file"), i18n.T().Get("Removing systemd service file..."))
progress(i18n.T.Get("Removing service file"), i18n.T.Get("Removing systemd service file..."))
_ = u.systemd.RemoveServiceFile("acepanel")
_ = u.systemd.DaemonReload(ctx)
// 删除CLI工具
progress(i18n.T().Get("Removing CLI tool"), i18n.T().Get("Removing /usr/local/sbin/acepanel..."))
progress(i18n.T.Get("Removing CLI tool"), i18n.T.Get("Removing /usr/local/sbin/acepanel..."))
_ = os.Remove("/usr/local/sbin/acepanel")
// 移除swap
progress(i18n.T().Get("Removing swap file"), i18n.T().Get("Removing swap file..."))
progress(i18n.T.Get("Removing swap file"), i18n.T.Get("Removing swap file..."))
swapFile := setupPath + "/swap"
if _, err := os.Stat(swapFile); err == nil {
_, _ = u.executor.Run(ctx, "swapoff", swapFile)
@@ -74,13 +74,13 @@ func (u *uninstaller) Uninstall(ctx context.Context, setupPath string, progress
// 验证fstab
result, _ := u.executor.Run(ctx, "mount", "-a")
if result != nil && result.ExitCode != 0 {
return fmt.Errorf(i18n.T().Get("fstab configuration error, please check /etc/fstab"))
return errors.New(i18n.T.Get("fstab configuration error, please check /etc/fstab"))
}
// 删除安装目录
progress(i18n.T().Get("Removing installation directory"), i18n.T().Get("Removing %s...", setupPath))
progress(i18n.T.Get("Removing installation directory"), i18n.T.Get("Removing %s...", setupPath))
_ = os.RemoveAll(setupPath)
progress(i18n.T().Get("Uninstallation complete"), i18n.T().Get("Panel uninstalled successfully"))
progress(i18n.T.Get("Uninstallation complete"), i18n.T.Get("Panel uninstalled successfully"))
return nil
}

View File

@@ -234,7 +234,7 @@ func (d *detector) CheckRoot() error {
return err
}
if currentUser.Uid != "0" {
return errors.New(i18n.T().Get("Please run with root privileges"))
return errors.New(i18n.T.Get("Please run with root privileges"))
}
return nil
}
@@ -252,7 +252,7 @@ func (d *detector) CheckCPUFeatures(ctx context.Context) error {
// 检查是否支持ssse3 (x86-64-v2的标志之一)
if !strings.Contains(string(data), "ssse3") {
return errors.New(i18n.T().Get("CPU must support at least x86-64-v2 instruction set"))
return errors.New(i18n.T.Get("CPU must support at least x86-64-v2 instruction set"))
}
return nil

View File

@@ -41,7 +41,7 @@ func (f *firewall) Install(ctx context.Context) error {
}
pkgMgr := NewPackageManager(info.OS, f.executor)
if pkgMgr == nil {
return fmt.Errorf("%s", i18n.T().Get("Unsupported operating system"))
return fmt.Errorf("%s", i18n.T.Get("Unsupported operating system"))
}
return pkgMgr.Install(ctx, "firewalld")
}
@@ -52,7 +52,7 @@ func (f *firewall) Enable(ctx context.Context) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("Failed to enable firewalld"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("Failed to enable firewalld"), result.Stderr)
}
// 设置默认zone
@@ -67,7 +67,7 @@ func (f *firewall) AddPort(ctx context.Context, port int, protocol string) error
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to add port"), portStr, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to add port"), portStr, result.Stderr)
}
return nil
}
@@ -79,7 +79,7 @@ func (f *firewall) RemovePort(ctx context.Context, port int, protocol string) er
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to remove port"), portStr, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to remove port"), portStr, result.Stderr)
}
return nil
}
@@ -90,7 +90,7 @@ func (f *firewall) Reload(ctx context.Context) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("Failed to reload firewall"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("Failed to reload firewall"), result.Stderr)
}
return nil
}

View File

@@ -49,7 +49,7 @@ func (m *dnfManager) UpdateCache(ctx context.Context) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("dnf makecache failed"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("dnf makecache failed"), result.Stderr)
}
return nil
}
@@ -61,7 +61,7 @@ func (m *dnfManager) Install(ctx context.Context, packages ...string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("dnf install failed"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("dnf install failed"), result.Stderr)
}
return nil
}
@@ -73,7 +73,7 @@ func (m *dnfManager) Remove(ctx context.Context, packages ...string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("dnf remove failed"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("dnf remove failed"), result.Stderr)
}
return nil
}
@@ -159,7 +159,7 @@ func (m *aptManager) UpdateCache(ctx context.Context) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("apt-get update failed"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("apt-get update failed"), result.Stderr)
}
return nil
}
@@ -171,7 +171,7 @@ func (m *aptManager) Install(ctx context.Context, packages ...string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("apt-get install failed"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("apt-get install failed"), result.Stderr)
}
return nil
}
@@ -183,7 +183,7 @@ func (m *aptManager) Remove(ctx context.Context, packages ...string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("apt-get remove failed"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("apt-get remove failed"), result.Stderr)
}
return nil
}

View File

@@ -46,7 +46,7 @@ func (s *systemd) Start(ctx context.Context, service string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to start"), service, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to start"), service, result.Stderr)
}
return nil
}
@@ -57,7 +57,7 @@ func (s *systemd) Stop(ctx context.Context, service string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to stop"), service, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to stop"), service, result.Stderr)
}
return nil
}
@@ -68,7 +68,7 @@ func (s *systemd) Enable(ctx context.Context, service string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to enable"), service, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to enable"), service, result.Stderr)
}
return nil
}
@@ -79,7 +79,7 @@ func (s *systemd) Disable(ctx context.Context, service string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to disable"), service, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to disable"), service, result.Stderr)
}
return nil
}
@@ -90,7 +90,7 @@ func (s *systemd) Restart(ctx context.Context, service string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to restart"), service, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to restart"), service, result.Stderr)
}
return nil
}
@@ -106,7 +106,7 @@ func (s *systemd) DaemonReload(ctx context.Context) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s: %s", i18n.T().Get("Failed to daemon-reload"), result.Stderr)
return fmt.Errorf("%s: %s", i18n.T.Get("Failed to daemon-reload"), result.Stderr)
}
return nil
}

View File

@@ -52,7 +52,7 @@ func (u *userManager) CreateUser(ctx context.Context, username, groupname string
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to create user"), username, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to create user"), username, result.Stderr)
}
return nil
}
@@ -63,7 +63,7 @@ func (u *userManager) CreateGroup(ctx context.Context, groupname string) error {
return err
}
if result.ExitCode != 0 {
return fmt.Errorf("%s %s: %s", i18n.T().Get("Failed to create group"), groupname, result.Stderr)
return fmt.Errorf("%s %s: %s", i18n.T.Get("Failed to create group"), groupname, result.Stderr)
}
return nil
}

View File

@@ -2,6 +2,7 @@ package ui
import (
"context"
"errors"
"fmt"
"strings"
"time"
@@ -220,7 +221,7 @@ func (a *App) updateLanguageSelect(msg tea.Msg) (tea.Model, tea.Cmd) {
if a.langForm.State == huh.StateCompleted {
locale := gotext.NewLocaleFSWithPath(a.langChoice, embed.LocalesFS, "locales")
locale.AddDomain("helper")
i18n.Init(locale)
i18n.T = locale
a.state = ViewMainMenu
return a, a.initMainMenu()
}
@@ -238,12 +239,12 @@ func (a *App) initMainMenu() tea.Cmd {
a.menuForm = huh.NewForm(
huh.NewGroup(
huh.NewSelect[MenuChoice]().
Title(i18n.T().Get("Select Operation")).
Title(i18n.T.Get("Select Operation")).
Options(
huh.NewOption(i18n.T().Get("Install Panel"), MenuInstall),
huh.NewOption(i18n.T().Get("Uninstall Panel"), MenuUninstall),
huh.NewOption(i18n.T().Get("Disk Partition"), MenuMount),
huh.NewOption(i18n.T().Get("Exit"), MenuExit),
huh.NewOption(i18n.T.Get("Install Panel"), MenuInstall),
huh.NewOption(i18n.T.Get("Uninstall Panel"), MenuUninstall),
huh.NewOption(i18n.T.Get("Disk Partition"), MenuMount),
huh.NewOption(i18n.T.Get("Exit"), MenuExit),
).
Value(&a.menuChoice),
),
@@ -302,10 +303,10 @@ func (a *App) initInstall() tea.Cmd {
a.installForm = huh.NewForm(
huh.NewGroup(
huh.NewConfirm().
Title(i18n.T().Get("Confirm Installation")).
Description(i18n.T().Get("Panel will be installed to %s", a.setupPath)).
Affirmative(i18n.T().Get("Yes")).
Negative(i18n.T().Get("No")).
Title(i18n.T.Get("Confirm Installation")).
Description(i18n.T.Get("Panel will be installed to %s", a.setupPath)).
Affirmative(i18n.T.Get("Yes")).
Negative(i18n.T.Get("No")).
Value(&a.installConfirmed),
),
).WithTheme(huh.ThemeDracula())
@@ -400,19 +401,19 @@ func (a *App) viewInstall() string {
var sb strings.Builder
sb.WriteString(RenderLogo())
sb.WriteString("\n")
sb.WriteString(RenderTitle(i18n.T().Get("Install Panel")))
sb.WriteString(RenderTitle(i18n.T.Get("Install Panel")))
sb.WriteString("\n")
if a.installDone {
if a.installErr != nil {
sb.WriteString(RenderError(i18n.T().Get("Installation failed")))
sb.WriteString(RenderError(i18n.T.Get("Installation failed")))
sb.WriteString("\n\n")
sb.WriteString(ErrorBoxStyle.Render(a.installErr.Error()))
} else {
sb.WriteString(RenderSuccess(i18n.T().Get("Installation successful")))
sb.WriteString(RenderSuccess(i18n.T.Get("Installation successful")))
}
sb.WriteString("\n\n")
sb.WriteString(RenderHelp("Enter", i18n.T().Get("Back")))
sb.WriteString(RenderHelp("Enter", i18n.T.Get("Back")))
} else if a.installRunning {
sb.WriteString(fmt.Sprintf("%s %s\n\n", a.installSpinner.View(), a.installStep))
sb.WriteString(a.installProgress.ViewAs(a.installPercent))
@@ -520,10 +521,10 @@ func (a *App) initUninstallConfirm() {
a.uninstallForm = huh.NewForm(
huh.NewGroup(
huh.NewConfirm().
Title(i18n.T().Get("Confirm Uninstallation")).
Description(i18n.T().Get("Are you sure you want to uninstall the panel?")).
Affirmative(i18n.T().Get("Yes")).
Negative(i18n.T().Get("No")).
Title(i18n.T.Get("Confirm Uninstallation")).
Description(i18n.T.Get("Are you sure you want to uninstall the panel?")).
Affirmative(i18n.T.Get("Yes")).
Negative(i18n.T.Get("No")).
Value(&a.uninstallConfirm),
),
).WithTheme(huh.ThemeDracula())
@@ -551,26 +552,26 @@ func (a *App) viewUninstall() string {
var sb strings.Builder
sb.WriteString(RenderLogo())
sb.WriteString("\n")
sb.WriteString(RenderTitle(i18n.T().Get("Uninstall Panel")))
sb.WriteString(RenderTitle(i18n.T.Get("Uninstall Panel")))
sb.WriteString("\n")
switch a.uninstallState {
case 0: // warning
sb.WriteString(WarningBoxStyle.Render(
RenderWarning(i18n.T().Get("High-risk operation")) + "\n\n" +
i18n.T().Get("Please backup all data before uninstalling.") + "\n" +
i18n.T().Get("All data will be cleared and cannot be recovered!"),
RenderWarning(i18n.T.Get("High-risk operation")) + "\n\n" +
i18n.T.Get("Please backup all data before uninstalling.") + "\n" +
i18n.T.Get("All data will be cleared and cannot be recovered!"),
))
sb.WriteString("\n\n")
sb.WriteString(RenderHelp("Enter", i18n.T().Get("Continue"), "Esc", i18n.T().Get("Back")))
sb.WriteString(RenderHelp("Enter", i18n.T.Get("Continue"), "Esc", i18n.T.Get("Back")))
case 1: // countdown
sb.WriteString(WarningBoxStyle.Render(
fmt.Sprintf("%s\n\n%s %d %s",
i18n.T().Get("For safety, please wait before proceeding"),
i18n.T().Get("Waiting:"),
i18n.T.Get("For safety, please wait before proceeding"),
i18n.T.Get("Waiting:"),
a.uninstallCountdown,
i18n.T().Get("seconds"),
i18n.T.Get("seconds"),
),
))
sb.WriteString("\n\n")
@@ -578,7 +579,7 @@ func (a *App) viewUninstall() string {
sb.WriteString(MutedStyle.Render(fmt.Sprintf("Press Enter %d more times to skip", 10-a.uninstallSkipCount)))
sb.WriteString("\n")
}
sb.WriteString(RenderHelp("Esc", i18n.T().Get("Cancel"), "Enter×10", i18n.T().Get("Skip")))
sb.WriteString(RenderHelp("Esc", i18n.T.Get("Cancel"), "Enter×10", i18n.T.Get("Skip")))
case 2: // confirm
sb.WriteString(a.uninstallForm.View())
@@ -592,16 +593,16 @@ func (a *App) viewUninstall() string {
case 4: // done
if a.uninstallErr != nil {
sb.WriteString(RenderError(i18n.T().Get("Uninstallation failed")))
sb.WriteString(RenderError(i18n.T.Get("Uninstallation failed")))
sb.WriteString("\n\n")
sb.WriteString(ErrorBoxStyle.Render(a.uninstallErr.Error()))
} else {
sb.WriteString(RenderSuccess(i18n.T().Get("Uninstallation successful")))
sb.WriteString(RenderSuccess(i18n.T.Get("Uninstallation successful")))
sb.WriteString("\n\n")
sb.WriteString(i18n.T().Get("Thank you for using AcePanel."))
sb.WriteString(i18n.T.Get("Thank you for using AcePanel."))
}
sb.WriteString("\n\n")
sb.WriteString(RenderHelp("Enter", i18n.T().Get("Back")))
sb.WriteString(RenderHelp("Enter", i18n.T.Get("Back")))
}
return sb.String()
@@ -641,7 +642,7 @@ func (a *App) updateMount(msg tea.Msg) (tea.Model, tea.Cmd) {
}
if len(msg.disks) == 0 {
a.mountDone = true
a.mountErr = fmt.Errorf(i18n.T().Get("No available disks found"))
a.mountErr = errors.New(i18n.T.Get("No available disks found"))
return a, nil
}
a.disks = msg.disks
@@ -739,8 +740,8 @@ func (a *App) initDiskForm() {
a.mountDiskForm = huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title(i18n.T().Get("Select Disk")).
Description(i18n.T().Get("Select a disk to partition and mount")).
Title(i18n.T.Get("Select Disk")).
Description(i18n.T.Get("Select a disk to partition and mount")).
Options(options...).
Value(&a.mountConfig.Disk),
),
@@ -751,12 +752,12 @@ func (a *App) initMountPointForm() {
a.mountPointForm = huh.NewForm(
huh.NewGroup(
huh.NewInput().
Title(i18n.T().Get("Mount Point")).
Description(i18n.T().Get("Enter the mount point path (e.g. /opt/ace)")).
Title(i18n.T.Get("Mount Point")).
Description(i18n.T.Get("Enter the mount point path (e.g. /opt/ace)")).
Value(&a.mountConfig.MountPoint).
Validate(func(s string) error {
if len(s) == 0 || s[0] != '/' {
return fmt.Errorf(i18n.T().Get("Please enter an absolute path"))
return errors.New(i18n.T.Get("Please enter an absolute path"))
}
return nil
}),
@@ -768,10 +769,10 @@ func (a *App) initFSForm() {
a.mountFSForm = huh.NewForm(
huh.NewGroup(
huh.NewSelect[types.FSType]().
Title(i18n.T().Get("File System")).
Description(i18n.T().Get("Select the file system type")).
Title(i18n.T.Get("File System")).
Description(i18n.T.Get("Select the file system type")).
Options(
huh.NewOption(fmt.Sprintf("ext4 (%s)", i18n.T().Get("Recommended")), types.FSTypeExt4),
huh.NewOption(fmt.Sprintf("ext4 (%s)", i18n.T.Get("Recommended")), types.FSTypeExt4),
huh.NewOption("xfs", types.FSTypeXFS),
).
Value(&a.mountConfig.FSType),
@@ -784,14 +785,14 @@ func (a *App) initMountConfirmForm() {
a.mountConfirmForm = huh.NewForm(
huh.NewGroup(
huh.NewConfirm().
Title(i18n.T().Get("Confirm Operation")).
Description(i18n.T().Get("Disk: %s, Mount Point: %s, File System: %s",
Title(i18n.T.Get("Confirm Operation")).
Description(i18n.T.Get("Disk: %s, Mount Point: %s, File System: %s",
a.mountConfig.Disk,
a.mountConfig.MountPoint,
a.mountConfig.FSType,
)).
Affirmative(i18n.T().Get("Yes")).
Negative(i18n.T().Get("No")).
Affirmative(i18n.T.Get("Yes")).
Negative(i18n.T.Get("No")).
Value(&a.mountConfirmed),
),
).WithTheme(huh.ThemeDracula())
@@ -813,35 +814,35 @@ func (a *App) viewMount() string {
var sb strings.Builder
sb.WriteString(RenderLogo())
sb.WriteString("\n")
sb.WriteString(RenderTitle(i18n.T().Get("Disk Partition")))
sb.WriteString(RenderTitle(i18n.T.Get("Disk Partition")))
sb.WriteString("\n")
if a.mountDone {
if a.mountErr != nil {
sb.WriteString(RenderError(i18n.T().Get("Operation failed")))
sb.WriteString(RenderError(i18n.T.Get("Operation failed")))
sb.WriteString("\n\n")
sb.WriteString(ErrorBoxStyle.Render(a.mountErr.Error()))
} else {
sb.WriteString(RenderSuccess(i18n.T().Get("Partition and mount successful")))
sb.WriteString(RenderSuccess(i18n.T.Get("Partition and mount successful")))
sb.WriteString("\n\n")
sb.WriteString(BoxStyle.Render(fmt.Sprintf(
"%s: /dev/%s1\n%s: %s\n%s: %s",
i18n.T().Get("Device"), a.mountConfig.Disk,
i18n.T().Get("Mount Point"), a.mountConfig.MountPoint,
i18n.T().Get("File System"), a.mountConfig.FSType,
i18n.T.Get("Device"), a.mountConfig.Disk,
i18n.T.Get("Mount Point"), a.mountConfig.MountPoint,
i18n.T.Get("File System"), a.mountConfig.FSType,
)))
}
sb.WriteString("\n\n")
sb.WriteString(RenderHelp("Enter", i18n.T().Get("Back")))
sb.WriteString(RenderHelp("Enter", i18n.T.Get("Back")))
return sb.String()
}
switch a.mountState {
case 0: // loading
sb.WriteString(fmt.Sprintf("%s %s", a.mountSpinner.View(), i18n.T().Get("Loading disk list...")))
sb.WriteString(fmt.Sprintf("%s %s", a.mountSpinner.View(), i18n.T.Get("Loading disk list...")))
case 1: // select disk
sb.WriteString(i18n.T().Get("Available disks:"))
sb.WriteString(i18n.T.Get("Available disks:"))
sb.WriteString("\n\n")
for _, disk := range a.disks {
sb.WriteString(fmt.Sprintf(" • %s (%s)\n", disk.Name, disk.Size))
@@ -856,7 +857,7 @@ func (a *App) viewMount() string {
sb.WriteString(a.mountFSForm.View())
case 4: // confirm
sb.WriteString(WarningBoxStyle.Render(i18n.T().Get("Warning: This will erase all data on the disk!")))
sb.WriteString(WarningBoxStyle.Render(i18n.T.Get("Warning: This will erase all data on the disk!")))
sb.WriteString("\n\n")
sb.WriteString(a.mountConfirmForm.View())

View File

@@ -1,398 +0,0 @@
msgid ""
msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"X-Generator: xgotext\n"
#: internal/ui/app.go:558
msgid "All data will be cleared and cannot be recovered!"
msgstr ""
#: internal/ui/app.go:840
msgid "Available disks:"
msgstr ""
#: internal/ui/app.go:415
#: internal/ui/app.go:561
#: internal/ui/app.go:600
#: internal/ui/app.go:831
msgid "Back"
msgstr ""
#: internal/ui/app.go:577
msgid "Cancel"
msgstr ""
#: internal/service/mounter.go:58
msgid "Cannot operate on system disk"
msgstr ""
#: internal/service/installer.go:55
msgid "Checking system requirements"
msgstr ""
#: internal/service/installer.go:61
msgid "Configuring firewall"
msgstr ""
#: internal/ui/app.go:305
msgid "Confirm Installation"
msgstr ""
#: internal/ui/app.go:783
msgid "Confirm Operation"
msgstr ""
#: internal/ui/app.go:561
msgid "Continue"
msgstr ""
#: internal/service/mounter.go:78
msgid "Creating %s..."
msgstr ""
#: internal/service/mounter.go:78
msgid "Creating mount point"
msgstr ""
#: internal/service/mounter.go:108
msgid "Creating partition"
msgstr ""
#: internal/service/mounter.go:108
msgid "Creating partition on /dev/%s..."
msgstr ""
#: internal/service/installer.go:59
msgid "Creating swap file"
msgstr ""
#: internal/service/installer.go:62
msgid "Creating systemd service"
msgstr ""
#: internal/service/installer.go:56
msgid "Creating www user"
msgstr ""
#: internal/service/mounter.go:93
msgid "Deleting existing partitions"
msgstr ""
#: internal/service/mounter.go:93
msgid "Deleting existing partitions..."
msgstr ""
#: internal/service/installer.go:64
msgid "Detecting installed apps"
msgstr ""
#: internal/ui/app.go:825
msgid "Device"
msgstr ""
#: internal/ui/app.go:245
#: internal/ui/app.go:812
msgid "Disk Partition"
msgstr ""
#: internal/service/mounter.go:53
msgid "Disk not found"
msgstr ""
#: internal/service/mounter.go:171
msgid "Disk partition and mount successful"
msgstr ""
#: internal/ui/app.go:784
msgid "Disk: %s, Mount Point: %s, File System: %s"
msgstr ""
#: internal/service/installer.go:60
msgid "Downloading panel"
msgstr ""
#: internal/ui/app.go:522
msgid "Enter 'y' to confirm uninstallation"
msgstr ""
#: internal/ui/app.go:751
msgid "Enter the mount point path (e.g. /opt/ace)"
msgstr ""
#: internal/service/installer.go:79
msgid "Error"
msgstr ""
#: internal/ui/app.go:246
msgid "Exit"
msgstr ""
#: internal/service/mounter.go:112
msgid "Failed to create partition"
msgstr ""
#: internal/service/mounter.go:143
msgid "Failed to get UUID"
msgstr ""
#: internal/ui/app.go:767
#: internal/ui/app.go:827
msgid "File System"
msgstr ""
#: internal/ui/app.go:566
msgid "For safety, please wait before proceeding"
msgstr ""
#: internal/service/mounter.go:126
msgid "Format failed"
msgstr ""
#: internal/service/mounter.go:116
msgid "Formatting /dev/%s1 as %s..."
msgstr ""
#: internal/service/mounter.go:116
msgid "Formatting partition"
msgstr ""
#: internal/ui/app.go:556
msgid "High-risk operation"
msgstr ""
#: internal/service/installer.go:63
msgid "Initializing panel"
msgstr ""
#: internal/ui/app.go:243
#: internal/ui/app.go:403
msgid "Install Panel"
msgstr ""
#: internal/service/installer.go:95
msgid "Installation complete"
msgstr ""
#: internal/ui/app.go:408
msgid "Installation failed"
msgstr ""
#: internal/ui/app.go:412
msgid "Installation successful"
msgstr ""
#: internal/service/installer.go:58
msgid "Installing dependencies"
msgstr ""
#: internal/service/mounter.go:62
msgid "Installing partition tools"
msgstr ""
#: internal/service/mounter.go:62
msgid "Installing partition tools..."
msgstr ""
#: internal/ui/app.go:837
msgid "Loading disk list..."
msgstr ""
#: internal/ui/app.go:750
#: internal/ui/app.go:826
msgid "Mount Point"
msgstr ""
#: internal/service/mounter.go:171
msgid "Mount complete"
msgstr ""
#: internal/service/mounter.go:136
msgid "Mount failed"
msgstr ""
#: internal/service/mounter.go:86
msgid "Mount point is not empty"
msgstr ""
#: internal/service/mounter.go:133
msgid "Mounting /dev/%s1 to %s..."
msgstr ""
#: internal/service/mounter.go:133
msgid "Mounting partition"
msgstr ""
#: internal/ui/app.go:308
#: internal/ui/app.go:790
msgid "No"
msgstr ""
#: internal/ui/app.go:640
msgid "No available disks found"
msgstr ""
#: internal/ui/app.go:817
msgid "Operation failed"
msgstr ""
#: internal/service/installer.go:57
msgid "Optimizing system settings"
msgstr ""
#: internal/service/installer.go:97
msgid "Panel installed successfully"
msgstr ""
#: internal/service/uninstaller.go:48
msgid "Panel is not installed"
msgstr ""
#: internal/service/uninstaller.go:85
msgid "Panel uninstalled successfully"
msgstr ""
#: internal/ui/app.go:306
msgid "Panel will be installed to %s"
msgstr ""
#: internal/ui/app.go:821
msgid "Partition and mount successful"
msgstr ""
#: internal/ui/app.go:557
msgid "Please backup all data before uninstalling."
msgstr ""
#: internal/ui/app.go:755
msgid "Please enter an absolute path"
msgstr ""
#: internal/ui/app.go:770
msgid "Recommended"
msgstr ""
#: internal/service/uninstaller.go:82
msgid "Removing %s..."
msgstr ""
#: internal/service/uninstaller.go:62
msgid "Removing /usr/local/sbin/acepanel..."
msgstr ""
#: internal/service/uninstaller.go:62
msgid "Removing CLI tool"
msgstr ""
#: internal/service/uninstaller.go:82
msgid "Removing installation directory"
msgstr ""
#: internal/service/uninstaller.go:57
msgid "Removing service file"
msgstr ""
#: internal/service/uninstaller.go:66
msgid "Removing swap file"
msgstr ""
#: internal/service/uninstaller.go:66
msgid "Removing swap file..."
msgstr ""
#: internal/service/uninstaller.go:57
msgid "Removing systemd service file..."
msgstr ""
#: internal/ui/app.go:738
msgid "Select Disk"
msgstr ""
#: internal/ui/app.go:241
msgid "Select Operation"
msgstr ""
#: internal/ui/app.go:739
msgid "Select a disk to partition and mount"
msgstr ""
#: internal/ui/app.go:768
msgid "Select the file system type"
msgstr ""
#: internal/ui/app.go:577
msgid "Skip"
msgstr ""
#: internal/service/uninstaller.go:52
msgid "Stopping acepanel service..."
msgstr ""
#: internal/service/uninstaller.go:52
msgid "Stopping panel service"
msgstr ""
#: internal/ui/app.go:597
msgid "Thank you for using AcePanel."
msgstr ""
#: internal/ui/app.go:244
#: internal/ui/app.go:550
msgid "Uninstall Panel"
msgstr ""
#: internal/service/uninstaller.go:85
msgid "Uninstallation complete"
msgstr ""
#: internal/ui/app.go:591
msgid "Uninstallation failed"
msgstr ""
#: internal/ui/app.go:595
msgid "Uninstallation successful"
msgstr ""
#: internal/service/mounter.go:123
msgid "Unsupported filesystem type: %s"
msgstr ""
#: internal/service/mounter.go:140
msgid "Updating /etc/fstab for auto-mount..."
msgstr ""
#: internal/service/mounter.go:140
msgid "Updating fstab"
msgstr ""
#: internal/ui/app.go:567
msgid "Waiting:"
msgstr ""
#: internal/ui/app.go:855
msgid "Warning: This will erase all data on the disk!"
msgstr ""
#: internal/ui/app.go:307
#: internal/ui/app.go:789
msgid "Yes"
msgstr ""
#: internal/service/installer.go:90
msgid "completed"
msgstr ""
#: internal/service/mounter.go:168
msgid "fstab configuration error"
msgstr ""
#: internal/service/uninstaller.go:78
msgid "fstab configuration error, please check /etc/fstab"
msgstr ""
#: internal/ui/app.go:569
msgid "seconds"
msgstr ""

View File

@@ -1,26 +1,9 @@
package i18n
import (
"sync"
"github.com/leonelquinteros/gotext"
)
var (
locale *gotext.Locale
mu sync.RWMutex
T *gotext.Locale
)
// Init 设置全局 locale
func Init(l *gotext.Locale) {
mu.Lock()
defer mu.Unlock()
locale = l
}
// T 获取全局 locale
func T() *gotext.Locale {
mu.RLock()
defer mu.RUnlock()
return locale
}