iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0

Day 20 搭上國際航空,切換語系

在上一回『邁向國際化,Django 多語系』我們已經了解了何謂國際化,接下來我們要為網站設定多語系,並且在使用者介面可以自行切換語言。

資料內容多語系

資料內容多語系與國際化的定義語言檔不太一樣,在資料內容中定義的並非要”翻譯”的內容,而是要在不同語系環境下呈現的資料。

網站設定多語系

在之前我們已經有建立一個語言代碼為 zh-hant 的網站設定檔,我們要再建立一個語言代碼為 en 的設定檔。

https://ithelp.ithome.com.tw/upload/images/20221002/20151656vsNB7QBwhZ.png

商品分類多語系

加入多語系欄位的方式有許多方法,這裡介紹最簡單的新增欄位方式為大家呈現出不同語言的資料

在商品分類模型 新增英文欄位,並新增 property 函數來根據當前語言返回對應語言的資料

# products/models.py

# ...
from django.utils.translation import get_language
# ...

class ProductCategory(models.Model):
    '''
    商品分類模型
    '''
    name = models.CharField('商品分類名稱', max_length=50)
    name_en = models.CharField('商品分類名稱(英)', max_length=50, null=True, blank=True)         # new
    description = models.TextField('商品分類描述', max_length=500, null=True, blank=True)
    description_en = models.TextField('商品分類描述(英)', max_length=500, null=True, blank=True) # new
    created = models.DateTimeField('建立日期', auto_now_add=True)
    modified = models.DateTimeField('修改日期', auto_now=True)
    image = models.ImageField("圖片", null=True, blank=True, upload_to=core_helpers.upload_handle)

    @property
    def name_locale(self):
        language = get_language()
        if language == 'zh-hant':
            return f"{self.name}"
        elif language == 'en':
            return f"{self.name_en}"
        return f"{self.name}"
    
    @property
    def description_locale(self):
        language = get_language()
        if language == 'zh-hant':
            return f"{self.description}"
        elif language == 'en':
            return f"{self.description_en}"
        return f"{self.description}"

    class Meta:
        verbose_name = '商品分類'
        verbose_name_plural = '商品分類'
        ordering = ['-created']

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

生成資料庫遷移檔案

docker exec --workdir /opt/app/web example_tenant_web \
    python3.10 manage.py makemigrations

...

Migrations for 'products':
  products/migrations/0004_auto_20221002_1726.py
    - Add field description_en to productcategory
    - Add field name_en to productcategory

執行資料庫遷移

docker exec --workdir /opt/app/web example_tenant_web \
    python3.10 manage.py migrate

...

=== Starting migration
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, core, customers, products, sessions, sites
Running migrations:
  Applying products.0004_auto_20221002_1726...
 OK
=== Starting migration
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, core, customers, products, sessions, sites
Running migrations:
  Applying products.0004_auto_20221002_1726...
 OK
=== Starting migration
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, core, customers, products, sessions, sites
Running migrations:
  Applying products.0004_auto_20221002_1726...
 OK

商品分類管理介面加入英文欄位

# products/models.py

# ...

class ProductCategoryAdmin(admin.ModelAdmin):
    search_fields = ['name', 'name_en']
    fields = ('name', 'name_en', 'description', 'description_en', 'created', 'modified', 'image')
    list_display = ('name',)
    readonly_fields = ('created', 'modified')

在管理介面寫入英文資料

https://ithelp.ithome.com.tw/upload/images/20221002/20151656Wey3Hwi4ac.png

使用者介面切換語言功能

URLS 匯入 i18n,將使用 i18n 提供的 set_language 函數進行切換語系。

# main/urls.py

# ...

urlpatterns = [
    path('admin/', admin.site.urls),
    path('i18n/', include('django.conf.urls.i18n')),
    path('products/', include(('products.urls', 'products'), namespace='products')),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

# ...

在所有頁面都要能讀取當前所有網站設定檔的語言代碼與系統語言兩個欄位,用於生成下拉選單,在 core_tag 新增一個自定義模板標籤 get_setting_list。

# core/templatetags/core_tag.py

# ...

@register.simple_tag
def get_setting_list():
    settings = Setting.objects.all()
    return [{"code": setting.id, "name_local": setting.language} for setting in settings]

要在 base 頁面的載入 i18n 與使用 get_current_language 取得當前語言。

<!-- products/templates/base.html -->

{% load static %}
{% load i18n %}
{% load core_tag %}
{% get_current_language as LANGUAGE_CODE %}
{% get_setting as get_setting %}
{% get_setting_list as get_setting_list %}

要在 base 頁面的 header 調整語言切換的 form

原始區塊如下:

<!-- products/templates/base.html -->

<!-- Languages -->
<li class="commonSelect">
    <!-- ... -->
</li><!-- / Languages -->

在 commonSelect 新增切換語言表單,這裡會使用 i18n 的 set_language 進行語言切換後重新導轉頁面。

下拉選單讀取 get_setting_list 的網站設定列表,點選時會觸發 onchange 事件直接送出表單。

<!-- products/templates/base.html -->

<!-- Languages -->
<li class="commonSelect">
    <form action="{% url 'set_language' %}" method="post">
        {% csrf_token %}
        <input name="next" type="hidden" value="{{ redirect_to }}">
        <select name="language" class="form-control" onchange="this.form.submit()">
            {% for language in get_setting_list %}
                <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
                    {{ language.name_local }} ({{ language.code }})
                </option>
            {% endfor %}
        </select>
    </form>
</li><!-- / Languages -->

模板標籤多語系

切換了網站語言,那資料呢?

還記得上次定義的模板標籤 get_setting 嗎?只需要稍稍作一些改變就能達到我們的目的了!

原先的 get_setting 函數:

# core/templatetags/core_tag.py

# ...

@register.simple_tag
def get_setting():
    setting = Setting.objects.get(id='zh-hant')
    setting_dict = Setting.objects.get(id='zh-hant').__dict__
    setting_dict['home_type1'] = setting.home_type1.__dict__ if setting.home_type1 else None
    setting_dict['home_type2'] = setting.home_type2.__dict__ if setting.home_type2 else None
    setting_dict['home_type3'] = setting.home_type3.__dict__ if setting.home_type3 else None
    return setting_dict

原先指定了語言代碼為 zh-hant,現在要調整為從 template 將參數傳入,若沒有傳入預設使用 zh-hant,有傳入就使用該語言代碼的設定檔。

以下為調整後的 get_setting 函數:

# core/templatetags/core_tag.py

# ...

@register.simple_tag
def get_setting(language_code=None):
    if not language_code:
        setting = Setting.objects.get(id='zh-hant')
    else:
        setting = Setting.objects.get(id=language_code)
    setting_dict = setting.__dict__
    setting_dict['home_type1'] = setting.home_type1.__dict__ if setting.home_type1 else None
    setting_dict['home_type2'] = setting.home_type2.__dict__ if setting.home_type2 else None
    setting_dict['home_type3'] = setting.home_type3.__dict__ if setting.home_type3 else None
    return setting_dict

在基底頁面 base.html 的 get_setting 模板標籤加上語言代碼 LANGUAGE_CODE 參數

<!-- products/templates/base.html -->

{% load static %}
{% load i18n %}
{% load core_tag %}
{% get_current_language as LANGUAGE_CODE %}
{% get_setting LANGUAGE_CODE as get_setting %}
{% get_setting_list as get_setting_list %}

資料內容多語系

在上方的商品分類模型增加了 property 來訪問當前語言的 name 與 description 欄位,這裡的setting_dict 改為使用新的多語系變數。

# core/templatetags/core_tag.py

from django import template
register = template.Library()

from core.models import Setting

@register.simple_tag
def get_setting(language_code=None):
    if not language_code:
        setting = Setting.objects.get(id='zh-hant')
    else:
        setting = Setting.objects.get(id=language_code)
    setting_dict = setting.__dict__
    if setting.home_type1:
        setting_dict['home_type1'] = {
            'name': setting.home_type1.name_locale,
            'image': setting.home_type1.image,
            'description': setting.home_type1.description_locale
        }
    if setting.home_type2:
        setting_dict['home_type2'] = {
            'name': setting.home_type2.name_locale,
            'image': setting.home_type2.image,
            'description': setting.home_type2.description_locale
        }
    if setting.home_type3:
        setting_dict['home_type3'] = {
            'name': setting.home_type3.name_locale,
            'image': setting.home_type3.image,
            'description': setting.home_type3.description_locale
        }
    return setting_dict

模板頁面多語系

如果單單只是模板要有多語系呢?只要在頁面上直接使用 LANGUAGE_CODE 進行判斷就可以了!

這裡對首頁的商品分類區塊上方文字進行多語系判斷:

<!-- products/templates/products/home.html -->

<div class="col-md-12">
    <div class="title text-center">
        {% if LANGUAGE_CODE == 'zh-hant' %}
            以商品分類尋找屬於您的商品
        {% elif LANGUAGE_CODE == 'en' %}
            Product category
        {% endif %}
    </div>
</div>

到這裡多語系就設定完成了!

頁面展示

中文頁面:

https://ithelp.ithome.com.tw/upload/images/20221002/20151656kwA0KGhpSF.png

英文頁面:

https://ithelp.ithome.com.tw/upload/images/20221002/201516567UWlyXPsjj.png

明天將要來講解多租戶要如何擁有各自的多語系對照表,並且讓租戶可以在管理介面自行更新翻譯對照表!下一回『國際化租屋,Django 多租戶多語系』。


上一篇
Day 19 邁向國際化,Django 多語系
下一篇
Day 21 國際化租屋,Django 多租戶多語系
系列文
全能住宅改造王,Django 多租戶架構的應用 —— 實作一個電商網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言