429 lines
10 KiB
Go
429 lines
10 KiB
Go
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
|
||
}
|