feat(i18n): integrate i18next for internationalization support and add initial translation setup

This commit is contained in:
kever
2026-02-17 00:11:42 +08:00
parent dc73f6f6af
commit 9395f9d3af
8 changed files with 1081 additions and 445 deletions

View File

@@ -10,8 +10,12 @@ import (
"fmt"
"io/ioutil"
"math"
"os"
"path/filepath"
"sort"
"net/http"
"strings"
"sync"
"time"
)
@@ -19,6 +23,9 @@ type ParserService struct {
config *config.Config
logger *utils.Logger
hexParser *parser.HexParser
heroBase map[string]heroBaseStats
heroOnce sync.Once
heroErr error
}
func NewParserService(cfg *config.Config, logger *utils.Logger) *ParserService {
@@ -26,6 +33,7 @@ func NewParserService(cfg *config.Config, logger *utils.Logger) *ParserService {
config: cfg,
logger: logger,
hexParser: parser.NewHexParser(),
heroBase: make(map[string]heroBaseStats),
}
}
@@ -207,6 +215,8 @@ func (ps *ParserService) convertSingleItem(item map[string]interface{}) map[stri
// convertUnits 转换英雄数据
func (ps *ParserService) convertUnits(rawUnits []interface{}) []map[string]interface{} {
var convertedUnits []map[string]interface{}
ps.ensureHeroBaseData()
for _, rawUnit := range rawUnits {
if unitMap, ok := rawUnit.(map[string]interface{}); ok {
@@ -225,6 +235,12 @@ func (ps *ParserService) convertUnits(rawUnits []interface{}) []map[string]inter
convertedUnit["awaken"] = z
}
if code, ok := unitMap["code"].(string); ok && code != "" {
if base, ok := ps.heroBase[code]; ok {
ps.applyHeroBase(convertedUnit, base)
}
}
convertedUnits = append(convertedUnits, convertedUnit)
}
}
@@ -520,3 +536,232 @@ func (ps *ParserService) convertItemsAllWithLog(rawItems []interface{}) []map[st
}
return convertedItems
}
type heroBaseStats struct {
Atk float64
Def float64
Hp float64
Spd float64
Cr float64
Cd float64
Eff float64
Res float64
}
type heroDataEntry struct {
Code string `json:"code"`
Name string `json:"name"`
CalculatedStatus struct {
Lv60SixStarFullyAwakened struct {
Atk float64 `json:"atk"`
Def float64 `json:"def"`
Hp float64 `json:"hp"`
Spd float64 `json:"spd"`
Chc float64 `json:"chc"`
Chd float64 `json:"chd"`
Eff float64 `json:"eff"`
Efr float64 `json:"efr"`
} `json:"lv60SixStarFullyAwakened"`
} `json:"calculatedStatus"`
}
func (ps *ParserService) ensureHeroBaseData() {
ps.heroOnce.Do(func() {
homeDir, err := os.UserHomeDir()
if err != nil {
ps.heroErr = err
ps.logger.Error("load hero data failed", "error", err)
return
}
path := filepath.Join(homeDir, ".equipment-analyzer", "herodata.json")
body, err := ioutil.ReadFile(path)
if err != nil {
ps.heroErr = err
ps.logger.Error("load hero data failed", "error", err)
return
}
var raw map[string]heroDataEntry
if err := json.Unmarshal(body, &raw); err != nil {
ps.heroErr = err
ps.logger.Error("load hero data failed", "error", err)
return
}
for _, entry := range raw {
if entry.Code == "" {
continue
}
stats := entry.CalculatedStatus.Lv60SixStarFullyAwakened
ps.heroBase[entry.Code] = heroBaseStats{
Atk: stats.Atk,
Def: stats.Def,
Hp: stats.Hp,
Spd: stats.Spd,
Cr: stats.Chc * 100,
Cd: stats.Chd * 100,
Eff: stats.Eff * 100,
Res: stats.Efr * 100,
}
}
ps.logger.Info("hero base data loaded", "count", len(ps.heroBase))
})
}
func (ps *ParserService) FillHeroBase(heroes []model.OptimizeHero) error {
ps.ensureHeroBaseData()
if len(ps.heroBase) == 0 {
return fmt.Errorf("hero base data not loaded")
}
for i := range heroes {
code := heroes[i].Code
if code == "" {
continue
}
base, ok := ps.heroBase[code]
if !ok {
continue
}
if heroes[i].BaseAtk == 0 {
heroes[i].BaseAtk = base.Atk
}
if heroes[i].BaseDef == 0 {
heroes[i].BaseDef = base.Def
}
if heroes[i].BaseHp == 0 {
heroes[i].BaseHp = base.Hp
}
if heroes[i].BaseSpd == 0 {
heroes[i].BaseSpd = base.Spd
}
if heroes[i].BaseCr == 0 {
heroes[i].BaseCr = base.Cr
}
if heroes[i].BaseCd == 0 {
heroes[i].BaseCd = base.Cd
}
if heroes[i].BaseEff == 0 {
heroes[i].BaseEff = base.Eff
}
if heroes[i].BaseRes == 0 {
heroes[i].BaseRes = base.Res
}
if heroes[i].Atk == 0 {
heroes[i].Atk = base.Atk
}
if heroes[i].Def == 0 {
heroes[i].Def = base.Def
}
if heroes[i].Hp == 0 {
heroes[i].Hp = base.Hp
}
if heroes[i].Spd == 0 {
heroes[i].Spd = base.Spd
}
if heroes[i].Cr == 0 {
heroes[i].Cr = base.Cr
}
if heroes[i].Cd == 0 {
heroes[i].Cd = base.Cd
}
if heroes[i].Eff == 0 {
heroes[i].Eff = base.Eff
}
if heroes[i].Res == 0 {
heroes[i].Res = base.Res
}
}
return nil
}
func (ps *ParserService) GetHeroTemplates() ([]model.HeroTemplate, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, err
}
path := filepath.Join(homeDir, ".equipment-analyzer", "herodata.json")
body, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var raw map[string]heroDataEntry
if err := json.Unmarshal(body, &raw); err != nil {
return nil, err
}
list := make([]model.HeroTemplate, 0, len(raw))
for _, entry := range raw {
if entry.Code == "" {
continue
}
stats := entry.CalculatedStatus.Lv60SixStarFullyAwakened
list = append(list, model.HeroTemplate{
Code: entry.Code,
Name: entry.Name,
BaseAtk: stats.Atk,
BaseDef: stats.Def,
BaseHp: stats.Hp,
BaseSpd: stats.Spd,
BaseCr: stats.Chc * 100,
BaseCd: stats.Chd * 100,
BaseEff: stats.Eff * 100,
BaseRes: stats.Efr * 100,
})
}
sort.Slice(list, func(i, j int) bool {
return list[i].Name < list[j].Name
})
return list, nil
}
func (ps *ParserService) applyHeroBase(unit map[string]interface{}, base heroBaseStats) {
if _, ok := unit["baseAtk"]; !ok {
unit["baseAtk"] = base.Atk
}
if _, ok := unit["baseDef"]; !ok {
unit["baseDef"] = base.Def
}
if _, ok := unit["baseHp"]; !ok {
unit["baseHp"] = base.Hp
}
if _, ok := unit["baseSpd"]; !ok {
unit["baseSpd"] = base.Spd
}
if _, ok := unit["baseCr"]; !ok {
unit["baseCr"] = base.Cr
}
if _, ok := unit["baseCd"]; !ok {
unit["baseCd"] = base.Cd
}
if _, ok := unit["baseEff"]; !ok {
unit["baseEff"] = base.Eff
}
if _, ok := unit["baseRes"]; !ok {
unit["baseRes"] = base.Res
}
if _, ok := unit["atk"]; !ok {
unit["atk"] = base.Atk
}
if _, ok := unit["def"]; !ok {
unit["def"] = base.Def
}
if _, ok := unit["hp"]; !ok {
unit["hp"] = base.Hp
}
if _, ok := unit["spd"]; !ok {
unit["spd"] = base.Spd
}
if _, ok := unit["cr"]; !ok {
unit["cr"] = base.Cr
}
if _, ok := unit["cd"]; !ok {
unit["cd"] = base.Cd
}
if _, ok := unit["eff"]; !ok {
unit["eff"] = base.Eff
}
if _, ok := unit["res"]; !ok {
unit["res"] = base.Res
}
}