翻譯模塊?

本節將介紹如何為您的模塊提供翻譯功能。

注解

如果您想為Odoo本身的翻譯做出貢獻,請參考 `Odoo Wiki頁面<https://github.com/odoo/odoo/wiki/Translations>`_。

導出可翻譯術語?

你的模塊中有許多術語是“隱式可翻譯的”,因此,即使你沒有進行任何特定的翻譯工作,你也可以導出你的模塊可翻譯的術語,并找到需要處理的內容。

通過登錄后臺界面并打開 設置 ? 翻譯 ? 導入/導出 ? 導出翻譯 ,可以通過管理界面執行翻譯導出操作。

  • 將語言保留為默認值(新語言/空模板)

  • 選擇 PO 文件 格式

  • 選擇您的模塊

  • 點擊 導出 并下載文件

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

這將會生成一個名為 yourmodule.pot 的文件,需要將其移動到 yourmodule/i18n/ 目錄下。該文件是一個 PO 模板 ,僅列出可翻譯的字符串,并可用于創建實際的翻譯文件(PO 文件)??梢允褂?msginit 創建 PO 文件,也可以使用專用的翻譯工具如 POEdit,或者簡單地將模板復制到一個名為 language.po 的新文件中。翻譯文件應該放在 yourmodule/i18n/ 目錄下,與 yourmodule.pot 放在一起,當相應的語言被安裝時,Odoo 會自動加載它們 (通過 設置 ? 翻譯 ? 語言

注解

在安裝或更新模塊時,所有已加載語言的翻譯也會被安裝或更新。

隱式導出?

Odoo會自動從“data”類型的內容中導出可翻譯的字符串:

  • 在非 QWeb 視圖中,所有文本節點都會被導出,以及 string 、 help 、 sum 、 confirmplaceholder 屬性的內容

  • 在 QWeb 模板(包括服務器端和客戶端),除了在 t-translation="off" 塊內的所有文本節點都會被導出, title 、 alt 、 labelplaceholder 屬性的內容也會被導出。

  • 對于 Field ,除非它們所屬的模型被標記為 _translate = False

    • 它們的 stringhelp 屬性被導出

    • 如果 selection 存在且為列表(或元組),則導出

    • 如果它們的 translate 屬性設置為 True ,則會導出所有記錄中現有的值

  • _constraints_sql_constraints 的幫助/錯誤信息已導出

顯式導出?

當涉及到Python代碼或JavaScript代碼中的更”命令式”的情況時,Odoo無法自動導出可翻譯的術語,因此必須明確標記以進行導出。這可以通過將文字字符串包裝在函數調用中來完成。

在Python中,包裝函數是 odoo._()

title = _("Bank Accounts")

在JavaScript中,通常使用 :js odoo.web._t() 函數進行包裝:

title = _t("Bank Accounts");

警告

只有字面字符串可以被標記為導出項,不能是表達式或變量。對于需要格式化的字符串,需要標記格式化字符串而不是格式化后的字符串。

__t 的懶惰版本是 Python 中的 odoo._lt() 和 JavaScript 中的 :js odoo.web._lt() 。翻譯查找僅在渲染時執行,可用于在類方法或全局變量中聲明可翻譯屬性。

注解

默認情況下,模塊的翻譯 不會 被公開到前端,因此無法從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']

變量?

不要這樣做 提取可能有效,但它將無法正確翻譯文本:

_("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'),
};