i18n翻译

This commit is contained in:
hu xiaotong
2025-07-14 12:36:07 +08:00
parent 22b6b3c93b
commit c86598e60c
4 changed files with 451 additions and 613 deletions

428
internal/logic/i18n/i18n.go Normal file
View 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
}