Search code examples
odooodoo-16

Odoo 16 translating field through xmlrpc update_field_translations gives error \'str\' object has no attribute \'get\'


I am using xmlrpc to add translation for some fields in my website. I used a function to do this (update_translation). I successfully translated names of the products, and names of categories. But when I try to add translation for website description, I am facing this error -> 'str' object has no attribute 'get'.

import xmlrpc.client


def update_translation(auth_info, view_name, prod_id, field2update, translations):
    """This method is used for updating translation for fields in odoo16

    Args:
        auth_info (dict): dictionary of authentication info
        view_name (str): name of the table, example: product.template
        prod_id (int): id of the record to which we change translations
        field2update (str): name of the field to translate
        translations (dict): dict of translation'
                        {'ar_001': 'translated string in arabic'}
                        or
                        {'en_US': 'Enlgish string',
                        'ar_001': 'translated string in arabic'}

    Returns: 0
    """
    common = xmlrpc.client.ServerProxy(f"{auth_info['url']}/xmlrpc/2/common")
    models = xmlrpc.client.ServerProxy(f"{auth_info['url']}/xmlrpc/2/object")
    version_of_odoo_instance = common.version()
    uid = common.authenticate(auth_info['db'], auth_info['username'],
                              auth_info['password'], version_of_odoo_instance)

    models.execute_kw(auth_info['db'], uid,
                      auth_info['password'], view_name,
                      'update_field_translations',
                      [[prod_id], field2update, translations])
    return 0


# authentication info for the function.
server_1 = {
        'url': "http://mywebsiteip:8069/",
        'db': "database_name",  # name of database 1
        'username': "myusername",
        'password': "mypassword*"
        }

# in the case of both product name and category name, input data was a
# simple string. For website description I am using an html file.
# for each product I have two html files. One for English and One for Arabic.

html_web_descr = {}
# load English translaton to dictionary
html_path = 'fullpath/myfile_en.html'
with open(html_path, 'r', encoding='utf-8-sig') as f:
    data = f.read()
    html_web_descr['en_US'] = data

# load arabic translaton to dictionary
html_path = 'fullpath/myfile_ar.html'
with open(html_path, 'r', encoding='utf-8-sig') as f:
    data = f.read()
    html_web_descr['ar_001'] = data

product_id = 652
# (auth_info, view_name, prod_id, field2update, translations)
update_translation(server_1, 'product.template', product_id,                   'website_description', html_web_descr)

A sample of html file will be like this:

<div>
    <div>
        <h2>Product Description</h2>
        <div class="accordion-body">
            <p>Lot of text here followed by a break<br></p>
        </div>
    </div>
    <div>
        <h2>Product Features</h2>
        <div class="accordion-body">
            <p>Lot of text here followed by a break<br></p>
        </div>
    </div>
    <div>
        <h2>How to use</h2>
        <div class="accordion-body">
            <p>Lot of text here followed by a break<br></p>
        </div>
    </div>
</div>

Trace back of the error:

<Fault 1: 'Traceback (most recent call last):\n  
File "/opt/odoo/odoo-server/odoo/addons/base/controllers/rpc.py", line 148, in xmlrpc_2\n    response = self._xmlrpc(service)\n  
File "/opt/odoo/odoo-server/odoo/addons/base/controllers/rpc.py", line 128, in _xmlrpc\n    result = dispatch_rpc(service, method, params)\n  
File "/opt/odoo/odoo-server/odoo/http.py", line 368, in dispatch_rpc\n    return dispatch(method, params)\n  
File "/opt/odoo/odoo-server/odoo/service/model.py", line 37, in dispatch\n    res = execute_kw(db, uid, *params[3:])\n  
File "/opt/odoo/odoo-server/odoo/service/model.py", line 59, in execute_kw\n    return execute(db, uid, obj, method, *args, **kw or {})\n  
File "/opt/odoo/odoo-server/odoo/service/model.py", line 65, in execute\n    res = execute_cr(cr, uid, obj, method, *args, **kw)\n  
File "/opt/odoo/odoo-server/odoo/service/model.py", line 50, in execute_cr\n    result = retrying(partial(odoo.api.call_kw, recs, method, args, kw), env)\n  
File "/opt/odoo/odoo-server/odoo/service/model.py", line 134, in retrying\n    result = func()\n  
File "/opt/odoo/odoo-server/odoo/api.py", line 461, in call_kw\n    result = _call_kw_multi(method, model, args, kwargs)\n  
File "/opt/odoo/odoo-server/odoo/api.py", line 448, in _call_kw_multi\n    result = method(recs, *args, **kwargs)\n  
File "/opt/odoo/odoo-server/odoo/models.py", line 2987, in update_field_translations\n    return self._update_field_translations(field_name, translations)\n  
File "/opt/odoo/odoo-server/odoo/models.py", line 3075, in _update_field_translations\n    new_translations[lang] = field.translate(translation.get, old_value)\nAttributeError: \'str\' object has no attribute \'get\'\n'>

The function is working fine for name field. So I made sure that website description field also have the same property Translatable:True. Issue happens only through xmlrpc. I can translate the field manually through GUI. Which is not ideal considering I have 1000+ products. Is my approach wrong? How can I solve this?

I tried to read the source code of Odoo 16 to see why it happens it did not make sense to me, I am new to python. Line 2988 of model.py


Solution

  • You need to split your input into parts based on html. From my testing, html will get devided into parts of text in the translation context. the division is done based on ,

    tag etc.

    so to update the tranlation through xmlrpc you need to follow these steps.

    1. First create the description in english. You can use xmlrpc to update the web description field.

    def update_data_in_db(auth_info, view_name, prod_id, dict_of_values_to_change): """This function will update existing data fields with new value

    Args:
        auth_info (dictionary): authentication info for server
        view_name (str): view name for the model we are updating.
        prod_id (int): ID of the product we are editing
        dict_of_values_to_change (dict): a dict of values that needs to be updated
                    {'field_name': new_value}
    """
    common = xmlrpc.client.ServerProxy(f"{auth_info['url']}/xmlrpc/2/common")
    models = xmlrpc.client.ServerProxy(f"{auth_info['url']}/xmlrpc/2/object")
    version_of_odoo_instance = common.version()
    uid = common.authenticate(auth_info['db'], auth_info['username'],
                              auth_info['password'], version_of_odoo_instance)
    
    models.execute_kw(auth_info['db'], uid,
                      auth_info['password'], view_name,
                      'write', [prod_id, dict_of_values_to_change])
    return 0
    
    1. while doing the above step, extract text form the html to get the parts for translation. from my observation, odoo cuts the text at tags and

      tags. So you will endup with a list like this. [Product Description, Lot of text here followed by a break
      ,Product Features, Lot of text here followed by a break
      , How to use, Lot of text here followed by a break
      ]

    2. extract the same from your arabic translaton. so that you will endup with lists of same size. Thus english text and arabic text have the same index id in their corresponding lists.

    3. use zip to make a dictionary from the above lists. I named them english_text, and arabic_text. en_ar_dict = dict(zip(englist_text, arabic_text)) # you will endup with a dictionary like this ==> {'english txt1': 'arabic txt1', 'english txt2': 'arabic txt2'}

    4. Now you can use the same function you used for translationg names. update_translation(server_1, 'product.template', product_id,'website_description', {'ar_001': en_ar_dict)