Skip to content

Internationalization

Translate UI strings and record-level prose into the user's preferred language. The framework picks the locale per request from the user's language_id; views, labels, and select-options auto-translate.

# Mark a string as translatable in Python.
from ede.foundation.i18n import _

flash_message = _("Your order has been confirmed.")

# Translate at render time, in the active env's language.
rendered = env.i18n.translate(flash_message)
<!-- All view labels are translatable by default. -->
<field name="title" string="Title"/>

Once a res.language row exists and a translation catalogue is loaded, every reader who selects that language sees the translated text.


What you get

  • res.language — BCP-47 language master (en-US, fr-FR, hi-IN, …).
  • ir.translation — translation rows keyed by source string + target language.
  • _() marker — wraps a string for extraction.
  • env.i18n.translate(source) — runtime translation in the active language.
  • Per-user languageres.user.language_id drives the active locale on every request.
  • View auto-translation<field string="Title"> becomes Titre for French users with zero view edits.
  • Catalogue formats — XML translation files declared in the manifest's data: key.

How to use it

Mark a Python string for translation

from ede.foundation.i18n import _

raise ValueError(_("Quantity must be positive."))

_() is a no-op at runtime in the source locale — it's a marker for the extractor and a hook for translation lookup.

Author a translation catalogue

<!-- src/domains/blog/post/data/translations_fr.xml -->
<translations language="fr-FR">
    <t source="Quantity must be positive.">La quantité doit être positive.</t>
    <t source="Title">Titre</t>
    <t source="Body">Contenu</t>
</translations>

Declare in the manifest:

"data": [
    "data/translations_fr.xml",
    "data/translations_hi.xml",
]

Translate enum / selection labels

state = fields.Enum(
    selection=[
        ("draft",     _("Draft")),
        ("submitted", _("Submitted")),
    ],
)

The label half of each tuple flows through the translator at render time.

Switch the user's language

User picks a language in profile settings → res.user.language_id updates → next request renders in that language.

Reference