翻译模块

本节介绍如何为您的模块提供翻译功能。

注解

如果您想参与 Odoo 本身的翻译工作,请参考 Odoo Wiki 页面

导出可翻译术语

您的模块中有一些术语是隐式可翻译的。因此,即使您没有针对翻译进行任何具体工作,也可以导出模块中的可翻译术语,并可能找到可供处理的内容。

通过登录后台界面并在 设置 ‣ 翻译 ‣ 导入/导出 ‣ 导出翻译 中打开,可以经由管理界面执行翻译导出。

  • 将语言留空为默认值(新语言/空模板)

  • 选择 PO 文件 格式

  • 选择您的模块

  • 点击 导出 并下载文件

../../_images/po-export.png

这将生成一个名为 yourmodule.pot 的文件,该文件应移动到 yourmodule/i18n/ 目录中。该文件是一个 PO 模板,它仅列出可翻译的字符串,并可用于创建实际的翻译文件(PO 文件)。可以使用 msginit、专用的翻译工具如 POEdit 或者直接将模板复制为一个新文件 language.po 来创建 PO 文件。翻译文件应放在 yourmodule/i18n/ 目录中,与 yourmodule.pot 文件并列,当安装对应语言时(通过 设置 ‣ 翻译 ‣ 语言),Odoo 将会自动加载这些翻译文件。

注解

当安装或更新模块时,所有已加载语言的翻译也会被安装或更新

隐式导出

Odoo 会自动从“data”类型的内容中导出可翻译的字符串:

  • 在非 QWeb 视图中,所有文本节点都会被导出,同时也会导出 stringhelpsumconfirmplaceholder 属性的内容。

  • QWeb 模板(包括服务器端和客户端),所有文本节点都会被导出,除非位于 t-translation="off" 块中,titlealtlabelplaceholder 属性的内容也会被导出。

  • 对于 Field,除非其模型被标记为 _translate = False

    • 它们的 stringhelp 属性会被导出

    • 如果 selection 存在且为列表(或元组),则会被导出

    • 如果它们的 translate 属性设置为 True,则会导出它们的所有现有值(跨所有记录)

  • 帮助/错误信息的 _constraints_sql_constraints 会被导出

显式导出

在涉及 Python 代码或 JavaScript 代码中更“命令式”的场景时,Odoo 无法自动导出可翻译的术语,因此必须显式标记这些术语以便导出。这是通过将字面字符串包裹在函数调用中来实现的。

在 Python 中,包装函数是 odoo.api.Environment._()odoo.tools.translate._()

title = self.env._("Bank Accounts")

# old API for backward-compatibility
from odoo.tools import _
title = _("Bank Accounts")

在 JavaScript 中,包装函数通常是 odoo.web._t()

title = _t("Bank Accounts");

警告

只有字面字符串可以标记为导出,不能是表达式或变量。在字符串需要格式化的情况下,这意味着必须标记格式字符串,而不是格式化后的字符串。

懒版本的 __t 是 Python 中的 odoo.tools.translate.LazyTranslate 工厂和 JavaScript 中的 odoo.web._lt()。翻译查找仅在渲染时执行,可用于在全局变量的类方法中声明可翻译属性。

from odoo.tools import LazyTranslate
_lt = LazyTranslate(__name__)
LAZY_TEXT = _lt("some text")

注解

模块的翻译**默认**不会暴露给前端,因此无法从 JavaScript 中访问。要实现这一点,模块名称必须以 website 为前缀(例如 website_salewebsite_event 等),或者通过为 ir.http 模型实现 _get_translation_frontend_modules_name() 方法来显式注册。

这可能看起来如下:

from odoo import models

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

    @classmethod
    def _get_translation_frontend_modules_name(cls):
        modules = super()._get_translation_frontend_modules_name()
        return modules + ['your_module']

上下文

要进行翻译,翻译函数需要知道*语言*和*模块*名称。当使用 Environment._ 时,语言是已知的,您可以将模块名称作为参数传递,否则会从调用者中提取。

在使用 odoo.tools.translate._ 时,语言和模块是从上下文中提取的。为此,我们会检查调用者的本地变量。这种方法的缺点是容易出错:我们尝试查找上下文变量或 self.env,但如果您在模型方法之外使用翻译,则这些变量可能不存在;即它在普通函数或 Python 推导式中无法正常工作。

延迟翻译在创建时与模块绑定,语言在使用 str() 评估时才会确定。请注意,您也可以将延迟翻译传递给 Envionrment._,以在不进行任何语言解析的情况下进行翻译。

变量

**不要**直接提取,这可能有效,但无法正确翻译文本:

_("Scheduled meeting with %s" % invitee.name)

**请**将动态变量作为翻译查找的参数设置(如果翻译中缺少占位符,将回退到来源):

_("Scheduled meeting with %s", invitee.name)

**不要**将你的翻译拆分成多个块或多行:

# bad, trailing spaces, blocks out of context
_("You have ") + len(invoices) + _(" invoices waiting")
_t("You have ") + invoices.length + _t(" invoices waiting");

# bad, multiple small translations
_("Reference of the document that generated ") + \
_("this sales order request.")

**请**保持在一个段落中,为翻译人员提供完整的上下文::

# good, allow to change position of the number in the translation
_("You have %s invoices wainting") % len(invoices)
_.str.sprintf(_t("You have %s invoices wainting"), invoices.length);

# good, full sentence is understandable
_("Reference of the document that generated " + \
  "this sales order request.")

复数

**不要**按英文方式对术语进行复数形式处理:

msg = _("You have %(count)s invoice", count=invoice_count)
if invoice_count > 1:
  msg += _("s")

**请**注意,每种语言都有不同的复数形式:

if invoice_count > 1:
  msg = _("You have %(count)s invoices", count=invoice_count)
else:
  msg = _("You have one invoice")

读取时 vs 运行时

**不要**在服务器启动时调用翻译查找:

ERROR_MESSAGE = {
  # bad, evaluated at server launch with no user language
  'access_error': _('Access Error'),
  'missing_error': _('Missing Record'),
}

class Record(models.Model):

  def _raise_error(self, code):
    raise UserError(ERROR_MESSAGE[code])

**不要**在读取 JavaScript 文件时调用翻译查找:

# bad, js _t is evaluated too early
var core = require('web.core');
var _t = core._t;
var map_title = {
    access_error: _t('Access Error'),
    missing_error: _t('Missing Record'),
};

**请**使用延迟翻译查找方法:

ERROR_MESSAGE = {
  'access_error': _lt('Access Error'),
  'missing_error': _lt('Missing Record'),
}

class Record(models.Model):

  def _raise_error(self, code):
    # translation lookup executed at error rendering
    raise UserError(ERROR_MESSAGE[code])

或者 执行 动态评估可翻译内容:

# good, evaluated at run time
def _get_error_message(self):
  return {
    access_error: _('Access Error'),
    missing_error: _('Missing Record'),
  }

在翻译查找是在 JS 文件被*读取*时完成的情况下,当*使用*该术语时,请使用 `_lt` 而不是 `_t` 进行翻译

# good, js _lt is evaluated lazily
var core = require('web.core');
var _lt = core._lt;
var map_title = {
    access_error: _lt('Access Error'),
    missing_error: _lt('Missing Record'),
};