Internationalization and localization for your Django project.
Configuration
in settings.py
USE_I18N = True # 默認的語言 # 中文的話是 'zh-tw' 或 'zh-cn',不過 Django 1.7 之後要改用 'zh-hant' 或 'zh-hans' LANGUAGE_CODE = 'zh-tw' # 支援的語言,不指定的話就會列出全部的語言 LANGUAGES = ( ('en', 'English'), ('zh-cn', 'Simplified Chinese'), ('zh-tw', 'Traditional Chinese'), ) LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale/'), ) # 要記得加上 LocaleMiddleware,才會針對 request headers 使用對應的語言 MIDDLEWARE_CLASSES = ( ... 'django.middleware.locale.LocaleMiddleware', ... )
ref:
https://docs.djangoproject.com/en/dev/ref/settings/#languages
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#how-django-discovers-language-preference
in urls.py
urlpatterns = patterns('', ... url(r'^i18n/', include('django.conf.urls.i18n')), ... )
ref:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#the-set-language-redirect-view
Usage
in models.py
from django.db import models from django.utils.translation import ugettext_lazy as _ class HighHeels(models.Model): name = models.CharField(_('Name'), max_length=255) brand = models.CharField(_('Brand'), max_length=255, null=True, blank=True) created = models.DateTimeField(_('Created'), auto_now_add=True) class Meta: verbose_name = _('High Heels') verbose_name_plural = _('High Heels') get_latest_by = 'created' ordering = ('-created', ) def __unicode__(self): return self.name
在 models.py 中通常都會使用 ugettext_lazy()
而不是 ugettext()
。因為 gettext_lazy() 其中的值是在被訪問的時候才翻譯,而不是在呼叫 gettext_lazy() 的時候就翻譯。
另外,ugettext()
和 ugettext_lazy()
出來的字串都是 unicode。
pgettext()
的作用跟 ugettext() 一樣,只是多了一個參數可以傳 context 進去。
ref:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#lazy-translation
https://docs.djangoproject.com/en/dev/ref/utils/#django.utils.translation.pgettext
in views.py
from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.translation import ugettext as _ def home(request): contexts = { 'VAR_1': _('string 1'), 'VAR_2': _('string 2'), 'VAR_3': 'string 3', 'VAR_4': 'string 4', } return render_to_response('home.html', contexts, RequestContext(request))
要在 ugettext 中使用 string format
必須要用以下的形式:
msg = _(u'您有 %(not_ready_count)s 項行程的尚未填寫「司機備忘錄」。') % {'not_ready_count': not_ready_count}
ref:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#internationalization-in-python-code
in base.html
{% load i18n %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Heelstagram</title> </head> <body> {% block content %}{% endblock %} <form action="/i18n/setlang/" method="POST"> {% csrf_token %} <input name="next" type="hidden" value="{{ redirect_to }}" /> <select name="language"> {% get_language_info_list for LANGUAGES as LANGUAGES %} {% for language in LANGUAGES %} <option value="{{ language.code }}">{{ language.name_local }} ({{ language.code }})</option> {% endfor %} </select> <input type="submit" value="{% trans 'Change Language' %}" /> </form> </body> </html>
in home.html
{% extends 'base.html' %} {# 即使 base.html 已經 load i18n 了,在每個 template 還是得在 load 一次 #} {% load i18n %} {% block content %} <p>current language: "{{ LANGUAGE_CODE }}"</p> {# 在 .po 中會表示為 msgid "string 1" #} <p>1: {{ VAR_1 }} 會被翻譯</p> {# 這兩行是等價的,在 .po 中會表示為 msgid "string 3" #} <p>2: {% trans VAR_3 %} 會被翻譯</p> <p>3: {% trans "string 3" %} 會被翻譯</p> {# 在 .po 中會表示為: #} {# msgctxt "說明的文字" #} {# msgid "User" #} <p>4: {% trans 'User' context '說明的文字' %} 會被翻譯</p> <p>5: {{ VAR_3 }} 不會被翻譯</p> {# 在 .po 中會表示為 msgid "%(VAR_2)s 這整句會被翻譯,包含 VAR_2 的值" #} {# VAR_2 也會被翻譯是因為它等於 _('string 2') #} {# blocktrans 中的變數不能是 artist.name 這種形式,必須用 artist_name 或是 with #} <p>6: {% blocktrans %}{{ VAR_2 }} 這整句會被翻譯,包含 VAR_2 的值{% endblocktrans %}</p> {# VAR_4 不會被翻譯是因為它等於 'string 4',只是一個單純的字串 #} <p>7: {% blocktrans %}{{ VAR_4 }} 這整句會被翻譯,但是不包含 VAR_4 的值{% endblocktrans %}</p> {% endblock %}
blocktrans
的作用是讓你在翻譯字串中插入 template contexts 變數。
ref:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#trans-template-tag
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#internationalization-in-template-code
Create Language Files
要先安裝 gettext
,否則會出現 /bin/sh: xgettext: command not found
的錯誤
# 必須手動建立 locale 目錄(放在 project 或 app 的根目錄) $ mkdir -p locale # 第一次執行,必須指定 locale name,不能直接用 `-a` 參數,這樣才會在 locale 底下產生相對應的目錄 # 注意!是 zh_TW 而不是 zh-tw # 在專案根目錄執行 makemessages 就是對整個專案的所有 apps 產生翻譯檔案 $ ./manage.py makemessages -l en $ ./manage.py makemessages -l ja $ ./manage.py makemessages -l zh_CN $ ./manage.py makemessages -l zh_TW $ ./manage.py makemessages --all # .js 的翻譯要額外指定 `-d djangojs` 才會產生 djangojs.po $ ./manage.py makemessages -l zh_TW -d djangojs --ignore=node_modules # 如果你的 django.po 裡有些字串被標記為 `#, fuzzy` 的話,要記得刪掉,否則該字串不會被翻譯 $ ./manage.py compilemessages # 只對特定 app 產生翻譯檔案 $ cd mamba_client/contrib/django $ django-admin.py makemessages -l zh_CN $ django-admin.py compilemessages
ref:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#localization-how-to-create-language-files
https://docs.djangoproject.com/en/dev/ref/django-admin/#makemessages
每次 compilemessages 完要記得重啟 server
How Django discovers translations
django-admin.py makemessages -a
這個指令會去收集整個 project 裡的所有 apps 的 locale 字串。
優先權最高的是 LOCALE_PATHS
定義的那個目錄,找不到的話才會去找個別 app 之下的 locale 目錄。
ref:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#how-django-discovers-translations
強制使用某種語言
除了可以用上面那個 /i18n/setlang/
的 form 表單之外,也可以在 views 裡面這樣寫:
from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.translation import activate from django.utils.translation import ugettext as _ def common_processor(request): contexts = { 'T1': _('string 1'), } return contexts def home(request): activate('zh-tw') # 強制使用正體中文,覆蓋掉 user 和 browser 的設定 contexts = { 'T2': _('string 2'), 'T3': 'string 3', } return render_to_response('home.html', contexts, RequestContext(request, processors=[common_processor]))
ref:
https://docs.djangoproject.com/en/dev/ref/utils/#module-django.utils.translation
JavaScript
in urls.py
urlpatterns = patterns( '', ... url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog'), ... )
in your_shit.html
<script src="{% url 'django.views.i18n.javascript_catalog' %}"></script> <script> var text = gettext('要被翻譯的字串'); </script>
是 gettext()
而不是 ugettext()
# 要加上 -d djangojs 才會去 parse .js 裡的字串 $ ./manage.py makemessages --all -d djangojs