While migrating a client to Odoo 19, the OnlyOffice integration started printing warnings when the module loaded. Nothing was broken yet, but after a major version bump a warning is rarely harmless: it's usually the first sign of what will break at the next version. We traced both causes to Odoo 19 changes, fixed each one, and the fix is now merged into the official ONLYOFFICE module.
Both fixes are tiny: five files, a function rename and a decorator argument. But they're the two most common ways a major Odoo version breaks an integration, and catching them early is exactly the point. Here they are.
Warning #1: translations evaluated too early
First symptom: a stream of no translation language detected warnings at startup. The Selection field labels used the immediate translation function _(), imported from odoo:
from odoo import _, fields, models
internal_users = fields.Selection(
[
("none", _("None")),
("view", _("Viewer")),
("commenter", _("Commenter")),
("reviewer", _("Reviewer")),
("edit", _("Editor")),
("form_filling", _("Form Filling")),
("custom_filter", _("Custom Filter")),
],
...
)A field definition runs when the module is imported, as Odoo builds its registry. At that point there is no user, no request, no language: _() tries to resolve a translation with no context to resolve it against. Older versions let it slide; 19.0 emits a warning.
The fix is the lazy translation function _lt(), in odoo.tools.translate:
from odoo import fields, models
from odoo.tools.translate import _lt
internal_users = fields.Selection(
[
("none", _lt("None")),
("view", _lt("Viewer")),
("commenter", _lt("Commenter")),
("reviewer", _lt("Reviewer")),
("edit", _lt("Editor")),
("form_filling", _lt("Form Filling")),
("custom_filter", _lt("Custom Filter")),
],
...
)_lt() returns a lazy object: it waits until the string is actually rendered, so until a language is present in the context, before looking up its translation. The final output is identical. The rule: for any string evaluated at import time (field labels, Selection options, class-level defaults), use _lt(), not _().
Warning #2: the deprecated type='json' route
The second is a plain rename. Routes declared like this:
@http.route("/onlyoffice/editor/get_config", auth="user", methods=["POST"], type="json", csrf=False)
def get_config(self, document_id=None, attachment_id=None, access_token=None):
...still work in 19.0, but type="json" is now just a deprecated alias for jsonrpc:
@http.route("/onlyoffice/editor/get_config", auth="user", methods=["POST"], type="jsonrpc", csrf=False)Same JSON-RPC dispatch, no behaviour change. But the alias warns today and will be removed in a future version: might as well do the rename now.
Why we sent it upstream
We could have kept this diff in our own fork and moved on. We don't: a private fix decays silently every time the upstream project evolves. Once merged upstream, it becomes the project's responsibility, maintained, run through its CI, and shipped to everyone at the next version, us included. Contributing back isn't a favour, it's the pragmatic choice.
That's also the whole point of the Odoo and OnlyOffice combination for the clients we deploy it for: editing Word, Excel and PowerPoint files directly inside their ERP, on their own infrastructure, with an integration that keeps up as Odoo evolves because we help write it. If you use Odoo and want document editing you truly control, write to us at contact@eclypsys.ch.


