Membaca Source Code Odoo : Multi Bahasa Pada Portal Hanya Diload Sebagian

Artikel ini adalah bagian ketiga dari seri Membaca Source Code Odoo. Pada seri Membaca Source Code Odoo biasanya saya akan menulis tip dan trik tentang bagaimana menyelesaikan suatu problem pada saat membuat module odoo, dengan cara membaca source code odoo langsung. Dengan harapan dapat melatih kita menjadi programmer yang mandiri, tanpa tergantung dari forum odoo atau stackoverflow.

Artikel seri ketiga ini tidak berhubungan dengan seri pertama atau seri kedua, tetapi saya sangat merekomendasikan Anda untuk membaca kedua artikel tersebut, barangkali dapat menambah wawasan dan senjata Anda dalam membuat sebuah module/addon odoo.

Contoh kasus yang akan kita bahas pada artikel ini adalah bagaimana menyelesaikan terjemahan (multi bahasa) yang hanya ter-load sebagian di halaman portal/frontend pada odoo 14. Lebih tepatnya pada halaman portal/frontend yang bersifat single page application yang terpisah dari halaman-halaman odoo lain.

Sebelum memulai pembahasan, silakan download module tutorial_multi_language yang sudah saya siapkan di sini. Install module tersebut. Kemudian buka tab baru dan masukkan url http://localhost:8069/customer-portal. Tampilan awal dari url tersebut akan terlihat seperti pada gambar di bawah ini.

Tampilan awal halaman tutorial multi bahasa odoo

Pada halaman tersebut terdapat 2 buah menu, yaitu menu Sale dan menu Purchase. Jika kita klik pada salah satu menu, misal pada menu Sale, tampilan menu Sale akan dirender tanpa reload halaman secara keseluruhan.

Tampilan halaman sale tutorial multi bahasa odoo

Jika kita klik tombol Save maka sebuah pesan akan ditampilkan dalam sebuah dialog. Seperti pada gambar di bawah ini.

Tampilan pesan tutorial multi bahasa odoo

Pada artikel ini kita akan membahas bagaimana membuat halaman di atas bisa multi bahasa. Sehingga user bisa membuka halaman dengan bahasa yang biasa dia gunakan. Pada tutorial ini saya akan menggunakan bahasa korea selain bahasa inggris. Perlu Anda ingat saya tidak bisa berbahasa korea, bahasa korea yang tampil pada seri artikel ini adalah hasil dari Google Translate, dan saya tidak punya kapasitas untuk memastikan terjemahannya sudah benar atau tidak.

Agar tampilan yang kita buat bisa multi bahasa, ada beberapa hal yang perlu kita perhatikan. Yaitu :

1. Multi bahasa di file XML

Jika kita ingin fitur multi bahasa aktif pada file XML, yang perlu kita lakukan adalah menyediakan file/record terjemahan untuk setiap kata/kalimat yang ada di file XML yang telah kita buat. Jika kata yang ada di file XML memiliki terjemahaannya, pada umumnya odoo secara otomatis akan mereplace kata tersebut dengan kata terjemahan. Sebagai contoh kasus, jika pada file XML ada kata Quantity dan di database odoo ada terjemahan untuk kata Quantity, saat kita merender file XML tersebut di url yang menggunakan bahasa indonesia, kata Quantity akan secara otomatis diganti jadi Jumlah oleh odoo. Jika kata/kalimat itu tidak memiliki terjemahan di database odoo, maka kata/kalimat aslilah yang akan ditampilkan.

2. Multi bahasa di file Python

Multi bahasa di file Python berbeda dengan multi bahasa di file XML, selain kita harus menyediakan file terjemahan untuk setiap kata/kalimat, kita juga harus menAndai secara manual variabel dengan type data string mana saja yang perlu diterjemahkan oleh odoo. Jika kita tidak menAndai variabel string tersebut, odoo tidak akan menerjemahkan kata/kalimat tersebut. Untuk menAndai kata/kalimat tersebut kita bisa menggunakan module translasi yang telah disediakan oleh odoo. Berikut ini adalah cara mengimport module translasi odoo di file Python.

from odoo import _

Module yang digunakan untuk mengaktifkan fitur multi bahasa pada file python adalah module _. Sedangkan cara untuk menAndai string XXX perlu diterjemahkan atau tidak adalah dengan membungkus string tersebut dengan module _, seperti pada kode di bawah ini.

menus = [
    {'menu_id': 'sale', 'label': _('Sale')},
    {'menu_id': 'purchase', 'label': _('Purchase')}
]

Dengan membungkus string dalam module _ odoo secara otomatis akan menerjemahkan string tersebut jika dibutuhkan.

3. Multi bahasa di file JavaScript

Sama dengan multi bahasa di Python, jika kita ingin menggunakan fitur multi bahasa di javascript, kita juga harus membalut kata/kalimat tersebut dengan module translasi. Berikut ini adalah cara mengimport module translasi dan cara penggunaannya di file javascript.

var core = require('web.core');
var _t = core._t;

alert(_t('Good Morning'));

Yang perlu kita perhatikan, jika kode javascript kita dieksekusi sebelum daftar kata terjemahan diload, kode di atas tidak akan berjalan. Jadi kita harus memperhatikan dimana kita meletakkan kode javascript tersebut.

Setelah semua kata/kalimat sudah kita bungkus dengan module translasi, selanjutnya kita bisa menyiapkan file terjemahan untuk kata/kalimat tersebut. Kita tidak perlu menyiapkan file ini secara manual, karena odoo sudah memiliki fitur untuk meng-generate file ini. Caranya, pastikan Anda sudah masuk developer mode, kemudian masuk menu Setting >> Translations >> Export Translation. Sebuah popup akan muncul, isi field Language pada popup tersebut dengan bahasa yang akan Anda gunakan. Kemudian pada field File Format saya menyarankan untuk memilih PO File. Karena menurut saya tipe file ini lebih mudah untuk diedit. Kemudian pada pilihan field Apps to Export pilih nama module/addon yang telah Anda buat.

Generate file multi bahasa pada odoo

Jika bahasa yang ingin Anda gunakan tidak tersedia, Anda harus mengaktifkannya terlebih dahulu. Caranya, pastikan Anda sudah masuk developer mode, kemudian masuk menu Setting >> Translations >> Languages. Kemudian klik tombol Activate jika bahasa yang ingin Anda gunakan belum aktif.

Mengaktifkan bahasa pada odoo

Kembali ke proses generate file terjemahan. Jika Anda sudah menentukan bahasa, jenis file, dan nama module/addon, klik tombol Export, sebuah popup akan muncul.

Download file multi bahasa pada odoo

Klik pada link dengan icon download, maka Anda akan mendapat sebuah file dengan extensi .po yang isinya kira-kira seperti ini.

# Translation of Odoo Server.
# This file contains the translation of the following modules:
# 	* tutorial_multi_language
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-10-19 03:01+0000\n"
"PO-Revision-Date: 2021-10-19 03:01+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: tutorial_multi_language
#. openerp-web
#: code:addonstutorial_multi_language/static/src/xml/templates.xml:0
#: code:addonstutorial_multi_language/static/src/xml/templates.xml:0
#, python-format
msgid "Customer Name"
msgstr ""

Yang perlu Anda lakukan adalah, membuka file .po yang baru saja Anda download dengan sebuah text editor, kemudian menerjemahkan setiap kata yang ditAndai dengan keyword msgid kemudian masukkan hasil terjemahan Anda ke dalam double quote yang ditAndai dengan keyword msgstr. Seperti pada kode di bawah ini.

#. module: tutorial_multi_language
#. openerp-web
#: code:addonstutorial_multi_language/static/src/xml/templates.xml:0
#: code:addonstutorial_multi_language/static/src/xml/templates.xml:0
#, python-format
msgid "Customer Name"
msgstr "고객 이름"

Perlu Anda ingat, kadang terjadi kasus tidak setiap kata/kalimat yang ada di module Anda masuk ke dalam file .po hasil generate otomatis ini, atau, kadang odoo malah memasukkan kata/kalimat yang Anda tahu itu tidak ada di module Anda. Selain itu, jika kata/kalimat yang ada pada module Anda adalah kata yang sudah diterjemahkan oleh module lain, misal kata Save, odoo secara otomatis akan menerjemahkan kata tersebut dan memasukkannya di isian dengan keyword msgstr, yang belum tentu hasil tejemahannya adalah hasil yang Anda inginkan. Jadi, Anda harus memeriksa ulang file hasil generate otomatis ini.

Setelah Anda selesai menerjemahkan setiap kata/kalimat pada file .po buat sebuah folder dengan nama i18n kemudian letakkan file .po yang telah Anda terjemahkan ke dalam folder ini. Anda tidak perlu mengimport folder i18n pada file __manifest__.py. Yang perlu Anda lakukan hanya merestart service odoo kemudian upgrade module.

Setelah proses upgrade module selesai, pastikan kata/kalimat yang telah Anda terjemahkan telah disimpan pada database odoo. Caranya, masuk menu Setting >> Translations >> Translated Term, kemudian filter berdasarkan nama module Anda, seperti pada gambar di bawah ini.

Filter translated term pada odoo

Ok, sekarang masuk pada pokok permasalahan yang sebenarnya. Pada awal tulisan ini, saya telah membuat sebuah single page aplication pada halaman frontend odoo, di alamat http://localhost:8069/customer-portal. Jika saya ingin membuka halaman tersebut dengan bahasa korea, yang perlu saya lakukan adalah menambah URL_Code dari bahasa korea yaitu ko_KP untuk korea utara atau ko_KR untuk korea selatan (tolong koreksi saya jika salah), setelah ip/domain/port server odoo, diikuti dengan route dari controller yang ingin kita akses. Jadi jika saya ingin membuka halaman tersebut dengan bahasa korea utara saya dapat mengetikkan alamat http://localhost:8069/ko_KP/customer-portal di address bar web browser.

Bahasa korea pada halaman portal odoo

Dari gambar di atas terlihat bahwa halaman sudah ditampilkan dalam bahasa korea. Sekarang mari kita coba untuk membuka salah satu menu, misal menu Sale.

Bahasa korea pada halaman portal odoo

Nah, inilah masalah yang akan kita coba untuk selesaikan. Kenapa pada satu halaman tidak semua kata/kalimat diterjemahkan oleh odoo. Kenapa kata/kalimat pada file xml yang dirender oleh controller(python) berhasil diterjemahkan dengan baik, tetapi kata/kalimat pada file xml yang dirender secara dinamis dengan javascript tidak diterjemahkan oleh odoo.

Pertanyaannya, dari mana kita memulai proses penyelesaian masalah ini ?

Berikut ini adalah tips dari saya, jika kita memiliki masalah di frontend, cobalah untuk menginspect elemen yang Anda curigai di Google Chrome Developer Tools. Coba cari class, id atau attribute apapun di element tersebut yang bisa jadi petunjuk mengenai masalah yang kita hadapi. Sebelum menulis artikel ini saya sudah melakukan riset dengan melakukan cara ini, tetapi tidak menemukan apapun yang bisa saya gunakan sebagai petunjuk untuk menyelesaikan masalah ini. Tetapi bukan berarti cara ini tidak efektif. Cara ini akan efektif hanya pada kasus-kasus tertentu, misal pada tulisan saya ini.

Jika cara pertama tidak bisa menyelesaikan masalah, saatnya untuk menggunakan tips yang kedua. Yaitu dengan cara mencari petunjuk di Tab Network pada Google Chrome Developer Tools. Saya juga telah menulis bagaimana menggunakan cara ini di salah satu artikel dari seri Membaca Source Code Odoo. Saya sangat menyarankan Anda untuk membaca artikel tersebut.

Saat kita membuka Tab Network pada Google Chrome Developer Tools, Google Chrome akan menampilkan setiap aksi yang berhubungan dengan komunikasi antara web browser dengan web server. Cobalah untuk menganalisa setiap aksi tersebut. Proses ini memang butuh kesabaran, tetapi jika Anda sudah sering melakukannya, Anda akan terbiasa dan akan mulai mengerti aksi-aksi apa saja yang biasanya dieksekusi oleh odoo, sehingga Anda akan menemukan pola-pola tertentu.

Saat saya melakukan riset dengan cara ini, hal inilah yang saya temukan.

Tab Network pada Google Chrome Developer Tools

Dari gambar di atas terlihat adanya keanehan pada aksi dengan url http://localhost:8069/ko_KP/website/translations/3e3e3662962d53a0d7dce4b78680b64cceb942a3?mods=&lang=en_US. Di browser address bar, jelas-jelas bahasa yang saya pilih adalah bahasa korea (ko_KP), di cookies nilai dari item frontend_lang juga bahasa korea, tetapi kenapa parameter lang pada url tersebut terisi en_US ?

Kita harus mencari tahu siapa yang bertanggung jawab mengisi parameter lang di atas, yaitu dengan cara mencari dimana route /website/translations/ di atas ditulis, baik di controller maupun di file xml atau javascript yang mentrigger url http://localhost:8069/ko_KP/website/translations/3e3e3662962d53a0d7dce4b78680b64cceb942a3?mods=&lang=en_US di atas.

Ada banyak cara untuk mengetahui dimana suatu kode ditulis, tetapi cara yang paling saya suka adalah dengan menggunakan perintah grep bawaan linux. Saya sudah menulis cara penggunaan perintah ini silakan Anda baca di sini.

Menggunakan grep untuk mencari file pada linux

Dari gambar di atas, saat saya mengeksekusi perintah grep dengan keyword website/translations, kata itu ditemukan di 2 file di module http_routing yaitu di controller dengan kode di bawah ini.

@http.route('/website/translations/<string:unique>', type='http', auth="public", website=True)
def get_website_translations(self, unique, lang, mods=None):
    IrHttp = request.env['ir.http'].sudo()
    modules = IrHttp.get_translation_frontend_modules()
    if mods:
        modules += mods
    return WebClient().translations(unique, mods=','.join(modules), lang=lang)

Dan di model dengan kode di bawah ini.

@api.model
def get_frontend_session_info(self):
    session_info = super(IrHttp, self).get_frontend_session_info()

    IrHttpModel = request.env['ir.http'].sudo()
    modules = IrHttpModel.get_translation_frontend_modules()
    user_context = request.session.get_context() if request.session.uid else {}
    lang = user_context.get('lang')
    translation_hash = request.env['ir.translation'].get_web_translations_hash(modules, lang)

    session_info.update({
        'translationURL': '/website/translations',
        'cache_hashes': {
            'translations': translation_hash,
        },
    })
    return session_info

Sayangnya saya tidak menemukan file xml atau javascript yang mentrigger aksi ke http://localhost:8069/ko_KP/website/translations/3e3e3662962d53a0d7dce4b78680b64cceb942a3?mods=&lang=en_US di atas. Menemukan file xml atau javascript yang mentrigger aksi ini sangat penting, karena saya berkeyakinan parameter lang dengan value en_US ini di atur di xml atau javascript secara dinamis.

Sekarang mari kita buat controller dengan route /website/translations di module http_routing di atas menjadi error, dengan harapan kita bisa memperoleh info stack dari aksi-aksi apa saja yang mentrigger controller ini di frontend. Yaitu dengan memaksa error division by zero, seperti pada kode di bawah ini.

@http.route('/website/translations/<string:unique>', type='http', auth="public", website=True)
def get_website_translations(self, unique, lang, mods=None):
    a = 8 / 0
    IrHttp = request.env['ir.http'].sudo()
    modules = IrHttp.get_translation_frontend_modules()
    if mods:
        modules += mods
    return WebClient().translations(unique, mods=','.join(modules), lang=lang)

Setelah kita merestart service odoo, kemudian merefresh web browser, saat odoo mengeksekusi perintah ke http://localhost:8069/ko_KP/website/translations/3e3e3662962d53a0d7dce4b78680b64cceb942a3?mods=&lang=en_US, odoo akan mentrigger internal server error, dengan stack di frontend seperti pada gambar bawah ini.

Stack error javascript pada odoo

Selanjutnya kita tinggal menganalisa setiap baris info stack yang ada di gambar di atas. Tetapi saya memiliki kecurigaan pada 2 method load_translations di atas. Kita bisa mengklik langsung pada nama file dan nomor baris pada info di atas untuk melihat isi dari file tersebut, tetapi karena isi file javascript sudah di minimize, mengakibatkan isi file sulit dibaca. Oleh karena itu saya lebih memilih untuk melakukan grep dengan keyword tersebut, hasilnya terlihat seperti pada gambar di bawah ini.

Mencari file dengan grep pada linux

Dari hasil grep di atas, terlihat bahwa keyword load_translations ada di beberapa file javascript di beberapa module. Saat ini terjadi usahakan untuk mengecek file pada module yang kita ketahui telah kita install terlebih dahulu. Kita bisa mengabaikan file pada module yang kita ketahui tidak kita install, karena secara otomatis file tersebut tidak akan di eksekusi oleh odoo. Selanjutnya jika kita mau mengecek file javascript, usahakan untuk mengecek file pada module dengan nama yang diawali dengan kata web, misal web, web_enterprise, website, website_sale dll.

File pertama yang sebaiknya kita cek adalah file di web/static/src/js/core/translation.js, dimana kode yang mengandung kata load_translations isinya terlihat seperti pada kode di bawah ini.

load_translations: function(session, modules, lang, url) {
    var self = this;
    var cacheId = session.cache_hashes && session.cache_hashes.translations;
    url = url || '/web/webclient/translations';
    url += '/' + (cacheId ? cacheId : Date.now());
    return $.get(url, {
        mods: modules ? modules.join(',') : null,
        lang: lang || null,
    }).then(function (trans) {
        self.set_bundle(trans);
    });
}

Untuk mengetahui bahwa ini adalah file yang tepat atau tidak, kita bisa mengujinya dengan menambah/mengurangi beberapa kode, misal dengan menambah perintah console.log. Akan tetapi, karena method ini hanya menerima inputan parameter lang kita bisa mengabaikan method ini. Yang kita ingin tahu adalah method yang mengatur kenapa parameter lang di isi en_US bukan method yang menerima inputan itu. Jadi walaupun method ini adalah method load_translations yang ada di stack seperti pada gambar di atas, method ini bukanlah method yang kita cari.

Selanjutnya kita bisa memeriksa pada file web/static/src/js/core/session.js. File ini memiliki beberapa baris kode dengan keyword load_translations. Tetapi file ini hanya memiliki satu method dengan nama load_translations, yang isinya terlihat seperti pada kode di bawah ini.

load_translations: function () {
    /* We need to get the website lang at this level.
       The only way is to get it is to take the HTML tag lang
       Without it, we will always send undefined if there is no lang
       in the user_context. */
    var html = document.documentElement,
        htmlLang = (html.getAttribute('lang') || 'en_US').replace('-', '_'),
        lang = this.user_context.lang || htmlLang;

    return _t.database.load_translations(this, this.module_list, lang, this.translationURL);
},

Sepertinya file ini adalah file yang benar, yaitu file yang mengatur parameter lang yang dilewatkan pada url http://localhost:8069/ko_KP/website/translations/3e3e3662962d53a0d7dce4b78680b64cceb942a3?mods=&lang=en_US. Untuk mengujinya mari kita tambah kode console.log seperti pada kode di bawah ini.

load_translations: function () {
    /* We need to get the website lang at this level.
       The only way is to get it is to take the HTML tag lang
       Without it, we will always send undefined if there is no lang
       in the user_context. */
    var html = document.documentElement,
        htmlLang = (html.getAttribute('lang') || 'en_US').replace('-', '_'),
        lang = this.user_context.lang || htmlLang;

    console.log("The loaded lang is ", lang)

    return _t.database.load_translations(this, this.module_list, lang, this.translationURL);
},

Pada tab console Google Chrome Developer Tools, web browser menampilkan pesan seperti pada gambar di bawah ini.

Console log pada google chrome

Dari gambar di atas, dapat dipastikan bahwa method load_translations di file web/static/src/js/core/session.js adalah method yang bertanggung jawab mengatur fitur multi bahasa di halaman portal odoo. Dari kode di atas, value untuk parameter lang diambil dari element html dengan attribute lang atau dari property user_context. Selanjutnya kita tinggal mencari tahu bagaimana menambah element dengan attribute lang atau menambah user_context secara dinamis.

Tetapi sebelum melakukan itu semua, ada satu tips yang ingin saya bagi kepada Anda. Yaitu selalu bandingkan source code yang ada di komputer/server Anda dengan source code yang ada di github odoo. Barangkali source code yang ada di komputer/server Anda versinya terlalu lama dan memiliki bug, sedangkan source code yang ada di github sudah diperbaiki. Saat saya coba membandingkan isi dari file web/static/src/js/core/session.js dengan file yang ada di github, saya menemukan pesan commit ini.

Pesan commit perbaikan bug pada odoo

Sepertinya parameter lang yang selalu terisi en_US padahal kita sudah memilih bahasa lain adalah sebuah bug. Setelah saya lihat, isi dari method load_translations sudah diubah. Dimana isinya menjadi seperti ini.

load_translations: function () {
    var lang = this.user_context.lang
    /* We need to get the website lang at this level.
       The only way is to get it is to take the HTML tag lang
       Without it, we will always send undefined if there is no lang
       in the user_context. */
    var html = document.documentElement,
        htmlLang = html.getAttribute('lang');
    if (!this.user_context.lang && htmlLang) {
        lang = htmlLang.replace('-', '_');
    }

    return _t.database.load_translations(this, this.module_list, lang, this.translationURL);
},
 

Sekarang mari kita terapkan perubahan dari github odoo di atas pada file yang ada di komputer/server kita.

Setelah kita menghapus kode yang memaksa error division by zero, di module http_routing, merestart service odoo, kemudian merefresh web browser, hasilnya menjadi seperti ini.

Bahasa korea pada halaman portal odoo

Sekarang parameter lang pada url http://localhost:8069/ko_KP/website/translations/3e3e3662962d53a0d7dce4b78680b64cceb942a3?mods=&lang= tidak memiliki nilai. Sekarang mari kita buka salah satu menu.

Bahasa korea pada halaman portal odoo

Sekarang tombol Save pada menu Sale sudah diterjemahkan, tetapi kata yang lain belum. Masih belum menyelesaikan masalah, tapi paling tidak lebih baik daripada yang sebelumnya.

Selanjutnya kita perlu mencari tahu kenapa kata Save diterjemahkan, sedangkan kata yang lain tidak. Untuk mengetahui hal itu kita harus mencari tahu kapan kata hasil terjemahan diload. Dan method apa yang menangani hal itu.

Perlu Anda ketahui, saat Anda melihat daftar kata/kalimat yang sudah diterjemahkan pada menu Setting >> Translations >> Translated Term, dari menu itu kita bisa melihat bahwa file terjemahan disimpan di model ir.translation (perhatikan address bar di web browser Anda). Maka kita bisa mengoverride method search_read dan memaksa method itu agar error division by zero, sehingga kita bisa mendapatkan info stack method apa saja dan di file apa saja yang mentrigger method search_read ini. Perhatikan kode di bawah ini.

class IrTranslation(models.Model):
    _inherit = "ir.translation"
    
    @api.model
    def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
        res = super(IrTranslation, self).search_read(domain=domain, fields=fields, offset=offset, limit=limit, order=order)
        for r in res:
            if r.get('src','') == 'Save':
                a = 8 / 0
        return res

Maksud kode di atas adalah, jika odoo memanggil method search_read untuk mencari dan membaca kata/kalimat terjemahan di model ir.translation dan hasilnya ada record dengan field src (kata/kalimat asli) yang bernilai Save kita akan memaksa odoo untuk error division by zero. Kenapa kita memilih kata Save ? Ingat, pada gambar terakhir kata Save sudah diterjemahkan. Jadi kita bisa memastikan saat odoo membaca record terjemahan dari kata Save yang memerintahkan proses pembacaan itu pasti ada hubungannya dengan customer portal.

Setelah kita restart service odoo, kemudian refresh halaman customer portal (ingat, jangan refresh halaman erp odoo, karena disana ada tombol dengan label Save juga) pesan errornya terlihat seperti di bawah ini.

http: 500 Internal Server Error:

Traceback (most recent call last):
  File "/odoo14/odoo14-server/odoo/addons/base/models/ir_http.py", line 237, in _dispatch
    result = request.dispatch()
  File "/odoo14/odoo14-server/odoo/http.py", line 806, in dispatch
    r = self._call_function(**self.params)
  File "/odoo14/odoo14-server/odoo/http.py", line 359, in _call_function
    return checked_call(self.db, *args, **kwargs)
  File "/odoo14/odoo14-server/odoo/service/model.py", line 94, in wrapper
    return f(dbname, *args, **kwargs)
  File "/odoo14/odoo14-server/odoo/http.py", line 347, in checked_call
    result = self.endpoint(*a, **kw)
  File "/odoo14/odoo14-server/odoo/http.py", line 912, in __call__
    return self.method(*args, **kw)
  File "/odoo14/odoo14-server/odoo/http.py", line 531, in response_wrap
    response = f(*args, **kw)
  File "/odoo14/odoo14-server/addons/http_routing/controllers/main.py", line 17, in get_website_translations
    return WebClient().translations(unique, mods=','.join(modules), lang=lang)
  File "/odoo14/odoo14-server/odoo/http.py", line 531, in response_wrap
    response = f(*args, **kw)
  File "/odoo14/odoo14-server/addons/web/controllers/main.py", line 1030, in translations
    translations_per_module, lang_params = request.env["ir.translation"].get_translations_for_webclient(mods, lang)
  File "/odoo14/odoo14-server/odoo/addons/base/models/ir_translation.py", line 896, in get_translations_for_webclient
    ['module', 'src', 'value', 'lang'], order='module')
  File "/odoo14/custom/tutorial/tutorial_multi_language/models/models.py", line 21, in search_read
    a = 8 / 0
Exception

Sekarang kita sudah memiliki info stack mengenai method/file apa saja yang mentrigger method search_read di model ir.translation. Sekarang mari kita analisa satu-persatu.

Pertama mari kita analisa method get_translations_for_webclient di baris 896 di file /odoo14/odoo14-server/odoo/addons/base/models/ir_translation.py. Silakan lihat isi file tersebut di halaman github odoo. Perhatikan domain yang dilewatkan pada method search_read, yang isinya terlihat seperti ini.

[('module', 'in', mods), ('lang', '=', lang),
('comments', 'like', 'openerp-web'), ('value', '!=', False),
('value', '!=', '')],

Dari domain di atas, terlihat bahwa agar kata/kalimat yang telah kita terjemahkan diload oleh odoo, ada beberapa syarat yang harus dipenuhi. Yang pertama adalah nama module yang kita tulis masuk dalam daftar. Oleh karena itu mari kita cek, apakah module yang telah saya buat dengan nama tutorial_multi_language ada dalam daftar atau tidak, dengan menambah perintah print, seperti pada kode di bawah ini.

@api.model
def get_translations_for_webclient(self, mods, lang):
    print(mods)

Dimana hasilnya terlihat seperti pada list di bawah ini.

['web', 'web_editor', 'portal', 'website_studio', 'website_sms', 'website', 'website_enterprise', 'website_mail', 'website_form', 'payment']

Ternyata module yang saya buat (tutorial_multi_language) tidak ada dalam daftar itu. Lalu apa yang harus kita lakukan agar module yang kita buat masuk dalam daftar ? Pertama, kita harus mencari tahu method apa dan di file apa yang mengatur daftar nama module ini.

Mari kita analisa stack yang kedua, yaitu method translations di baris 1030 di file /odoo14/odoo14-server/addons/web/controllers/main.py. Silakan lihat isi file di halaman github odoo ini. Method tersebut cuma menerima daftar module yang dilewatkan di parameter mods kemudian melewatkannnya lagi ke method get_translations_for_webclient, jadi method ini bukan yang kita cari.

Sekarang mari kita analisa stack yang ketiga, yaitu method get_website_translations di baris 17 pada file /odoo14/odoo14-server/addons/http_routing/controllers/main.py. Silakan lihat isi file tersebut di halaman github odoo ini.

Sepertinya ini adalah method yang kita cari, method yang mengatur daftar module yang perlu diload file terjemahannya di portal. Perhatikan kode di bawah ini.

modules = IrHttp.get_translation_frontend_modules()

Ternyata daftar module yang perlu diload file terjemahannya di atur di method get_translation_frontend_modules. Mari kita grep dengan keyword get_translation_frontend_modules untuk mencari tahu isi dari method tersebut, sehingga kita bisa mengoverridenya. Beginilah hasilnya di komputer saya.

Mencari file dengan grep di linux

Sekarang mari kita lihat isi file ir_http.py di module portal.

@classmethod
def _get_translation_frontend_modules_name(cls):
    mods = super(IrHttp, cls)._get_translation_frontend_modules_name()
    return mods + ['portal']

Ternyata nama module harus ditambahkan secara manual agar kata/kalimat terjemahan bisa diload oleh odoo, yaitu dengan cara mengoverride method _get_translation_frontend_modules_name seperti pada module portal di atas. Ok, sekarang dengan cara yang sama, mari kita tambahkan nama module yang telah kita buat.

class IrHttp(models.AbstractModel):
    _inherit = 'ir.http'

    @classmethod
    def _get_translation_frontend_modules_name(cls):
        mods = super(IrHttp, cls)._get_translation_frontend_modules_name()
        return mods + ['tutorial_multi_language']

Hapus/comment kode yang memaksa error division by zero di model ir.translation, restart service odoo, kemudian refresh web browser.

Bahasa korea pada halaman portal odoo

Akhirnya kata/kalimat yang sebelumnya belum diterjemahkan oleh odoo, sekarang sudah diterjemahkan dengan benar. Ok, sekarang mari kita tes dengan cara mengklik tombol Save untuk melihat pesan error yang diload lewat ajax.

Bahasa korea pada halaman portal odoo

Pesan errornya juga sudah diterjemahkan. Berarti semua halaman sudah diterjemahkan dengan baik oleh odoo.

Tulisan Serupa

Leave a Reply