Translation 多语言模型
使用 JSON 字段保存模型翻译内容,支持默认语言、回退语言和原始字段回退。
extensions/translation 提供模型字段级多语言。默认 backend 是单个 JSON 字段 translations,业务字段仍保留为真实列,用来保存原始值,也作为最后兜底值。
import "github.com/duxweb/oro/extensions/translation"
安装
打开数据库时设置默认语言、回退语言和需要翻译的字段:
db, err := oro.Open(oro.Config{
Connections: map[string]oro.ConnectionConfig{
"default": {Driver: sqlite.Open("app.db")},
},
Extensions: []oro.Extension{
translation.Extension(
translation.DefaultLocale("zh-CN"),
translation.FallbackLocale("en-US"),
translation.TranslatedFields("Name", "Description"),
),
},
})
字段取值顺序是:
- 当前语言;
- 回退语言;
- 模型原始字段值。
模型字段
嵌入 translation.Fields,把需要翻译的字段定义为普通字段:
type Product struct {
oro.Model
translation.Fields
Code string
Name string
Description string
}
func (Product) Define(s *oro.SchemaBuilder) {
s.Table("products")
s.Field("Code").String().Unique()
s.Field("Name").String().Nullable()
s.Field("Description").Text().Nullable()
}
translation.Fields 会自动定义隐藏 JSON 字段 Translations -> translations。翻译字段不要定义成 Virtual(),因为原始值兜底需要真实列保存基础内容。
统一 Apply 入口
translation 的查询、读取转换和写入翻译值都通过核心查询链的 Apply 完成,因此可以和普通 Where、With、OrderBy、分页等方法自然组合,不需要扩展包维护另一套查询 API。
旧的 translation.Use[Product](db) 仍作为兼容薄封装存在;新代码推荐直接使用 db.Use[Product]().Apply(...)。
一次创建多语言
created, err := db.Use[Product]().
Apply(translation.Write(translation.Values{
"zh-CN": oro.Map{
"Name": "苹果",
"Description": "红色苹果",
},
"en-US": oro.Map{
"Name": "Apple",
"Description": "Red apple",
},
})).
Create(ctx, &Product{Code: "P001"})
传入 translation.Values 时,扩展会保存完整翻译 JSON,并把默认语言的值写入原始字段;如果模型原始字段已有非零值,则保留原始字段值。
创建当前语言
created, err := db.Use[Product]().
Apply(translation.Locale("zh-CN")).
Create(ctx, &Product{
Code: "P002",
Name: "梨子",
Description: "黄色梨子",
})
不传 translation.Values 时,Create 会把非零翻译字段写入当前语言,同时普通列仍保存原始值。
读取与回退
product, err := db.Use[Product]().
Apply(translation.Locale("ja-JP"), translation.Fallback("en-US")).
Find(ctx, id)
如果 ja-JP.Name 不存在,会使用 en-US.Name。如果回退语言也不存在,就保留模型原始字段值。
也可以通过 context.Context 传递语言:
ctx = translation.WithLocale(ctx, "ja-JP")
ctx = translation.WithFallback(ctx, "en-US")
product, err := db.Use[Product]().Apply(translation.Configured()).Find(ctx, id)
更新翻译
更新当前语言:
_, err := db.Use[Product]().
Apply(translation.Locale("zh-CN")).
Where("ID", id).
Update(ctx, oro.Map{"Name": "新梨子"})
一次更新多个语言:
_, err := db.Use[Product]().
Apply(translation.Write(translation.Values{
"en-US": oro.Map{"Name": "Pear"},
})).
Where("ID", id).
Update(ctx, nil)
翻译更新会先读取当前记录,再合并到已有 translations JSON,不会删除其他语言。为了避免把同一份 JSON 合并到多条记录,带翻译值的 Update 只允许命中一条记录。
查询翻译值
product, err := db.Use[Product]().
Apply(translation.Locale("en-US"), translation.WhereTrans("Name", "Apple")).
First(ctx)
WhereTrans 查询当前语言在 JSON 字段里的值。
使用 WhereTransLike 可以对当前语言的翻译字段做 LIKE 模糊匹配:
products, err := db.Use[Product]().
Apply(translation.Locale("en-US"), translation.WhereTransLike("Name", "%App%")).
Get(ctx)
未配置到 TranslatedFields 的字段不能使用 WhereTrans 或 WhereTransLike。
模型辅助 API
直接操作模型翻译内容:
name := translation.Translate(product, "zh-CN", "en-US").String("Name")
err := translation.Translate(product, "zh-CN").Set("Name", "葡萄")
Translate(...).String("Name") 同样按当前语言、回退语言、原始字段值的顺序取值。