Files
epic-go/internal/logic/i18n/i18n.go
hu xiaotong c86598e60c i18n翻译
2025-07-14 12:36:07 +08:00

429 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}