Internationalization

Giới thiệu chung

Odoo hỗ trợ đa ngôn ngữ và cho phép các người dùng khác nhau trên cùng một instance sử dụng các ngôn ngữ khác nhau. Điều này được thực hiện bởi tính năng i18n của Odoo.

Trong bài viết này, chúng ta sẽ tìm hiểu cách kích hoạt đa ngôn ngữ trong Odoo và cách thêm các file dịch cho một module mới.

Cài đặt ngôn ngữ mới và cấu hình ngôn ngữ của người dùng

Khi được cài đặt lần đầu, chỉ có một ngôn ngữ tiếng Anh mặc định. Để có các ngôn ngữ các, chúng ta cần cài đặt chúng. Ví dụ dưới đây sẽ cài đặt và chuyển ngôn ngữ của người dùng sang Tiếng Việt.

  1. Đầu tiên, chúng ta đăng nhập với tài khoản admin và kích hoạt chế độ developer mode, Để kích hoạt bạn vào Setting ‣ General Settings ‣ Developer Tools ‣ Activate the developer mode hoặc thêm ?debug=1 vào trên url của trình duyệt như trong hình.

    Kích hoạt deverloper tool Kích hoạt deverloper tool url
  2. Tiếp theo chúng ta cài đặt một ngôn ngữ mới bằng cách vào Setting ‣ General Settings ‣ Translations ‣ Languages. Sau đó tìm một ngôn ngữ (Tiếng Việt) và nhấn Activate/Update ‣ Add để cài đặt.

    Cài đặt ngôn ngữ mới, ảnh 1 cài đặt ngôn ngữ mới, ảnh 2

    Ghi chú

    Bạn có thể cài đặt một ngôn ngữ mới bằng cách thêm --load-language=<language> vào cấu hình chạy của odoo. Ví dụ: $ ./odoo-bin -d mydb --load-language=vi_VN. Nếu checkbox Overwrite Existing Terms không được tích, sẽ chỉ có những thuật ngữ mới sẽ được dịch. Tích vào nó nếu bạn muốn cập nhật lại các thuật ngữ đã được dịch trước đó, việc này cũng có thể làm mất các bản dịch mà bạn đã sửa thủ công trên giao diện.

  3. Lúc này bạn có thể chuyển ngôn ngữ của user sang Tiếng Việt luôn bằng cách nhấn Switch to Vietnamese/Tiếng Việt & Close. Để thay đổi lại ngôn ngữ của user, chúng ta click vào Ảnh đại diện của user ‣ Tùy chỉnh cá nhân, chọn một ngôn ngữ khác và bấm Lưu. Các user khác cũng làm tương tự như vậy.

    Thay đổi ngôn ngữ của user

Chỉnh sửa thông tin bản địa của ngôn ngữ

Khi chuyển sang một ngôn ngữ mới, các thông tin về định dạng ngày giờ, dấu phân cách thập phân,... cũng thay đổi để phù hợp với ngôn ngữ/quốc gia đó. Nếu bạn thích tiếng Việt nhưng vẫn muốn giữ định dạng ngày giờ của tiếng Anh thì có thể vào ngôn ngữ đó để chỉnh sửa lại.

Chỉnh sửa thông tin bản địa của ngôn ngữ

Thêm bản dịch cho một module mới

Khi tạo một module mới, thông thường chúng ta sẽ viết các đoạn code và thông điệp bằng tiếng Anh. Để quốc tế hóa module, chúng ta cần phải xuất các file dịch từ module cho các ngôn ngữ khác nhau. Ví dụ với module viin_education:

viin_education
├── i18n
├── models
│   ├── education_student.py
│   └── __init__.py
├── security
└── views
├── __init__.py
├── __manifest__.py

File education_student.py:

from odoo import models, fields, _
from odoo.exceptions import UserError


class EducationStudent(models.Model):
    _name = 'education.student'
    _description = 'Education Student'

    name = fields.Char(string='Name')
    student_code = fields.Char(string='Student Code')
    note = fields.Text(string='Note', help="Brief notes about students")

    _sql_constraints = [
        ('student_code_unique',
        'UNIQUE(student_code)',
        "Student Code must be unique!"),
    ]

    def copy(self, default=None):
        self.ensure_one()
        raise UserError(_("Student %s cannot be copied, please create it manually.") % self.name)

Để xuất file dịch tiếng Việt cho module, chúng ta làm theo những bước sau:

Cảnh báo

Cần bỏ cấu hình --dev xml, --dev all khi chạy odoo trong quá trình thêm bản dịch cho module, nó có thể khiến một số chỗ không dịch được.

  1. Cài đặt module, Vào Thiết LậpDịch thuậtXuất bản dịch.
    Phần Phân hệ cần xuấtname của module trong file __manifest__.py. Chúng ta sẽ xuất 2 file viin_education.potvi_VN.po như trong hình:
    Xuất file dịch viin_education.pot Xuất file dịch vi_VN.po
  2. Tải về và copy 2 file đó vào thư mục i18n của module, nếu chưa có thư mục bạn cần phải tạo thủ công. Trong đó file viin_education.pot cung cấp một mẫu cho tất cả các bản dịch và chứa tất cả các từ ngữ/câu văn có thể dịch được. Còn file vi_VN.po chứa bản dịch cho tiếng Việt, chúng ta cần dịch những từ ngữ trong file đấy, ví dụ như sau:

    file vi_VN.po

    # Translation of Odoo Server.
    # This file contains the translation of the following modules:
    #       * viin_education
    #
    msgid ""
    msgstr ""
    "Project-Id-Version: Odoo Server 14.0\n"
    "Report-Msgid-Bugs-To: \n"
    "POT-Creation-Date: 2022-01-22 01:17+0000\n"
    "PO-Revision-Date: 2022-01-22 01:17+0000\n"
    "Last-Translator: \n"
    "Language-Team: \n"
    "MIME-Version: 1.0\n"
    "Content-Type: text/plain; charset=UTF-8\n"
    "Content-Transfer-Encoding: \n"
    "Plural-Forms: \n"
    
    #. module: viin_education
    #: model:ir.model.fields,help:viin_education.field_education_student__note
    msgid "Brief notes about students"
    msgstr "Ghi chú ngắn gọn về học sinh"
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__create_uid
    msgid "Created by"
    msgstr "Được tạo bởi"
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__create_date
    msgid "Created on"
    msgstr "Được tạo vào"
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__display_name
    msgid "Display Name"
    msgstr "Tên hiển thị"
    
    #. module: viin_education
    #: model:ir.model,name:viin_education.model_education_student
    msgid "Education Student"
    msgstr ""
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__id
    msgid "ID"
    msgstr ""
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student____last_update
    msgid "Last Modified on"
    msgstr ""
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__write_uid
    msgid "Last Updated by"
    msgstr ""
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__write_date
    msgid "Last Updated on"
    msgstr ""
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__name
    msgid "Name"
    msgstr "Tên"
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__note
    msgid "Note"
    msgstr "Ghi chú"
    
    #. module: viin_education
    #: code:addons/viin_education/models/education_student.py:0
    #, python-format
    msgid "Student %s cannot be copied, please create it manually."
    msgstr ""
    
    #. module: viin_education
    #: model:ir.model.fields,field_description:viin_education.field_education_student__student_code
    msgid "Student Code"
    msgstr "Mã học sinh"
    
    #. module: viin_education
    #: model:ir.model.constraint,message:viin_education.constraint_education_student_student_code_unique
    msgid "Student Code must be unique!"
    msgstr "Mã học sinh phải là duy nhất"
    

    Chạy lại odoo, nâng cấp module và chuyển sang ngôn ngữ của user sang tiếng Việt để xem thành quả:

    Kết quả dịch, ảnh 1

    Với những từ ngữ trong file vi_VN.po chưa được dịch, chúng sẽ vẫn giữ nguyên như trạng thái ban đầu:

    Kết quả dịch, ảnh 2

Giải thích

Các bản dịch sẽ được lưu trong model ir.translation và sẽ được cập nhật khi chúng ta cài đặt hoặc nâng cấp module.

Khi xuất file dịch, một số string trong view và model sẽ tự động được trích xuất để dịch. Ví dụ như thuộc tính _description, thuộc tính string, help của field... Text trong code Python hoặc JavaScript sẽ không tự động được trích xuất để dịch. Để thêm các thuật ngữ có thể dịch được, chúng ta cần bao bọc chúng trong hàm odoo._(), odoo._lt() với python và odoo.web._t(), odoo.web._lt() với JavaScript.

from odoo import _
_("Hello")

Một số lưu ý

  1. Để thêm biến vào đoạn dịch:

    Cách làm đúng.

    _("Hello %s") % name
    _("Hello %s", name)
    

    Cách làm sai.

    _("Hello %s" % name)
    
  2. Không chia bản dịch thành nhiều khối hoặc nhiều dòng:

    _("Class: ") + student.class_name + _(", Student: ") + student.name
    _t("Class: ") + student.class_name + _t(", Student: ") + student.name
    
    # bad, chia thành nhiều bản dịch nhỏ, lúc xuất file dịch sẽ bị chia thành từng câu đơn lẻ và không thể dịch sát nghĩa
    _("A student cannot belong to ") + \
    _("more than one class.")
    

    Thay vào đó.

    # good, câu đầy đủ sẽ dễ hiểu khi dịch
    _("A student cannot belong to" + \
    "more than one class.")
    
  3. Runtime

    Không gọi hàm dịch _() lúc khởi chạy server, (lúc này chưa có ngữ cảnh ngôn ngữ người dùng).

    #bad
    ERROR_MESSAGE = {
        'error_copy': _('Error when copying')
    }
    
    class EducationStudent(models.Model):
        _name = 'education.student'
    
        def copy(self, default=None):
            self.ensure_one()
            raise UserError(ERROR_MESSAGE['error_copy'])
    

    Thay vào đó, sử dụng hàm _lt() (Lazy Translate).

    ERROR_MESSAGE = {
        'error_copy': _lt('Error when copying'),
    }
    
    class EducationStudent(models.Model):
        _name = 'education.student'
    
        def copy(self, default=None):
             self.ensure_one()
             raise UserError(ERROR_MESSAGE['error_copy'])
    

    Hoặc tách thành hàm nhỏ, gọi khi đã có ngữ cảnh ngôn ngữ người dùng.

    def _get_error_message(self):
        return {
            error_copy: _('Error when copying')
        }
    

    Với JavaScript cũng tương tự:

    # bad, js _t() được gọi quá sớm
    var core = require('web.core');
    var _t = core._t;
    var error_message = {
        error_copy: _t('Error when copying')
    };
    

    Thay vào đó sử dụng hàm _lt().

    # good
    var core = require('web.core');
    var _t = core._t;
    var error_message = {
        error_copy: _lt('Error when copying')
    };
    
  4. Dịch một câu văn dài: xuống dòng khi cần thiết, msgstr đủ số dòng như msgid.

    #. module: viin_education
    #: model:ir.model.fields,help:viin_education.field_education_student__phone_sanitized
    msgid ""
    "Field used to store sanitized phone number. Helps speeding up searches and "
    "comparisons."
    msgstr ""
    "Trường được sử dụng để lưu trữ số điện thoại đã qua xử lý. Giúp tăng tốc tìm"
    " kiếm và so sách."