翻译模块¶
本节将介绍如何为您的模块提供翻译功能。
注解
如果您想为Odoo本身的翻译做出贡献,请参考 Odoo Wiki页面。
导出可翻译术语¶
你的模块中有许多术语是“隐式可翻译的”,因此,即使你没有进行任何特定的翻译工作,你也可以导出你的模块可翻译的术语,并找到需要处理的内容。
通过登录后台界面并打开
,可以通过管理界面执行翻译导出操作。将语言保留为默认值(新语言/空模板)
选择 PO 文件 格式
选择您的模块
点击 导出 并下载文件
这将给你一个名为 yourmodule.pot
的文件,应该移动到 yourmodule/i18n/
目录中。该文件是一个 PO 模板,简单地列出可翻译的字符串,可以从中创建实际的翻译文件(PO 文件)。可以使用 msginit 创建 PO 文件,也可以使用专用的翻译工具如 POEdit,或者简单地将模板复制到一个名为 language.po
的新文件中。翻译文件应该放在 yourmodule/i18n/
目录中,与 yourmodule.pot
并列,并且在对应的语言被安装时,Odoo 会自动加载它们(通过 )
注解
在安装或更新模块时,所有已加载语言的翻译也会被安装或更新。
隐式导出¶
Odoo会自动从“data”类型的内容中导出可翻译的字符串:
在非 QWeb 视图中,所有文本节点都会被导出,以及
string
、help
、sum
、confirm
和placeholder
属性的内容QWeb模板(包括服务器端和客户端),所有文本节点都会被导出,除非在
t-translation="off"
块内,title
、alt
、label
和placeholder
属性的内容也会被导出对于
Field
,除非它们的模型被标记为_translate = False
:它们的
string
和help
属性被导出如果
selection
存在且为列表(或元组),则导出如果它们的
translate
属性设置为True
,则会导出它们所有现有的值(在所有记录中)
help/error messages of
_constraints
and_sql_constraints
are exported
显式导出¶
当涉及到Python代码或JavaScript代码中的更”命令式”的情况时,Odoo无法自动导出可翻译的术语,因此必须明确标记以进行导出。这可以通过将文字字符串包装在函数调用中来完成。
In Python, the wrapping function is odoo.api.Environment._()
and 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");
警告
只有字面字符串可以被标记为导出项,不能是表达式或变量。对于需要格式化的字符串,需要标记格式化字符串而不是格式化后的字符串。
The lazy version of _
and _t
is the odoo.tools.translate.LazyTranslate
factory in python and odoo.web._lt()
in javascript.
The translation lookup is executed only
at rendering and can be used to declare translatable properties in class methods
of global variables.
from odoo.tools import LazyTranslate
_lt = LazyTranslate(__name__)
LAZY_TEXT = _lt("some text")
注解
模块的翻译默认情况下 不会 被暴露给前端,因此无法从JavaScript中访问。为了实现这一点,模块名称必须以 website
为前缀(就像 website_sale
, website_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']
上下文¶
To translate, the translation function needs to know the language and the
module name. When using Environment._
the language is known and you
may pass the module name as a parameter, otherwise it’s extracted from the
caller.
In case of odoo.tools.translate._
, the language and the module are
extracted from the context. For this, we inspect the caller’s local variables.
The drawback of this method is that it is error-prone: we try to find the
context variable or self.env
, however these may not exist if you use
translations outside of model methods; i.e. it does not work inside regular
functions or python comprehensions.
Lazy translations are bound to the module during their creation and the
language is resolved when evaluating using str()
.
Note that you can also pass a lazy translation to Envionrment._
to translate it without any magic language resolution.
变量¶
不要 提取可能有效,但它不会正确翻译文本:
_("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'),
};