Question: I would like to write multiple languages to a ir.ui.view Object's arch_db field. However, if I provide a dict/json value with languages as keys and HTML as values ({"de_DE"=><german-html>, "en_US"=><american-html>}), validation will fail. If I write html with a de_DE context first, and then with en_US context, the latter will overwrite the former for both languages. How can I write different HTML for different languages? Is there e.g. some way to call update_raw via RPC somehow? Example // This example demonstrates how (not) to create a view with translated HTML content. 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 // stores user id after login // Needed per call: 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, "failed to create login client") } var uid int err = loginClient.Call("authenticate", []any{ odooDB, username, password, map[string]any{}, }, &uid) if err != nil { return nil, wrapErr(err, "failed to authenticate") } client, err := xmlrpc.NewClient(fmt.Sprintf("%s/xmlrpc/2/object", url), nil) if err != nil { return nil, wrapErr(err, "failed to create object client") } 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("no translations provided") } 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("created view with ID %d, Lang %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("updated view with Lang %s, %v, %s", lang, reply, arch[lang]) } return nil, nil } 

Answer: Found solution: One has to use xml_translate to extract translatable terms from arch_db[context lang] and then use it multiple times to extract translations from all arch_db[other lang]. (I built a Flask JSON API to interact with Odoo code not reachable via RPC from Go.) Next, one can create view via RPC (with same context lang) and then provide extracted translations to update_field_translations_sha.