基于Django国际化的多语言实现

Author: Satori Satori

Posted: 2018-02-14 21:32:57

Category: Django HTML JavaScript 技术

Views: 2144 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. hxs2580 hxs2580 Says:

    我的消息提醒好像出了点问题,消息数量时刻显示1,标注这一条我没看过,尽管点开了好几回[[Emoji 10.png]]

    1. Satori Satori Says:

      你点了查看详情了吗?没点那个按钮是会一直在的

      1. hxs2580 hxs2580 Says:

        原来你不是判断是否读过这篇文章而是判断是否按过read more啊

        1. Satori Satori Says:

          这个逻辑可以改一下。不过最近有一种全部重构前端的冲动,然而还是没时间。现在发现自己的前端代码是在太渣

          1. hxs2580 hxs2580 Says:

            重构呀,顺便跟着偷学,23333

            1. Satori Satori Says:

              重构成本相当高,很多模块都要重写。因为现在前后端是耦合的,这个问题很大。要解耦合基本上就是全部重写,很多以前包装好的模块需要重新研究,自己去实现。现在我还真不一定有勇气重头来过[[Monkey 1.gif]] 不过重构的动力还是有的,现在好多前端逻辑是直接操作dom实现,设计模式混乱,非常不能维护,这也督促我进行修改。

  2. hxs2580 hxs2580 Says:

    emmm,难道以前用的是英文版?我居然没发现

    1. Satori Satori Says:

      用了这么久都没发现么2333

      1. hxs2580 hxs2580 Says:

        是呢,说明我英文功底好