國際化(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',
],
},
},
]
使用 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}'
儲存後商品管理介面已變為英文
使用 {% 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>
<!-- ... -->
儲存後商品詳細頁面的麵包屑已變為英文
首先要為新語言建立 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
到這裡語言檔的生成就完成了!
商品管理介面
商品詳細頁面
翻譯的詞彙對於每個租戶來說可能會有不同的理解與需求,接下來我們將繼續延伸,讓每個租戶能有自己的語言檔,並且能自自行設定,下一回『搭上國際航空,切換語系』。