YOLO813

Django多语言网站的尝试

    我们知道,django具备多语言功能,主要是i18n模块,i18n(中文一般译为国际化),代表 Internationalization 中 i 和 n 之间有 18 个字母,本地化简称 L10n,表示 Localization 中 l 和 n 中之间有 10 个字母。需注意的是,一般会用小写的 i 和大写的 L 防止混淆。


实战

    完成翻译的步骤:

  •     配置
  •     标记需翻译的文本
  •     配置根url
  •     django-admin makemessages生成可编辑的po文件
  •     django-admin compilemessages生成优化过后的二进制mo文件


    先来看下配置,中间件

MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware', # important for multi language support
    'django.middleware.common.CommonMiddleware',
....
]

USE_I18N = True
USE_L10N = True
USE_TZ = True

import os
# 语言包选项
LANGUAGES = (
    ('en', 'English'),
    ('zh-hans', '简体'),
)
# 编译的语言文件存放路径,我这里是放在了根目录
LOCALE_PATHS = (
    os.path.join(BASE_DIR,'locale'),
)


    标记需要翻译的文本,先贴一段通用的切换语言代码,第一行必须load i18n,我们主要关注下form表单,index.html如下

{% load i18n %}
......
<form action="{% url 'set_language' %}" method="post">
  {% csrf_token %}
  <input type="hidden" name="next" value="{{ redirect_to }}">
  <select name="language">
      {% get_current_language as LANGUAGE_CODE %}
      {% get_available_languages as LANGUAGES %}
      {% get_language_info_list for LANGUAGES as languages %}
      {% for language in languages %}
          <option value="{{ language.code }}" {% if language.code == LANGUAGE_CODE %} selected{% endif %}>
              {{ language.name_local }} {{ language.code }}              
          </option>
      {% endfor %}
  </select>  
  {% translate "language:" %} {{ LANGUAGE_CODE }}
  <input type="submit" value="{% translate 'Switch' %}">  
</form>

<h1>{% translate "这是标题" %}</h1>

上面还写了一个<h1>标签用于待会切换语言看效果,需要翻译的内容放在translate标签内(请注意:trans 标签已改名为 translate。trans 标签仍作为别名支持,以便向后兼容。)


    配置根url,以便可以在前端模板中使用set_language切换url:

urlpatterns = [
    path('i18n/',include("django.conf.urls.i18n")),
    path('1', views.my_view),
]
# view.py
def my_view(request):
    # Translators: This message appears on the home page only
    return render(request, "index.html")

    生成可编辑的po文件,例如我想生成英文语言下的翻译po文件,命令如下

django-admin makemessages -l en

在windows上面操作时,可能会出现报错:

CommandError: Can't find msgfmt. Make sure you have GNU gettext tools 0.15 or newer installed

对此,django官网有一句话:makemessages 命令(和稍后讨论的 compilemessages )使用来自 GNU 文字工具集的命令行:xgettext, msgfmt, msgmerge 和 msguniq 。gettext 实用工具集支持的最低版本是 0.15 。

    需要下载gettext-iconv工具,链接我已经放在了下方参阅中,选择合适的版本下载即可,一路安装,记得添加系统环境变量

    安装成功之后,重启CMD,进入到项目目录下,输入如下命令

django-admin makemessages -l en
=>processing locale en

可以看到生成了一个django.po文件,我们可以打开看一下,如下图

其中的msgid正是我们在模板中定义在translate标签内的内容,我们可以在msgstr中填入相关的翻译,修改之后如下


    在CMD中执行如下命令,生成二进制mo文件

django-admin compilemessages

    因为我们在settings.py中定义了LANGUAGES,同理,我们可以生成中文版的po和mo文件,注意,简体中文代码是zh_hans(下划线!)。


    接下来,启动服务器,可以来看下效果了


    可能细心的朋友发现,这个翻译确实成功了,但是路径没有变化,按照常理,当然是英文路径和中文路径不一样才好。


    Django也为我们提供了工具,打开根url文件,配置如下代码

urlpatterns = [
    path('i18n/',include("django.conf.urls.i18n")),
    # path('1', views.my_view), 注释掉
]
from django.conf.urls.i18n import i18n_patterns
urlpatterns += i18n_patterns(
    path('1', views.my_view),
)


    再次访问http://127.0.0.1:8000/1我们发现页面的路径自动跳转到http://127.0.0.1:8000/en/1了,我们将路径en修改成zh-hans,发现中文内容也可以正常加载,但是此时会发现,不管你怎么点击语言切换,始终无法通过代码更改页面的语言,这似乎是django的一个bug,网上的大神也是各显神通,具体的讨论在下方参考中,我使用了其中两种方法来解决

    先贴上原来的切换语言代码

{% load i18n %}
<form action="{% url 'set_language' %}" 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 }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
          {{ language.name_local }} ({{ language.code }})
        </option>
      {% endfor %}
  </select>
  <input type="submit" value="{% trans "Change" %}" />
</form>

    主要看上面第4行input标签里面的value,官方一开始推荐的写法是redirect_to,但是这种写法会导致页面只会跳转到该语言版本下

    第一种解决方法如下,slice:'3:'的含义就是切片索引3以后的路径,例如上面的路径是/en/1,切完只会就变成了/1了,这样可以进行成功的跳转,但需要注意的是,由于zh_hans的长度不一样,那么如果想要切片中文页面,那么就应该使用slice:'8:'了:

  <input name="next" type="hidden" value="{{request.get_full_path|slice:'3:'}}" />


    第二种方法是制作一个过滤器,利用split函数将所有字符串以斜杠/划分成列表,这样的好处在于不需要考虑语言代码的长度,当然这也是有前提的,你的域名下的二级路径是语言代码(如domain.com/amp/en这种下方的过滤器就要修改一下才能用了),如下:

# custom_filters.py
from django.template import Library
register = Library()
@register.filter("getLangCode")
def processLang(value):
    # vari = '/'.join(value.split('/')[2:])
    vari = '/'+ value.split('/')[2:][0]
    print(vari)
    return vari
# index.html
{% load custom_filters %}
<input name="next" type="hidden" value="{{ request.get_full_path|getLangCode}}" />


    最终效果如下


    如果你的数据已经有了对应的翻译,可以考虑建一张表,通过判断语言的不同来切换不同的字段内容,例如:

# views.py
from django.utils.translation import gettext as _

def my_view(request):
    # Translators: This message appears on the home page only
    output = _("Welcome to my site.")
    myvar = my_models.objects.all()
    if request.LANGUAGE_CODE == 'en':
        context = {
            "output": output,
            "myvar": myvar.tc_title
        }
    if request.LANGUAGE_CODE == 'zh-hans':
        context = {
            "output": output,
            "myvar": myvar.en_title
        }
    return render(request, "1.html",context = context)

    在上面的代码中,通过判断LANGUAGE_CODE的不同,获取模型的不同字段内容,再当做变量传递给变量myvar,这样前端页面中并不用翻译,直接使用myvar变量来展示就可以。

    django的官方文档中,还演示了了translation的一个gettext函数,上面的_("Welcome to my site."),这样该句内容也会出现在po文件中,当其msgstr内容被填充后,我们在前端只需要使用常规的{{output}}变量渲染方法就可以让其内容通过路径中的语言切换自动更改。


参考:

#i18n
https://docs.djangoproject.com/zh-hans/3.2/topics/i18n/translation/
# CommandError: Can't find msgfmt. Make sure you have GNU gettext tools 0.15 or newer installed.
https://docs.djangoproject.com/zh-hans/3.2/topics/i18n/translation/#gettext-on-windows
https://mlocati.github.io/articles/gettext-iconv-windows.html
# Issue trying to change language from Django template
https://stackoverflow.com/questions/18392405/issue-trying-to-change-language-from-django-template