pythongoodoorpcodoo-17

Provide multiple languages via Odoo RPC call


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
}


Solution

  • 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.