From 481427683ece44a7286c4d86a58d45d33a5233ea Mon Sep 17 00:00:00 2001 From: krau <71133316+krau@users.noreply.github.com> Date: Sun, 8 Jun 2025 13:37:51 +0800 Subject: [PATCH] chore: translate config package --- common/os.go | 18 +++++++++++--- config/viper.go | 16 ++++++++++--- i18n/i18n.go | 52 ++++++++++++++++++++++++++++++++++++++-- i18n/i18nk/keys.go | 6 +++++ i18n/locale/zh-Hans.toml | 12 ++++++++++ 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/common/os.go b/common/os.go index 6b21399..6003b44 100644 --- a/common/os.go +++ b/common/os.go @@ -4,18 +4,30 @@ import ( "os" "path/filepath" "time" + + "github.com/krau/SaveAny-Bot/i18n" + "github.com/krau/SaveAny-Bot/i18n/i18nk" ) func RmFileAfter(path string, td time.Duration) { _, err := os.Stat(path) if err != nil { - Log.Errorf("Failed to create timer for %s: %s", path, err) + Log.Errorf(i18n.T(i18nk.CreateRmTimerFailed, map[string]any{ + "Path": path, + "Error": err, + })) return } - Log.Debugf("Remove file after %s: %s", td, path) + Log.Debugf(i18n.T(i18nk.RemoveFileAfter, map[string]any{ + "Duration": td.String(), + "Path": path, + })) time.AfterFunc(td, func() { if err := os.Remove(path); err != nil { - Log.Errorf("Failed to remove file %s: %s", path, err) + Log.Errorf(i18n.T(i18nk.RemoveFileFailed, map[string]any{ + "Path": path, + "Error": err, + })) } }) } diff --git a/config/viper.go b/config/viper.go index 15197fb..6492cb7 100644 --- a/config/viper.go +++ b/config/viper.go @@ -1,12 +1,15 @@ package config import ( + "errors" "fmt" "os" "strings" "github.com/duke-git/lancet/v2/slice" "github.com/krau/SaveAny-Bot/config/storage" + "github.com/krau/SaveAny-Bot/i18n" + "github.com/krau/SaveAny-Bot/i18n/i18nk" "github.com/spf13/viper" ) @@ -131,18 +134,25 @@ func Init() error { storageNames := make(map[string]struct{}) for _, storage := range Cfg.Storages { if _, ok := storageNames[storage.GetName()]; ok { - return fmt.Errorf("重复的存储名: %s", storage.GetName()) + return errors.New(i18n.TWithoutInit(Cfg.Lang, i18nk.ConfigInvalidDuplicateStorageName, map[string]any{ + "Name": storage.GetName(), + })) } storageNames[storage.GetName()] = struct{}{} } - fmt.Printf("已加载 %d 个存储:\n", len(Cfg.Storages)) + fmt.Println(i18n.TWithoutInit(Cfg.Lang, i18nk.LoadedStorages, map[string]any{ + "Count": len(Cfg.Storages), + })) for _, storage := range Cfg.Storages { fmt.Printf(" - %s (%s)\n", storage.GetName(), storage.GetType()) } if Cfg.Workers < 1 || Cfg.Retry < 1 { - return fmt.Errorf("workers 和 retry 必须大于 0, 当前值: workers=%d, retry=%d", Cfg.Workers, Cfg.Retry) + return errors.New(i18n.TWithoutInit(Cfg.Lang, i18nk.ConfigInvalidWorkersOrRetry, map[string]any{ + "Workers": Cfg.Workers, + "Retry": Cfg.Retry, + })) } for _, storage := range Cfg.Storages { diff --git a/i18n/i18n.go b/i18n/i18n.go index 50486b0..70e3236 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -5,7 +5,6 @@ import ( "maps" - "github.com/krau/SaveAny-Bot/common" "github.com/nicksnyder/go-i18n/v2/i18n" "github.com/pelletier/go-toml/v2" "golang.org/x/text/language" @@ -53,7 +52,56 @@ func T(key string, templateData ...map[string]any) string { TemplateData: templateDataMap, }) if err != nil { - common.Log.Errorf("failed to localize message for key '%s': %v", key, err) + return key + } + return msg +} + +func TWithLang(lang, key string, templateData ...map[string]any) string { + if bundle == nil { + panic("bundle is not initialized, call Init() first") + } + templateDataMap := make(map[string]any) + for _, data := range templateData { + maps.Copy(templateDataMap, data) + } + localizerWithLang := i18n.NewLocalizer(bundle, lang) + msg, err := localizerWithLang.Localize(&i18n.LocalizeConfig{ + MessageID: key, + TemplateData: templateDataMap, + }) + if err != nil { + return key + } + return msg +} + +// Only use in tests or packages that load before i18n +func TWithoutInit(lang, key string, templateData ...map[string]any) string { + bundle := i18n.NewBundle(language.SimplifiedChinese) + bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) + files, err := localesFS.ReadDir("locale") + if err != nil { + return key + } + for _, file := range files { + if _, err := bundle.LoadMessageFileFS(localesFS, "locale/"+file.Name()); err != nil { + return key + } + } + localizer := i18n.NewLocalizer(bundle, lang) + if localizer == nil { + return key + } + templateDataMap := make(map[string]any) + for _, data := range templateData { + maps.Copy(templateDataMap, data) + } + msg, err := localizer.Localize(&i18n.LocalizeConfig{ + MessageID: key, + TemplateData: templateDataMap, + }) + if err != nil { return key } return msg diff --git a/i18n/i18nk/keys.go b/i18n/i18nk/keys.go index b146801..4635b3c 100644 --- a/i18n/i18nk/keys.go +++ b/i18n/i18nk/keys.go @@ -4,9 +4,15 @@ package i18nk const ( CleanCacheFailed = "CleanCacheFailed" CleaningCache = "CleaningCache" + ConfigInvalidDuplicateStorageName = "ConfigInvalid.DuplicateStorageName" + ConfigInvalidWorkersOrRetry = "ConfigInvalid.WorkersOrRetry" + CreateRmTimerFailed = "CreateRmTimerFailed" GetCacheAbsPathFailed = "GetCacheAbsPathFailed" GetWorkdirFailed = "GetWorkdirFailed" InvalidCacheDir = "InvalidCacheDir" + LoadedStorages = "LoadedStorages" + RemoveFileAfter = "RemoveFileAfter" + RemoveFileFailed = "RemoveFileFailed" Bye = "bye" Exiting = "exiting" Initing = "initing" diff --git a/i18n/locale/zh-Hans.toml b/i18n/locale/zh-Hans.toml index 191dc9a..33c4bb7 100644 --- a/i18n/locale/zh-Hans.toml +++ b/i18n/locale/zh-Hans.toml @@ -14,3 +14,15 @@ other = "获取缓存绝对路径失败: {{.Error}}" other = "正在清理缓存文件夹: {{.Path}}" [CleanCacheFailed] other = "清理缓存失败: {{.Error}}" +[CreateRmTimerFailed] +other = "创建清理定时器失败, 路径: {{.Path}}, 错误: {{.Error}}" +[RemoveFileAfter] +other = "将在 {{.Duration}} 后删除文件: {{.Path}}" +[RemoveFileFailed] +other = "删除文件失败: {{.Path}}, 错误: {{.Error}}" +[LoadedStorages] +other = "已加载 {{.Count}} 个存储" +[ConfigInvalid.WorkersOrRetry] +other = "配置无效: workers 或 retry 必须大于 0, 但当前值为: workers={{.Workers}}, retry={{.Retry}}" +[ConfigInvalid.DuplicateStorageName] +other = "存储名称重复: {{.Name}}"