iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0

國際化(internationalization) 是指一種軟體設計和開發模式,目的是讓系統、產品可以快速地滿足不同文化、地區、語言的在地化需求。國際化的簡寫為 i18n 是因為 internationalization 的 i 與 n 之前有18個字。
我們將在 python 的程式裡面加入一些變化,讓程式碼中需要翻譯的文字去使用語言檔的翻譯進行對照,在通常的情況下會使用英文作為基礎語言(Django 預設的複數形式為英文)。

以下有幾個需要先了解幾個術語:

區域名稱 locale name
以語言小寫於國家首字母大寫組成,繁體中文則為 zh_Hant。

語言名稱 language code
瀏覽器會使用 Accept-Language HTTP header 來送出瀏覽器接受的語言名稱,例如 zh-hant。

語言檔 message file
為 .po 檔,包含可翻譯文字的對照表。

多語系環境設定

在預設的專案中,Django 的國際化模式是開啟的,在 settings.py 中國際化的參數如下:

# main/settings.py

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

在這裡將 LANGUAGE_CODE 與 TIME_ZONE 調整為繁體中文與台灣時區,並加入語言檔路徑 LOCALE_PATHS:

# main/settings.py

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'zh-hant'

TIME_ZONE = 'Asia/Taipei'

USE_I18N = True

USE_L10N = True

USE_TZ = True

LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

加入多語系 Middleware

# main/settings.py

MIDDLEWARE = [
    'django_tenants.middleware.main.TenantMainMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',   # add LocaleMiddleware
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

加入多語系 TEMPLATES context_processors

# main/settings.py

TEMPLATES = [
    {
        # ...
        'OPTIONS': {
            'context_processors': [
                # ...
                'django.template.context_processors.i18n',
            ],
        },
    },
]

在 Python 中進行國際化

使用 gettext 與 gettext_lazy 函數

本系列文章不會介紹太過複雜的翻譯函數,會以 gettext_lazy 為主。依照慣例 gettext 會使用 _ 下劃線作為別名,而 gettext_lazy 為惰性翻譯,當翻譯文字被使用時才會執行翻譯。

這裡將原本使用中文定義的欄位名稱改為英文:

from django.db import models
from django.utils.translation import gettext_lazy as _ # 匯入 gettext_lazy 函數
from core import helpers as core_helpers

# Create your models here.
class Product(models.Model):
    '''
    商品模型
    '''
    name = models.CharField(_('Product Name'), max_length=50)
    description = models.TextField(_('Product Description'), max_length=500, null=True, blank=True)
    price = models.PositiveIntegerField(_('Product Price'), default=0)
    created = models.DateTimeField(_('Created Date'), auto_now_add=True)
    modified = models.DateTimeField(_('Modified Date'), auto_now=True)
    category = models.ForeignKey(
        'products.ProductCategory', blank=True, null=True, 
        on_delete=models.RESTRICT, verbose_name=_('Product Category'), related_name='product_set'
    )

    class Meta:
        verbose_name = _('Product')
        verbose_name_plural = _('Product')
        ordering = ['-created']

    def __str__(self):
        return f'{self.name}'

儲存後商品管理介面已變為英文

https://ithelp.ithome.com.tw/upload/images/20221001/20151656la2vg11exw.png

在 模板 中進行國際化

使用 {% load i18n %} 載入國際化,此標籤需要在所有需要翻譯的模板中加入,包含繼承而來的模板也需要載入。

{% load i18n %}

使用 {% translate %} 模板標籤可以翻譯常數字串。

以下為商品詳細頁面版型一 template1.html 的麵包屑區塊:

<!-- products/templates/detail/template1.html -->
{% extends "base.html" %}
{% load static %}
{% load i18n %}

<!-- ... -->

<ol class="breadcrumb">
    <li><a href="{% url 'products:home' %}">{% translate "Home page" %}</a></li>
    <li><a href="{% url 'products:list' %}">{% translate "List of products" %}</a></li>
    <li class="active">{% translate "Product detail" %}</li>
</ol>

<!-- ... -->

儲存後商品詳細頁面的麵包屑已變為英文

https://ithelp.ithome.com.tw/upload/images/20221001/20151656DvfmelFdeL.png

建立語言檔

首先要為新語言建立 message file 語言檔,是一個副檔名為 .po 的純文字檔。

建立 locale 目錄

docker exec --workdir /opt/app/web example_tenant_web \
    mkdir locale

使用 django-admin makemessages 指令會自動建立語言檔

docker exec --workdir /opt/app/web example_tenant_web \
    django-admin makemessages -l zh_Hant

...

processing locale zh_Hant

以下為 locale 目錄結構:

.
├── core
├── customers
├── locale
│   └── zh_Hant
│       └── LC_MESSAGES
│           └── django.po
├── main
├── manage.py
├── media
└── products

django.po 語言檔

msgid 是顯示需要翻譯的字串,自動生成後不需進行調整。
msgstr 則是翻譯後的字串,我們需要自行填寫。

#: locale/zh_Hant/LC_MESSAGES/django.po

#: products/models.py:10
msgid "Product Name"
msgstr ""

#: products/models.py:11
msgid "Product Description"
msgstr ""

#: products/models.py:12
msgid "Product Price"
msgstr ""

#: products/models.py:13
msgid "Created Date"
msgstr ""

#: products/models.py:14
msgid "Modified Date"
msgstr ""

#: products/models.py:17
msgid "Product Category"
msgstr ""

#: products/models.py:21 products/models.py:22
msgid "Product"
msgstr ""

#: products/templates/products/detail/template1.html:12
msgid "Home page"
msgstr "首頁"

#: products/templates/products/detail/template1.html:13
msgid "List of products"
msgstr ""

#: products/templates/products/detail/template1.html:14
msgid "Product detail"
msgstr ""

加入繁體中文翻譯

#: locale/zh_Hant/LC_MESSAGES/django.po

#: products/models.py:10
msgid "Product Name"
msgstr "商品名稱"

#: products/models.py:11
msgid "Product Description"
msgstr "商品描述"

#: products/models.py:12
msgid "Product Price"
msgstr "商品價格"

#: products/models.py:13
msgid "Created Date"
msgstr "建立日期"

#: products/models.py:14
msgid "Modified Date"
msgstr "修改日期"

#: products/models.py:17
msgid "Product Category"
msgstr "商品分類"

#: products/models.py:21 products/models.py:22
msgid "Product"
msgstr "商品"

#: products/templates/products/detail/template1.html:12
msgid "Home"
msgstr "首頁"

#: products/templates/products/detail/template1.html:13
msgid "List of products"
msgstr "商品列表"

#: products/templates/products/detail/template1.html:14
msgid "Product detail"
msgstr "商品詳細資料"

編譯語言檔

使用 django-admin compilemessages 指令將語言檔進行編譯。

docker exec --workdir /opt/app/web example_tenant_web \
    django-admin compilemessages 

...

>     django-admin compilemessages 
processing file django.po in /opt/app/web/locale/zh_Hant/LC_MESSAGES

生成編譯後的 django.mo 檔

.
├── core
├── customers
├── locale
│   └── zh_Hant
│       └── LC_MESSAGES
│           ├── django.mo  # new
│           └── django.po
├── main
├── manage.py
├── media
└── products

到這裡語言檔的生成就完成了!

頁面展示

商品管理介面

https://ithelp.ithome.com.tw/upload/images/20221001/20151656AYK8Jt3e7a.png

商品詳細頁面

https://ithelp.ithome.com.tw/upload/images/20221001/201516565wozjkrjCb.png

翻譯的詞彙對於每個租戶來說可能會有不同的理解與需求,接下來我們將繼續延伸,讓每個租戶能有自己的語言檔,並且能自自行設定,下一回『搭上國際航空,切換語系』。


上一篇
Day 18 個性風格,自定義樣式版面
下一篇
Day 20 搭上國際航空,切換語系
系列文
全能住宅改造王,Django 多租戶架構的應用 —— 實作一個電商網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言