feat(i18n): integrate i18next for internationalization support and add initial translation setup
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"equipment-analyzer/internal/capture"
|
||||
"equipment-analyzer/internal/config"
|
||||
"equipment-analyzer/internal/model"
|
||||
"equipment-analyzer/internal/optimizer"
|
||||
"equipment-analyzer/internal/utils"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
@@ -83,6 +84,71 @@ func (a *App) Shutdown(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// OptimizeBuilds runs the optimizer on latest parsed data.
|
||||
func (a *App) OptimizeBuilds(req model.OptimizeRequest) (*model.OptimizeResponse, error) {
|
||||
log.Printf("[service] OptimizeBuilds entry heroId=%s", req.HeroID)
|
||||
parsedResult, err := a.GetLatestParsedDataFromDatabase()
|
||||
if err != nil {
|
||||
log.Printf("[service] OptimizeBuilds get data failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if parsedResult == nil || (len(parsedResult.Items) == 0 && len(parsedResult.Heroes) == 0) {
|
||||
log.Printf("[service] OptimizeBuilds no data items=%d heroes=%d", len(parsedResult.Items), len(parsedResult.Heroes))
|
||||
return &model.OptimizeResponse{TotalCombos: 0, Results: []model.OptimizeResult{}}, nil
|
||||
}
|
||||
|
||||
items, err := optimizer.ParseItems(parsedResult.Items)
|
||||
if err != nil {
|
||||
log.Printf("[service] OptimizeBuilds parse items failed: %v", err)
|
||||
return nil, fmt.Errorf("parse items failed: %w", err)
|
||||
}
|
||||
logMissingSetStats(parsedResult.Items)
|
||||
heroes, err := optimizer.ParseHeroes(parsedResult.Heroes)
|
||||
if err != nil {
|
||||
log.Printf("[service] OptimizeBuilds parse heroes failed: %v", err)
|
||||
return nil, fmt.Errorf("parse heroes failed: %w", err)
|
||||
}
|
||||
templates, err := a.parserService.GetHeroTemplates()
|
||||
if err != nil {
|
||||
log.Printf("[service] OptimizeBuilds hero templates missing: %v", err)
|
||||
return nil, fmt.Errorf("未读取到英雄信息数据")
|
||||
}
|
||||
templateHeroes := make([]model.OptimizeHero, 0, len(templates))
|
||||
for _, t := range templates {
|
||||
templateHeroes = append(templateHeroes, model.OptimizeHero{
|
||||
Code: t.Code,
|
||||
Name: t.Name,
|
||||
BaseAtk: t.BaseAtk,
|
||||
BaseDef: t.BaseDef,
|
||||
BaseHp: t.BaseHp,
|
||||
BaseSpd: t.BaseSpd,
|
||||
BaseCr: t.BaseCr,
|
||||
BaseCd: t.BaseCd,
|
||||
BaseEff: t.BaseEff,
|
||||
BaseRes: t.BaseRes,
|
||||
Atk: t.BaseAtk,
|
||||
Def: t.BaseDef,
|
||||
Hp: t.BaseHp,
|
||||
Spd: t.BaseSpd,
|
||||
Cr: t.BaseCr,
|
||||
Cd: t.BaseCd,
|
||||
Eff: t.BaseEff,
|
||||
Res: t.BaseRes,
|
||||
})
|
||||
}
|
||||
// Use template heroes for optimizer to match the template-based selection.
|
||||
heroes = templateHeroes
|
||||
|
||||
log.Printf("[service] OptimizeBuilds parsed items=%d heroes=%d", len(items), len(heroes))
|
||||
resp, err := optimizer.Optimize(items, heroes, req)
|
||||
if err != nil {
|
||||
log.Printf("[service] OptimizeBuilds optimize failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("[service] OptimizeBuilds done results=%d totalCombos=%d", len(resp.Results), resp.TotalCombos)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetNetworkInterfaces returns available network interfaces.
|
||||
func (a *App) GetNetworkInterfaces() ([]model.NetworkInterface, error) {
|
||||
interfaces, err := capture.GetNetworkInterfaces()
|
||||
@@ -93,6 +159,11 @@ func (a *App) GetNetworkInterfaces() ([]model.NetworkInterface, error) {
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
// GetHeroTemplates returns template heroes from local herodata.json.
|
||||
func (a *App) GetHeroTemplates() ([]model.HeroTemplate, error) {
|
||||
return a.parserService.GetHeroTemplates()
|
||||
}
|
||||
|
||||
// StartCapture starts capture on the given interface.
|
||||
func (a *App) StartCapture(interfaceName string) error {
|
||||
if a.captureService.IsCapturing() {
|
||||
@@ -329,6 +400,7 @@ func (a *App) GetLatestParsedDataFromDatabase() (*model.ParsedResult, error) {
|
||||
return nil, fmt.Errorf("failed to unmarshal items: %w", err)
|
||||
}
|
||||
}
|
||||
logMissingSetStats(items)
|
||||
|
||||
var heroes []interface{}
|
||||
if heroesJSON != "" {
|
||||
@@ -424,6 +496,95 @@ func (a *App) GetAllAppSettings() (map[string]string, error) {
|
||||
return a.databaseService.GetAllAppSettings()
|
||||
}
|
||||
|
||||
func logMissingSetStats(items []interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
var total int
|
||||
var emptyF int
|
||||
var emptySet int
|
||||
var healthF int
|
||||
var healthSet int
|
||||
var mapHp int
|
||||
setCounts := map[string]int{}
|
||||
uncategorized := 0
|
||||
emptySetByF := map[string]int{}
|
||||
emptySetByGear := map[string]int{}
|
||||
ingameSetMap := map[string]string{
|
||||
"set_acc": "HitSet",
|
||||
"set_att": "AttackSet",
|
||||
"set_coop": "UnitySet",
|
||||
"set_counter": "CounterSet",
|
||||
"set_cri_dmg": "DestructionSet",
|
||||
"set_cri": "CriticalSet",
|
||||
"set_def": "DefenseSet",
|
||||
"set_immune": "ImmunitySet",
|
||||
"set_max_hp": "HealthSet",
|
||||
"set_penetrate": "PenetrationSet",
|
||||
"set_rage": "RageSet",
|
||||
"set_res": "ResistSet",
|
||||
"set_revenge": "RevengeSet",
|
||||
"set_scar": "InjurySet",
|
||||
"set_speed": "SpeedSet",
|
||||
"set_vampire": "LifestealSet",
|
||||
"set_shield": "ProtectionSet",
|
||||
"set_torrent": "TorrentSet",
|
||||
"set_revenant": "ReversalSet",
|
||||
"set_riposte": "RiposteSet",
|
||||
"set_chase": "PursuitSet",
|
||||
"set_opener": "WarfareSet",
|
||||
}
|
||||
for _, raw := range items {
|
||||
item, ok := raw.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
total++
|
||||
if f, ok := item["f"]; !ok || f == nil || f == "" {
|
||||
emptyF++
|
||||
} else {
|
||||
if f == "set_max_hp" {
|
||||
healthF++
|
||||
}
|
||||
}
|
||||
setValue, _ := item["set"].(string)
|
||||
if setValue == "" {
|
||||
emptySet++
|
||||
fValue, _ := item["f"].(string)
|
||||
if fValue != "" {
|
||||
emptySetByF[fValue] = emptySetByF[fValue] + 1
|
||||
}
|
||||
if gear, ok := item["gear"].(string); ok && gear != "" {
|
||||
emptySetByGear[gear] = emptySetByGear[gear] + 1
|
||||
}
|
||||
if fValue != "" {
|
||||
if mapped, ok := ingameSetMap[fValue]; ok {
|
||||
setCounts[mapped] = setCounts[mapped] + 1
|
||||
} else {
|
||||
uncategorized++
|
||||
}
|
||||
} else {
|
||||
uncategorized++
|
||||
}
|
||||
} else {
|
||||
setCounts[setValue] = setCounts[setValue] + 1
|
||||
}
|
||||
if set, ok := item["set"].(string); ok && set == "HealthSet" {
|
||||
healthSet++
|
||||
}
|
||||
if f, ok := item["f"]; ok && f == "set_max_hp" {
|
||||
mapHp++
|
||||
}
|
||||
}
|
||||
log.Printf("[service] set stats: total=%d empty_f=%d empty_set=%d set_max_hp=%d HealthSet=%d mapped_hp=%d", total, emptyF, emptySet, healthF, healthSet, mapHp)
|
||||
log.Printf("[service] set counts: %+v", setCounts)
|
||||
log.Printf("[service] uncategorized items: %d", uncategorized)
|
||||
if emptySet > 0 {
|
||||
log.Printf("[service] empty set by f: %+v", emptySetByF)
|
||||
log.Printf("[service] empty set by gear: %+v", emptySetByGear)
|
||||
}
|
||||
}
|
||||
|
||||
// StartCaptureWithFilter allows frontend to provide a custom BPF filter.
|
||||
func (a *App) StartCaptureWithFilter(interfaceName string, filter string) error {
|
||||
a.logger.Info("StartCaptureWithFilter requested", "interface", interfaceName, "filter", filter)
|
||||
|
||||
Reference in New Issue
Block a user