如何通过Odoo RPC为ir.ui.view的arch_db字段写入多语言HTML?
Odoo中为ir.ui.view的arch_db字段添加多语言内容的正确方式
问题描述
我想给ir.ui.view对象的arch_db字段写入多语言内容,但遇到两个问题:
- 直接传入以语言为键、HTML为值的字典/JSON(比如
{"de_DE": "德语HTML", "en_US": "美式英语HTML"})会触发验证失败; - 先在de_DE上下文写入HTML,再切换到en_US上下文写入时,后者会覆盖前者的所有语言内容。
请问如何为不同语言写入对应的HTML?是否可以通过RPC调用update_raw方法?
示例代码(原错误实现)
// 本示例展示错误的多语言视图创建方式 package main import ( "errors" "fmt" "log" "github.com/kolo/xmlrpc" ) func main() { viewArch := TranslatedHTML{ LangDE: `<p>Deutscher Text</p>`, LangEN: `<p>English text</p>`, } cl, err := NewClient( "http://localhost:3017", "odoo_17", "admin", "admin", LangDE, ) panicOnErr(err) reply, err := cl.CreateView(viewArch) panicOnErr(err) fmt.Println(reply) } func panicOnErr(err error) { if err != nil { panic(err) } } func wrapErr(err error, msg string) error { if err != nil { return fmt.Errorf("%s: %w", msg, err) } return nil } type Lang string const LangDE = Lang("de_DE") const LangEN = Lang("en_US") type Client struct { *xmlrpc.Client ContextLang Lang uid int // 存储登录后的用户ID // 每次调用所需参数: OdooDB string Username string Password string } func NewClient(url, odooDB, username, password string, contextLang Lang) (*Client, error) { loginClient, err := xmlrpc.NewClient(fmt.Sprintf("%s/xmlrpc/2/common", url), nil) if err != nil { return nil, wrapErr(err, "创建登录客户端失败") } var uid int err = loginClient.Call("authenticate", []any{ odooDB, username, password, map[string]any{}, }, &uid) if err != nil { return nil, wrapErr(err, "认证失败") } client, err := xmlrpc.NewClient(fmt.Sprintf("%s/xmlrpc/2/object", url), nil) if err != nil { return nil, wrapErr(err, "创建对象客户端失败") } return &Client{client, contextLang, uid, odooDB, username, password}, nil } func (c *Client) WithContextLang(contextLang Lang) *Client { return &Client{c.Client, contextLang, c.uid, c.OdooDB, c.Username, c.Password} } type TranslatedHTML map[Lang]string func (th TranslatedHTML) Langs() []Lang { langs := make([]Lang, 0, len(th)) for lang := range th { langs = append(langs, lang) } return langs } func (cl *Client) ExecuteKW(model, method string, args, reply any) error { return cl.Call( "execute_kw", []any{cl.OdooDB, cl.uid, cl.Password, model, method, args, map[string]any{"context": map[string]string{"lang": string(cl.ContextLang)}}}, reply, ) } func (cl *Client) CreateView(arch TranslatedHTML) (any, error) { langs := arch.Langs() if (len(langs)) == 0 { return nil, errors.New("未提供翻译内容") } firstLang := langs[0] restLangs := langs[1:] var reply any err := cl.WithContextLang(firstLang).ExecuteKW("ir.ui.view", "create", []any{map[string]string{"arch_db": arch[firstLang], "type": "qweb"}}, &reply) if err != nil { return reply, err } log.Printf("已创建视图,ID %d,语言 %s,内容 %s", reply.(int64), firstLang, arch[firstLang]) viewID := reply.(int64) for _, lang := range restLangs { var reply any err := cl.WithContextLang(lang).ExecuteKW("ir.ui.view", "write", []any{viewID, map[string]any{"arch_db": arch[lang]}}, &reply) if err != nil { return reply, err } log.Printf("已更新视图,语言 %s,结果 %v,内容 %s", lang, reply, arch[lang]) } return nil, nil }
解决方案
核心结论
可以通过RPC调用update_raw方法来实现,这是Odoo官方提供的专门用于更新视图多语言架构的方法,不会出现覆盖问题。此外也可以通过操作翻译模型手动添加翻译,以下是具体实现:
方案1:使用update_raw方法(推荐)
ir.ui.view的update_raw方法支持在指定语言上下文下,将传入的HTML作为该语言的翻译版本存储,不会覆盖基准语言或其他语言的内容。步骤如下:
- 先在基准语言(比如en_US)下创建视图;
- 切换到目标语言上下文,调用
update_raw传入对应语言的HTML架构。
修正后的代码实现
// 示例:正确创建带多语言翻译的视图 package main import ( "errors" "fmt" "log" "github.com/kolo/xmlrpc" ) func main() { viewArch := TranslatedHTML{ LangDE: `<p>Deutscher Text</p>`, LangEN: `<p>English text</p>`, } cl, err := NewClient( "http://localhost:3017", "odoo_17", "admin", "admin", LangEN, // 以en_US作为基准语言 ) panicOnErr(err) reply, err := cl.CreateViewWithTranslations(viewArch) panicOnErr(err) fmt.Println(reply) } func panicOnErr(err error) { if err != nil { panic(err) } } func wrapErr(err error, msg string) error { if err != nil { return fmt.Errorf("%s: %w", msg, err) } return nil } type Lang string const LangDE = Lang("de_DE") const LangEN = Lang("en_US") type Client struct { *xmlrpc.Client ContextLang Lang uid int // 存储登录后的用户ID // 每次调用所需参数: OdooDB string Username string Password string } func NewClient(url, odooDB, username, password string, contextLang Lang) (*Client, error) { loginClient, err := xmlrpc.NewClient(fmt.Sprintf("%s/xmlrpc/2/common", url), nil) if err != nil { return nil, wrapErr(err, "创建登录客户端失败") } var uid int err = loginClient.Call("authenticate", []any{ odooDB, username, password, map[string]any{}, }, &uid) if err != nil { return nil, wrapErr(err, "认证失败") } client, err := xmlrpc.NewClient(fmt.Sprintf("%s/xmlrpc/2/object", url), nil) if err != nil { return nil, wrapErr(err, "创建对象客户端失败") } return &Client{client, contextLang, uid, odooDB, username, password}, nil } func (c *Client) WithContextLang(contextLang Lang) *Client { return &Client{c.Client, contextLang, c.uid, c.OdooDB, c.Username, c.Password} } type TranslatedHTML map[Lang]string func (cl *Client) ExecuteKW(model, method string, args, reply any) error { return cl.Call( "execute_kw", []any{cl.OdooDB, cl.uid, cl.Password, model, method, args, map[string]any{"context": map[string]string{"lang": string(cl.ContextLang)}}}, reply, ) } func (cl *Client) CreateViewWithTranslations(arch TranslatedHTML) (any, error) { // 确认基准语言内容存在 baseArch, hasBase := arch[LangEN] if !hasBase { return nil, errors.New("必须提供基准语言en_US的内容") } // 1. 创建基准语言视图 var viewID any err := cl.WithContextLang(LangEN).ExecuteKW("ir.ui.view", "create", []any{map[string]string{"arch_db": baseArch, "type": "qweb"}}, &viewID) if err != nil { return viewID, err } viewIDInt := viewID.(int64) log.Printf("已创建基准视图,ID: %d,语言: %s,内容: %s", viewIDInt, LangEN, baseArch) // 2. 为其他语言添加翻译,使用update_raw方法 for lang, html := range arch { if lang == LangEN { continue // 跳过基准语言 } var reply any // 调用update_raw更新对应语言的视图架构 err := cl.WithContextLang(lang).ExecuteKW("ir.ui.view", "update_raw", []any{viewIDInt, html}, &reply) if err != nil { return reply, wrapErr(err, fmt.Sprintf("更新语言%s失败", lang)) } log.Printf("已更新视图翻译,语言: %s,内容: %s", lang, html) } return viewIDInt, nil }
方案2:手动操作ir.translation模型
如果无法使用update_raw,可以直接向翻译模型添加记录:
- 模型:
ir.translation - 所需字段:
name: 固定为"ir.ui.view,arch_db"res_id: 目标视图的IDlang: 目标语言代码(如de_DE)type: 固定为"model"value: 对应语言的HTML内容
关键说明
- 直接用
write方法会覆盖基准语言内容,因为Odoo默认将传入的arch_db视为基准语言值,而非翻译; - 传入字典格式验证失败,是因为
arch_db字段本身是字符串类型,不支持字典输入; update_raw方法会自动处理翻译逻辑,在对应语言上下文调用时,会将内容存储为该语言的翻译版本。
内容的提问来源于stack exchange,提问作者Jasper
相关产品推荐
相关产品推荐

