基于Django国际化的多语言实现
Author:
Satori
Posted: 2018-02-14 21:32:57
Category: Django HTML JavaScript 技术
Views: 4368 Comments: 9
## 一、背景
做国际化是非常重要的事情。之前网站做出来的时候母亲表示全是英语看不懂。当时我就被触动了一下,虽然现阶段网站根本没有什么流量,但是大部分用户还是国内的,因此不做中文化翻译其他人很难去使用,而且做出国际化之后真的是让自己的网站一下子高大上起来。因此我决定还是做一做国际化。
## 二、实现方法
国际化的困难程度在我做之前就有估计,不过真实做的时候发现真的比较复杂。复杂不是在于实现难度,而是在细碎程度。因为**HTML文本**,**Javascript代码**,**Django 模型表单标签**都需要进行汉化工作。要做到疏而不漏很困难。还有就是歧义问题,同一个词在不同地方意思不同,还需要自己去做区分。
具体的实现方法参考[官方文档][1],主题思路就是通过在需要翻译的地方进行标注,然后使用Django命令将要翻译的地方生成一个翻译文件,接着补全翻译文件,最后再用Django命令生成翻译的二进制文件即可。
另外一点是需要切换语言,这可以用内置的方法往某个地址post数据即可更改。
## 三、具体步骤
### 1. 修改setting.py使之支持国际化
在`MIDDLEWARE`中加一个`'django.middleware.locale.LocaleMiddleware',`,这个中间件负责国际化的语言转化等等。
然后需要加一个`LANGUAGE_CODE = 'zh-hans'`,默认使用中文,再添加语言种类,这里我就添加英语和中文,如下。
```
LANGUAGES = [
('en', _('English')),
('zh-hans', _('Chinese')),
]
```
最后需要定义翻译的文件放在哪里,我们直接放在locale文件夹中,代码如下。
```
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
```
### 2. HTML文本的国际化
HTML文本的国际化相对比较简单,有两种方式来标记需要翻译的部分,第一种是直接将要翻译的文本放到`{% trans "xxx" %}`中,比如,`{% trans "language" %}`。
第二种方式适合大段文本的翻译,将要翻译的内容包含在`{% blocktrans %}`和`{% endblocktrans %}`中,比如。
```
{% blocktrans %}
Hello everyone
{% endblocktrans %}
```
但是需要注意的是当要翻译的文本中有变量之类的时候,只能用第二种方式,而且需要给变量一个别名,然后在翻译的文本中去引用,比如有原文是
```
title: {{ blog.title }}, author: {{ blog.author }}
```
需要声明一个变量储存,代码如下。
```
{% blocktrans with title=blog.title author=blog.author %}
title: {{ title }}, author: {{ author }}
{% endblocktrans %}
```
多个变量就往后加就行了,如上例。
其次遇到url之类的也需要提前存储,比如原文是
```
<a href="{% url 'auth_login' %}">Login</a>
```
需要有一个别名代替url,代码如下。
```
{% url 'auth_login' as login_url %}
{% blocktrans %}
<a href="{{ login_url }}">Login</a>
{% endblocktrans %}
```
最后需要注意歧义问题,对于template的歧义,加上context字段,例子如下。
```
{% blocktrans context "month" %}
He will leave on May 2nd.
{% endblocktrans %}
```
### 3. Django源文件的国际化
这部分国际化也比较简单,使用内置的translation中的gettext一系列方法即可获取文本。首先在python文件头部进行声明ugettext以及ugettext_lazy等方法,然后在需要翻译的地方用该方法包装即可。但是需要注意的是`ugettext_lazy`方法只能用于Model以及Form字段的翻译,不能直接作为HttpResponse的返回,这时候需要`ugettext`方法。例子如下。
```
from django.utils.translation import ugettext as _
def index(request):
context = {}
blog_list = Blog.objects.all().order_by('-publish_time')
page = request.GET.get('page')
blogs, page_list = BlogService.get_paginated_items(blog_list, page)
context['blogs'] = blogs
context['page_list'] = page_list
context['title'] = _("Blogs")
return render(request, "blogs/index.html", context)
```
这样就能返回多语言结果了。对于`ugettext_lazy`方法的例子如下。
```
from django.utils.translation import ugettext_lazy as _
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = ['gender', 'birthday', 'mobile', 'residence', 'website', 'microblog', 'qq', 'wechat', 'introduction']
labels = {
'gender': _('gender'),
'birthday': _('birthday'),
'mobile': _('mobile'),
'residence': _('residence'),
'website': _('website'),
'microblog': _('microblog'),
'qq': _('QQ'),
'wechat': _('wechat'),
'introduction': _('introduction'),
}
```
对于有上下文才能翻译的词句,需要用`pgettext`方法,例子如下。
```
from django.utils.translation import pgettext
month = pgettext("month name", "May")
```
这表示"May"这个词的上下文是"month",也就是月份,之后就好分辩怎么翻译,同样也不会和其他may文字冲突。
最后再把template tag中返回给前端的数据做国际化即可。
### 4. JavaScript的国际化
Javascript的国际化本身比较麻烦,因为它不能直接读翻译文件,也没有直接的翻译函数。还好Django帮我们做了这些事情。首先在根urls.py中引入i18n的url,代码如下。
```
from django.views.i18n import JavaScriptCatalog
# other code
urlpatterns += [
url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]
```
之后只需在引入javascript的html中引入django内置的一个javascript文件,将它引入到其他javascript文件之前即可。代码如下。
```
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
```
然后我们就可以在之后的javascript代码直接使用和之前类似python中的`ugettext`,`ugettext_lazy`, `pgettext`函数了。业务代码如下。
```
document.write(ugettext('this is to be translated'));
```
### 5. 生成翻译文件以及编译翻译文件
在前面的步骤当中已经将python代码和HTML代码和Javascript代码中需要翻译的地方全部标记了,下一步就是生成翻译文件并进行翻译。首先需要建一个locale文件夹,直接`mkdir locale`即可。下面执行得到翻译文件的命令。加上`-l`标签表示需要翻译的目标语言,比如得到中文翻译文件。
```
django-admin makemessages -l zh-Hans
```
对于javascript文件还需要单独执行一下命令。
```
django-admin makemessages -d djangojs -l zh_Hans
```
执行之后,就会得到两个翻译文件,`django.po`和`djangojs.po`。每个文件的条目大致如下。
```
#: accounts/forms.py:11
msgid "gender"
msgstr "性别"
```
第一行注释是出现的位置,第二行是msgid,也就是原文,这个不能修改,第三行是msgstr,就是需要翻译的文本,如果有上下文,就会有`msgctxt`字段,这表示这段文字的上下文。当全部翻译完成之后,再执行命令得到翻译后的二进制文件。命令如下。
```
django-admin compilemessages
```
然后就会得到两个.mo文件,这就是翻译后的文件 。
### 6. 切换语言
对于语言的切换,需要创建一个表单,然后向Django内置的setlang的url发带有`language`字段的数据即可,采用
POST方法。示例代码如下。
```
<div class="lang-div dropdown-menu" role="menu">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<a class="lang-item btn {% if language.code == LANGUAGE_CODE %}btn-primary{% else %}btn-default{% endif %}" onclick="$('#lang_type').val('{{ language.code }}'); $('#lang_form').submit();">
{{ language.name_local }}
</a>
{% endfor %}
</div>
<form action="{% url 'set_language' %}" method="post" id="lang_form">{% csrf_token %}
<input name="language" id="lang_type" type="hidden">
<input name="next" type="hidden" value="{{ redirect_to }}" />
</form>
```
这种方法通过按钮触发向input元素传入值,再发送即可实现语言切换。
## 四、总结
总之做国际化还是非常繁琐了,需要各种找,各种翻译,需要几天才能真正开发完。
[1]: https://docs.djangoproject.com/en/1.10/topics/i18n/translation/
LOGIN TO LEAVE A COMMENT
我的消息提醒好像出了点问题,消息数量时刻显示1,标注这一条我没看过,尽管点开了好几回[[Emoji 10.png]]
你点了查看详情了吗?没点那个按钮是会一直在的
原来你不是判断是否读过这篇文章而是判断是否按过read more啊
这个逻辑可以改一下。不过最近有一种全部重构前端的冲动,然而还是没时间。现在发现自己的前端代码是在太渣
重构呀,顺便跟着偷学,23333
重构成本相当高,很多模块都要重写。因为现在前后端是耦合的,这个问题很大。要解耦合基本上就是全部重写,很多以前包装好的模块需要重新研究,自己去实现。现在我还真不一定有勇气重头来过[[Monkey 1.gif]] 不过重构的动力还是有的,现在好多前端逻辑是直接操作dom实现,设计模式混乱,非常不能维护,这也督促我进行修改。
emmm,难道以前用的是英文版?我居然没发现
用了这么久都没发现么2333
是呢,说明我英文功底好