i18n翻译
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"epic/internal/consts"
|
||||
"epic/internal/dao"
|
||||
"epic/internal/logic/i18n"
|
||||
"epic/internal/model/dto"
|
||||
"epic/internal/model/entity"
|
||||
"epic/internal/util"
|
||||
@@ -224,9 +225,14 @@ func (t *ThirdPartyDataSync) saveHeroData(ctx context.Context, hero *dto.ThirdPa
|
||||
status60 := hero.CalculatedStatus["Lv60SixStarFullyAwakened"]
|
||||
status60json, _ := gjson.EncodeString(status60)
|
||||
heroJson, _ := gjson.EncodeString(hero)
|
||||
// 使用i18n服务转换字段
|
||||
zhHeroName := i18n.GetZh(ctx, hero.Name)
|
||||
zhRole := i18n.GetZh(ctx, hero.Role)
|
||||
zhAttribute := i18n.GetZh(ctx, hero.Attribute)
|
||||
|
||||
newHero := &entity.EpicHeroInfo{
|
||||
Id: 0,
|
||||
HeroName: "",
|
||||
HeroName: zhHeroName,
|
||||
HeroCode: hero.Code,
|
||||
HeroAttrLv60: status60json,
|
||||
Creator: "",
|
||||
@@ -236,10 +242,10 @@ func (t *ThirdPartyDataSync) saveHeroData(ctx context.Context, hero *dto.ThirdPa
|
||||
Deleted: false,
|
||||
NickName: "",
|
||||
Rarity: strconv.Itoa(hero.Rarity),
|
||||
Role: hero.Role,
|
||||
Role: zhRole,
|
||||
Zodiac: "",
|
||||
HeadImgUrl: "",
|
||||
Attribute: "",
|
||||
Attribute: zhAttribute,
|
||||
Remark: "",
|
||||
RawJson: heroJson,
|
||||
}
|
||||
|
||||
428
internal/logic/i18n/i18n.go
Normal file
428
internal/logic/i18n/i18n.go
Normal file
@@ -0,0 +1,428 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"context"
|
||||
"epic/internal/dao"
|
||||
"epic/internal/model/entity"
|
||||
"epic/internal/util"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// I18nEnToZh 英文->中文映射表(示例)
|
||||
var I18nEnToZh = map[string]string{
|
||||
// 英雄名称示例
|
||||
"Achates": "雅卡泰丝",
|
||||
"Adin": "艾庭",
|
||||
"Angelica": "安洁莉卡",
|
||||
"Bellona": "维尔萝娜",
|
||||
"Charles": "查尔斯",
|
||||
"Charlotte": "夏绿蒂",
|
||||
|
||||
// 职业示例
|
||||
"knight": "骑士",
|
||||
"warrior": "战士",
|
||||
"assassin": "潜行者",
|
||||
"mage": "魔导士",
|
||||
"ranger": "射手",
|
||||
"manauser": "精灵师",
|
||||
|
||||
// 属性示例
|
||||
"fire": "火",
|
||||
"wind": "木",
|
||||
"ice": "水",
|
||||
"dark": "暗",
|
||||
"light": "光",
|
||||
|
||||
// 星座示例
|
||||
"scales": "天秤座",
|
||||
"lion": "狮子座",
|
||||
"waterbearer": "水瓶座",
|
||||
"archer": "射手座",
|
||||
"crab": "巨蟹座",
|
||||
"twins": "双子座",
|
||||
"bull": "金牛座",
|
||||
"fish": "双鱼座",
|
||||
"maiden": "处女座",
|
||||
"goat": "摩羯座",
|
||||
"scorpion": "天蝎座",
|
||||
"ram": "白羊座",
|
||||
|
||||
// 这里可以继续添加你的完整映射...
|
||||
}
|
||||
|
||||
// I18nZhToEn 中文->英文映射表(自动生成)
|
||||
var I18nZhToEn = func() map[string]string {
|
||||
m := make(map[string]string, len(I18nEnToZh))
|
||||
for k, v := range I18nEnToZh {
|
||||
m[v] = k
|
||||
}
|
||||
return m
|
||||
}()
|
||||
|
||||
type Logic struct {
|
||||
cache map[string]map[string]string // lang -> key -> value
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func New() *Logic {
|
||||
return &Logic{
|
||||
cache: make(map[string]map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// 全局i18n实例
|
||||
var globalI18nLogic *Logic
|
||||
var globalI18nMutex sync.RWMutex
|
||||
|
||||
func init() {
|
||||
globalI18nLogic = New()
|
||||
}
|
||||
|
||||
// GetI18nLogic 获取全局i18n实例
|
||||
func GetI18nLogic() *Logic {
|
||||
globalI18nMutex.RLock()
|
||||
defer globalI18nMutex.RUnlock()
|
||||
return globalI18nLogic
|
||||
}
|
||||
|
||||
// LoadFromDB 从数据库加载所有翻译到缓存
|
||||
func (l *Logic) LoadFromDB(ctx context.Context) error {
|
||||
var mappings []*entity.EpicI18NMappings
|
||||
|
||||
err := dao.EpicI18NMappings.Ctx(ctx).
|
||||
Where(dao.EpicI18NMappings.Columns().Status, 1).
|
||||
Where(dao.EpicI18NMappings.Columns().Deleted, 0).
|
||||
Scan(&mappings)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载i18n数据失败: %v", err)
|
||||
}
|
||||
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
// 重新构建缓存
|
||||
l.cache = make(map[string]map[string]string)
|
||||
for _, m := range mappings {
|
||||
if l.cache[m.Language] == nil {
|
||||
l.cache[m.Language] = make(map[string]string)
|
||||
}
|
||||
l.cache[m.Language][m.KeyName] = m.Value
|
||||
}
|
||||
|
||||
util.Info(ctx, "i18n缓存加载完成,共", len(mappings), "条记录")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取指定key的指定语言翻译
|
||||
func (l *Logic) Get(ctx context.Context, lang, key string) string {
|
||||
l.mutex.RLock()
|
||||
defer l.mutex.RUnlock()
|
||||
|
||||
if m, ok := l.cache[lang]; ok {
|
||||
if v, ok := m[key]; ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return key // 找不到返回原文
|
||||
}
|
||||
|
||||
// GetBatch 批量获取翻译
|
||||
func (l *Logic) GetBatch(ctx context.Context, lang string, keys []string) map[string]string {
|
||||
l.mutex.RLock()
|
||||
defer l.mutex.RUnlock()
|
||||
|
||||
result := make(map[string]string)
|
||||
if m, ok := l.cache[lang]; ok {
|
||||
for _, key := range keys {
|
||||
if v, ok := m[key]; ok {
|
||||
result[key] = v
|
||||
} else {
|
||||
result[key] = key // 找不到返回原文
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 语言不存在,全部返回原文
|
||||
for _, key := range keys {
|
||||
result[key] = key
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Refresh 刷新缓存
|
||||
func (l *Logic) Refresh(ctx context.Context) error {
|
||||
return l.LoadFromDB(ctx)
|
||||
}
|
||||
|
||||
// Add 添加翻译
|
||||
func (l *Logic) Add(ctx context.Context, key, lang, value, category string) error {
|
||||
// 先保存到数据库
|
||||
_, err := dao.EpicI18NMappings.Ctx(ctx).Data(g.Map{
|
||||
dao.EpicI18NMappings.Columns().KeyName: key,
|
||||
dao.EpicI18NMappings.Columns().Language: lang,
|
||||
dao.EpicI18NMappings.Columns().Value: value,
|
||||
dao.EpicI18NMappings.Columns().Category: category,
|
||||
dao.EpicI18NMappings.Columns().Status: 1,
|
||||
dao.EpicI18NMappings.Columns().CreateTime: gtime.Now(),
|
||||
dao.EpicI18NMappings.Columns().UpdateTime: gtime.Now(),
|
||||
}).OnDuplicate("value", value).Insert()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("添加翻译失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新缓存
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
if l.cache[lang] == nil {
|
||||
l.cache[lang] = make(map[string]string)
|
||||
}
|
||||
l.cache[lang][key] = value
|
||||
|
||||
util.Info(ctx, "添加翻译:", lang, key, "->", value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update 更新翻译
|
||||
func (l *Logic) Update(ctx context.Context, key, lang, value string) error {
|
||||
// 更新数据库
|
||||
_, err := dao.EpicI18NMappings.Ctx(ctx).
|
||||
Where(dao.EpicI18NMappings.Columns().KeyName, key).
|
||||
Where(dao.EpicI18NMappings.Columns().Language, lang).
|
||||
Data(g.Map{
|
||||
dao.EpicI18NMappings.Columns().Value: value,
|
||||
dao.EpicI18NMappings.Columns().UpdateTime: gtime.Now(),
|
||||
}).Update()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新翻译失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新缓存
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
if l.cache[lang] == nil {
|
||||
l.cache[lang] = make(map[string]string)
|
||||
}
|
||||
l.cache[lang][key] = value
|
||||
|
||||
util.Info(ctx, "更新翻译:", lang, key, "->", value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除翻译
|
||||
func (l *Logic) Delete(ctx context.Context, key, lang string) error {
|
||||
// 软删除数据库记录
|
||||
_, err := dao.EpicI18NMappings.Ctx(ctx).
|
||||
Where(dao.EpicI18NMappings.Columns().KeyName, key).
|
||||
Where(dao.EpicI18NMappings.Columns().Language, lang).
|
||||
Data(g.Map{
|
||||
dao.EpicI18NMappings.Columns().Deleted: 1,
|
||||
dao.EpicI18NMappings.Columns().UpdateTime: gtime.Now(),
|
||||
}).Update()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除翻译失败: %v", err)
|
||||
}
|
||||
|
||||
// 从缓存中删除
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
if m, ok := l.cache[lang]; ok {
|
||||
delete(m, key)
|
||||
}
|
||||
|
||||
util.Info(ctx, "删除翻译:", lang, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByCategory 根据分类获取翻译
|
||||
func (l *Logic) GetByCategory(ctx context.Context, lang, category string) (map[string]string, error) {
|
||||
var mappings []*entity.EpicI18NMappings
|
||||
|
||||
err := dao.EpicI18NMappings.Ctx(ctx).
|
||||
Where(dao.EpicI18NMappings.Columns().Language, lang).
|
||||
Where(dao.EpicI18NMappings.Columns().Category, category).
|
||||
Where(dao.EpicI18NMappings.Columns().Status, 1).
|
||||
Where(dao.EpicI18NMappings.Columns().Deleted, 0).
|
||||
Scan(&mappings)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取分类翻译失败: %v", err)
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
for _, m := range mappings {
|
||||
result[m.KeyName] = m.Value
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ImportFromMap 从map批量导入翻译
|
||||
func (l *Logic) ImportFromMap(ctx context.Context, lang string, mappings map[string]string, category string) error {
|
||||
if len(mappings) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 批量插入数据库
|
||||
var data []g.Map
|
||||
for key, value := range mappings {
|
||||
data = append(data, g.Map{
|
||||
dao.EpicI18NMappings.Columns().KeyName: key,
|
||||
dao.EpicI18NMappings.Columns().Language: lang,
|
||||
dao.EpicI18NMappings.Columns().Value: value,
|
||||
dao.EpicI18NMappings.Columns().Category: category,
|
||||
dao.EpicI18NMappings.Columns().Status: 1,
|
||||
dao.EpicI18NMappings.Columns().CreateTime: gtime.Now(),
|
||||
dao.EpicI18NMappings.Columns().UpdateTime: gtime.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
_, err := dao.EpicI18NMappings.Ctx(ctx).Data(data).OnDuplicate("value", g.Map{
|
||||
dao.EpicI18NMappings.Columns().Value: g.Map{"value": "VALUES(value)"},
|
||||
dao.EpicI18NMappings.Columns().UpdateTime: gtime.Now(),
|
||||
}).Insert()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("批量导入翻译失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新缓存
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
if l.cache[lang] == nil {
|
||||
l.cache[lang] = make(map[string]string)
|
||||
}
|
||||
for key, value := range mappings {
|
||||
l.cache[lang][key] = value
|
||||
}
|
||||
|
||||
util.Info(ctx, "批量导入翻译完成:", lang, category, "共", len(mappings), "条")
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartAutoRefresh 启动自动刷新
|
||||
func (l *Logic) StartAutoRefresh(ctx context.Context) {
|
||||
// 启动时加载
|
||||
if err := l.LoadFromDB(ctx); err != nil {
|
||||
util.Error(ctx, "初始加载i18n缓存失败:", err)
|
||||
}
|
||||
|
||||
// 定时刷新(每小时)
|
||||
go func() {
|
||||
ticker := time.NewTicker(1 * time.Hour)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := l.Refresh(ctx); err != nil {
|
||||
util.Error(ctx, "定时刷新i18n缓存失败:", err)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// En2Zh 英文转中文(静态映射)
|
||||
func En2Zh(s string) string {
|
||||
if v, ok := I18nEnToZh[s]; ok {
|
||||
return v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Zh2En 中文转英文(静态映射)
|
||||
func Zh2En(s string) string {
|
||||
if v, ok := I18nZhToEn[s]; ok {
|
||||
return v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetI18n 获取指定key的指定语言翻译(优先使用数据库,找不到则使用静态映射)
|
||||
func GetI18n(ctx context.Context, lang, key string) string {
|
||||
// 优先使用数据库服务
|
||||
if logic := GetI18nLogic(); logic != nil {
|
||||
result := logic.Get(ctx, lang, key)
|
||||
if result != key {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// 数据库找不到,使用静态映射(仅支持中英文)
|
||||
if lang == "zh" {
|
||||
return En2Zh(key)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// GetZh 获取中文翻译(便捷方法)
|
||||
func GetZh(ctx context.Context, key string) string {
|
||||
return GetI18n(ctx, "zh", key)
|
||||
}
|
||||
|
||||
// GetEn 获取英文翻译(便捷方法)
|
||||
func GetEn(ctx context.Context, key string) string {
|
||||
return GetI18n(ctx, "en", key)
|
||||
}
|
||||
|
||||
// GetI18nBatch 批量获取翻译
|
||||
func GetI18nBatch(ctx context.Context, lang string, keys []string) map[string]string {
|
||||
if logic := GetI18nLogic(); logic != nil {
|
||||
return logic.GetBatch(ctx, lang, keys)
|
||||
}
|
||||
|
||||
// 降级到静态映射
|
||||
result := make(map[string]string)
|
||||
for _, key := range keys {
|
||||
result[key] = GetI18n(ctx, lang, key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetZhBatch 批量获取中文翻译(便捷方法)
|
||||
func GetZhBatch(ctx context.Context, keys []string) map[string]string {
|
||||
return GetI18nBatch(ctx, "zh", keys)
|
||||
}
|
||||
|
||||
// GetEnBatch 批量获取英文翻译(便捷方法)
|
||||
func GetEnBatch(ctx context.Context, keys []string) map[string]string {
|
||||
return GetI18nBatch(ctx, "en", keys)
|
||||
}
|
||||
|
||||
// RefreshI18n 刷新i18n缓存
|
||||
func RefreshI18n(ctx context.Context) error {
|
||||
if logic := GetI18nLogic(); logic != nil {
|
||||
return logic.Refresh(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddI18n 添加翻译
|
||||
func AddI18n(ctx context.Context, key, lang, value, category string) error {
|
||||
if logic := GetI18nLogic(); logic != nil {
|
||||
return logic.Add(ctx, key, lang, value, category)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportI18nFromMap 从map批量导入翻译
|
||||
func ImportI18nFromMap(ctx context.Context, lang string, mappings map[string]string, category string) error {
|
||||
if logic := GetI18nLogic(); logic != nil {
|
||||
return logic.ImportFromMap(ctx, lang, mappings, category)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user