django 开发多语言网站
django的国际化支持非常好也很易用,支持代码、模板和JS的国际化整体解决方法。特别是到了1.2版本加入了对于模板本地的
日期和数字的本地格式化,其国际化支持已经完善。
应用方法
首先需要修改settings.py:
1. TEMPLATE_CONTEXT_PROCESSORS 加入django.core.context_processors.i18n
2. MIDDLEWARE_CLASSES中加入django.middleware.locale.LocaleMiddleware,注意:它要放在SessionMiddleware和CacheMiddleware的后面,其他中间件的前面
3. LANGUAGE_CODE 设置缺省的网站语言,如 en, zh-cn,it, de-at, es, pt-br
4. LANGUAGES设置网站所支持的所有语言,如(('en', u'English'),('zh-cn',u'中文'))
5. USE_I18N 设置为True
代码中的应用:
1. 所有需要国际化支持的源文件要引入函数gettext: from django.utils.translation import ugettext_lazy as _
2. 需要多语言支持的字符串如此例写: name = models.CharField(_('Name')…)
模板中的应用:
1. 所有需要国际化支持的模板文件需要加载: {% load i18n %}
2. 需要多语言支持的字符串如此写:<title>{% trans "This is the title." %}</title>
3. 翻译中间带变量的句子:{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}
JS中的应用(这部分看django的文档没搞出来,自己看源码琢磨了一个方法。你也可参考这个文档):
1. 在url.py中加入: (r'^jsi18n/(?P<packages>\S+?)/$', 'django.views.i18n.javascript_catalog'),
这句话告诉页面在加载这个脚本时由服务器上的javascript_catalog函数生成该脚本,具体的方法是通过packages找到locale,进而找到mo文件,然后将其中的所有翻译字符串放入生成脚本中的一个字典变量中,同时该脚本也定义了gettext函数,这样客户端脚本可使用gettext方法直接从字典中取出翻译字符串。
其中packages为你的项目名或者Application名的列表以加号隔开,格式为: testproject+testproject.app1+testproject.app2。这些项目或Application应在INSTALLED_APPS中申明。
2. 引用jsi18n: <script type="text/javascript" src="/jsi18n/testproject+testproject.app1+testproject.app2”></script>
我的项目不大,只在项目目录下有locale目录因此这样写就可以了 <script type="text/javascript" src="/jsi18n/testproject”></script>
如果你的项目较大,可考虑在Application中放locale目录,如例写脚本链接,这样的话不用每次都生成和加载全站的JS翻译字符串,以加快速度。
3. 在JS中使用gettext函数翻译:document.write(gettext('this is to be translated'));
4. 翻译带变量的句子
fmts = ngettext('There is %s object. Remaining: %s', 'There are %s objects. Remaining: %s', 11);
s = interpolate(fmts, [11, 20]); // s is 'There are 11 objects. Remaining: 20'
生成po文件和mo文件
这里先补补课,首先是locale目录, 请把locale创建在项目目录或Application目录中,其结构
locale
en
LC_MESSAGES (这个目录下放django.po/mo, djangojs.po/mo)
zh-cn
LC_MESSAGES(这个目录下放django.po/mo, djangojs.po/mo)
django.po/djangojs.po相当于资源文件,它被编译后形成django.mo/djangojs.mo文件,在运行时被读取。
po文件可以使用工具poedit编辑,我个人喜欢使用 mangage.py makemessages –a 让django自动分析源代码和模板文件
生成po,这里要注意两点:
1. 按我的实践,js中的字符串不能自动生成po,需手工生成
2. makemessages 需要调用工具xgettext, Windows没有,可下载晕版的gettext, 要下两个文件gettext-runtime-X.zip 和 gettext-tools-X.zip
X为版本号,注意版本低了会报错“Django internationalization requires GNU gettext 0.15 or newer”)
下来后放在解压在一个目录里,然后把下面的bin目录加到你系统路径中。
3. 通过mangage.py makemessages –a自动生成后,你需要做的是编辑po文件,如 zh-cn/LC_MESSAGES/djang.po,把你的
翻译逐个写上去。最后一步编译: mange.py compilemessages
让用户在页面上切换语言
把这个放到urls.py中:(r'^i18n/', include('django.conf.urls.i18n'))
搞个表单提交用户选择的语言,如例
<form action="/i18n/setlang/" method="post"> {% csrf_token %}
<input name="next" type="hidden" value="/next/page/" />
<select name="language"> {% for lang in LANGUAGES %} <option value="{{ lang.0 }}">{{ lang.1 }}</option> {% endfor %} </select> <input type="submit" value="Go" />
</form>
这里涉及一个问题,用户如此选择的语言是否能被记住,下次访问时无需再选?首先需要了解django如何确定用户需要的语言,其判断流程如此:
1. 首先判断Session里是否有键值为django_language的数据,如有则使用。(在用户选择语言时,如果网站支持Session,django把用户的语言偏好记录在Session中,否则记录在cookie里)
2. 检查cookie里是否有键值为settings.LANGUAGE_COOKIE_NAME的设置,如有则使用。
3. 检查浏览器提供的Accpet Language(各个浏览器都有的配置:语言首选项)是否被网站支持(是否有相应的翻译文件),如支持则使用
4. 以上方式都不能确定时,使用settings.LANGUAGE_CODE(网站缺省语言)
注意这样的方式能确保根据用户浏览器设置来为用户自动选定语言,也能保证用户选择语言后当次访问使用所选语言(用户的语言选择保存在Session和一次有效的cookie中)。如果用户浏览器的设置和其选择的语言不一致时,用户下次访问就无法使用本次选择的语言。要解决这个问题,可以接管"/i18n/setlang/"这个URL的处理,把用户选择直接保存在cookie里,而且其生命周期要搞得长一些。如例:
def set_language(request):
from django.utils.translation import check_for_language
next = request.REQUEST.get('next', None)
if not next:
next = request.META.get('HTTP_REFERER', None)
if not next:
next = '/'
response = http.HttpResponseRedirect(next)
if request.method == 'POST':
lang_code = request.POST.get('language', None)
if lang_code and check_for_language(lang_code):
if hasattr(request, 'session'):
request.session['django_language'] = lang_code
max_age = 60*60*24*365
expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=max_age), "%a, %d-%b-%Y %H:%M:%S GMT")
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code, max_age, expires)
return response