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?
// 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
}
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
.