Django、多言語対応

Python Django

概要

Djangoフレームワークは多言語に対応するための機能も提供しており、割と簡単に扱うことができます。基本的な使い方と流れを説明していきます。

多言語対応の準備

多言語対応する上での準備です。

settings.py

まず、LANGUAGE_CODEjaとしておきます。これはおなじみの設定ですね。

LANGUAGE_CODE = 'ja'

特に多言語対応をしない場合ならば、このLANGUAGE_CODEに指定した言語で翻訳されていきます。しかし多言語対応、サイト上で複数の言語を扱う場合は追加の設定が必要です。

MIDDLEWAREに、django.middleware.locale.LocaleMiddlewareを読み込ませる必要があります。

    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',  # これ
    'django.middleware.common.CommonMiddleware',

これにより、Djangoはこのサイト上で複数の言語を扱うのだなと判断します。アクセスしてきたURLやセッション、使っているブラウザの言語といった情報をもとに、サイト上の文章を翻訳しようとします。

どうしてもユーザーの言語がわからない場合にだけ、LANGUAGE_CODEが使われるようになります。言い換えると、Localeミドルウェアを使わない場合は必ずLANGUAGE_CODEの言語で翻訳されるということになります。

ミドルウェアの場所にも注意です。SessionMidlewareCommonMiddlewareの間に書きます。キャッシュ関連ミドルウェアを使う場合は、CommonMiddlewareの前にLocaleMiddlewareが来るようにしてください(つまりキャッシュの後に読み込ませる)。

また、次の記述も追加しておきましょう。

LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

localeディレクトリの作成

プロジェクト直下と、アプリケーションディレクトリ内にlocaleというディレクトリを作成しておきましょう。

今回はappというアプリケーションです。次の画像のようになります。 2つのlocaleディレクトリがあることを確認しましょう。 プロジェクト直下とアプリケーション直下にlocaleディレクトリを置く

Windowsの場合

翻訳ファイルを作成するためのツールが必要です。こちらにアクセスし、ダウンロードしてください。

多言語化したい文章を指定する

それでは、多言語化したい文章をマークしていきましょう。

テンプレート内の文章

テンプレート内で文章を多言語化する簡単な方法は、テンプレートタグtransを使う方法です。

{% load i18n %}
<h1>{% trans "Hello" %}</h1>

{% load i18n %}とするのを忘れないようにしましょう。{% trans %}をはじめとする、翻訳関連のテンプレートタグ・フィルタを読み込むための記述です。これがどういう動作をするかは、後々わかるようになります。

Pythonファイル内の文章

フォームのlabelやモデルのverbose_nameなど、そういった部分も多言語化できますし、テンプレートへ渡す文字列を多言語化したいかもしれません。

例えば、モデルの例です。

from django.db import models
from django.utils.translation import gettext_lazy as _


class Post(models.Model):
    title = models.CharField(_('post title'), max_length=255)

テンプレートではtransを使いましたが、Pythonファイル内ではdjango.utils.translation.gettext_lazy又はgettextをよく使います。lazyとついているのは遅延評価用...reverse関数とreverse_lazy関数の関係と似たようなものです。上の例ならば、titleフィールドの表示名を実際に表示する場面で翻訳をしてくれます。

モデルやフォームのフィールド内にあるlabel, verbose_name,help_text...モジュールのimport時にすぐに読み込まれるような場所、クラス属性内に関してはlazyを使うほうが無難です。

as _ として、gettext_lazy関数をアンダースコアで使えるようにしていますがこれは慣習的なものです。

翻訳ファイルの作成

以下のコマンドを実行しましょう。これは日本語の翻訳ファイルを作成するコマンドです。

django-admin makemessages -l ja

localeディレクトリの中に、jaというディレクトリができたはずです。おそらく、2通りの状態になります。

  1. アプリケーション直下のlocaleディレクトリだけ中身がある

  2. アプリケーション直下と、プロジェクト直下のlocaleディレクトリ両方に中身がある

Djangoが翻訳ファイルをどこに配置するかというと、まずアプリケーション内のlocaleディレクトリに配置しようとしますapp/models.pyに翻訳テキストがありましたが、これはappアプリケーション内のlocaleディレクトリに翻訳ファイルが置かれます。

なので、appアプリケーション内のlocaleディレクトリ内、django.poには次の記述が必ずあるはずです。

#: .\app\models.py:6
msgid "post title"
msgstr ""

特定のアプリケーションに属さないファイルはsettings.pyに定義したLOCALE_PATHSに翻訳ファイルが置かれます。特定のアプリケーションに属さないファイルというのは例えば、settings.pyやプロジェクト直下に置いたtemplates内の翻訳テキストが該当します。

なので、プロジェクト直下にtemplatesを置いている方であれば、プロジェクト直下のlocale内のdjango.poに次の記述があるはずです。

#: .\templates\app\top.html:7
msgid "Hello"
msgstr ""

templatesをアプリケーション内に置いている方ならば、プロジェクト直下のlocaleは空で、アプリケーション内のlocaleにそれがあります。

#: .\app\templates\app\top.html:7
msgid "Hello"
msgstr ""

アプリケーション内にlocaleディレクトリがない場合は、LOCALE_PATHSに翻訳ファイルが置かれます。なので、プロジェクト全ての翻訳を一か所にまとめたい場合は、アプリケーション内にlocaleディレクトリを置かないほうが良いかもしれません。また、LOCALE_PATHSも空の場合はエラーになります。

翻訳する

django.poというファイルがあるので開きましょう。

#: .\app\models.py:6
msgid "post title"
msgstr ""

#: .\app\templates\app\top.html:5
msgid "Hello"
msgstr ""

msgidは、{% trans Hello %}のHello部分であったり、_('post title')のpost title部分です。ここに入れたい日本語の文章をmsgstrに書いていきます。

#: .\app\models.py:6
msgid "post title"
msgstr "記事タイトル"

#: .\app\templates\app\top.html:5
msgid "Hello"
msgstr "こんにちは"

翻訳する文章を作成したら、コンパイルします。

django-admin compilemessages

これにより、django.moというファイルができます。これでひとまず終了です。

今後は、新しい言語を追加したり翻訳したい文章が増えればmakemessagesを行い、修正があればdjango.poを編集し、そしてcompilemessagesを行っていきます。

UnicodeEncodeErrorが出る場合

ロケールの問題が殆どです。CentOS7ならば、以下のようにロケールを変更できます。

localectl set-locale LANG=ja_JP.utf-8
source /etc/locale.conf

翻訳の上書き

翻訳ファイルの探索順序は、次のようになっています。

  1. settings.pyのLOCALE_PATHSが指している場所

    この記事で言うところの、プロジェクト直下のlocale

  2. 各アプリケーション内にあるlocaleディレクトリ(INSTALLED_APPSの上から順に)

    authやadminなどの組み込みのDjangoアプリケーション内にもlocaleがあります。

  3. django/conf/locale

    Django全体で使われる基本翻訳。例えば曜日の翻訳文章などはここで。

もしかしたら、Django標準の翻訳テキストを上書きしたい場合もあるかもしれませんし、サードパーティ製ライブラリの翻訳を上書きしたい、といったケースもあるかもしれません。

Django管理サイトのタイトルは"Django administration"なのですが、これを上書きするならばLOCALE_PATHS内で次のように定義を上書きすればOKです。

msgid "Django administration"
msgstr "管理サイトへようこそ!"

もしくは、アプリケーション内のlocaleで上書きすることも可能です。各アプリケーション内のlocaleディレクトリですが、INSTALLED_APPSの上から順に探索され、見つかったら探索が終わります。各種テンプレートやstaticファイルの探索順序と同じです。INSTALLED_APPSで自作アプリが上書きしたいアプリケーション(管理サイトタイトルならば、admin)より上にある状態にしておくことが必要です。

言語切り替えプルダウン

各ユーザーが、表示したい言語をそれぞれ切り替えれるようにしてみます。

urls.pyに以下を追加し...

path('i18n/', include('django.conf.urls.i18n')),

テンプレート内に以下の記述をしておきます。

{% load i18n %}

<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
    <input name="next" type="hidden" 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>
    <input type="submit" value="Go">
</form>

ヘッダーとかに置いておくと良さそうですね。
言語切り替えプルダウン

プルダウンの言語が多すぎる場合は、絞ることもできます。settings.pyに追記しましょう。

from django.utils.translation import ugettext_lazy as _
LANGUAGES = [
    ('en', _('English')),
    ('ja', _('Japanese')),
]

URLで言語を判断する

プロジェクトのurls.pyにて、以下のように定義します。

from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),

]

urlpatterns += i18n_patterns(
    path('', include('app.urls')),
)

いつもならば、path('', include('app.urls')),という記述はpath('admin/', admin.site.urls),の下あたりにありますね。しかし、今回は別の場所...i18n_patterns内に移しました。これにより、appの各URLに関しては、URLの頭に/ja/hogehoge とか/en/hogehoge とかでアクセスできるようになります。

Relation Posts

関連記事はありません。

Comment

記事にコメントする

名無し

こんばんは、いつもブログで勉強させていただいています。

urlで多言語対応すると楽そうだなあと記事を見ていて実感しました。 ただ、サイト内リンクなどでページ遷移する際、どのような方法を取れば選択した言語設定を保持できるのかわからなかったので、教えていただけたら幸いです。

返信する

なりと

例えば記事中の言語切り替えプルダウンで選択すると、それだけで言語設定が保存されます。

ブラウザのクッキーを削除したり別のブラウザを使わない限り、しばらくの間は他のページに移動しても選択した言語で表示されます。

okd

いつも良質な記事をありがとうございます。

今回の記事では LANGUAGE_CODE = 'ja'とあり、djangoがすでに日本語化されている状態で、さらに

django-admin makemessages -l ja

を実行しているので、 日本語を日本語に翻訳(?)しているという状況でしょうか。

返信する

なりと

LANGUAGE_CODE = 'ja'`は、「デフォルトでは日本語で翻訳する」という設定に過ぎません。日本語の翻訳ファイル自体が別途必要です

admin管理サイト等は既に日本語の翻訳ファイルがありますが、自作のアプリケーション内に書いた{% trans "Hello" %}_('post title')は、何もしないと翻訳ファイルがないので翻訳されません。

django-admin makemessages -l jaは、日本語の翻訳ファイルを作るためのコマンドです。

Kaito

現在Djangoを用いたWebアプリケーション開発に取り組んでおり、いつも勉強させていただいております。

django-admin makemessages -l jaを実行すると、

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

と出てきてしまいます。 gettext0.20.1をダウンロードした後のフローが原因かと考え参考になりそうなサイトを探しましたが、見つかりませんでした。 お忙しいところ恐縮ですが、教えていただけたら幸いです。

返信する

なりと

多分パスを追加する必要があります。「環境変数 パスの追加」等で検索し、gettxt.exeといった実行ファイルがあるパスを、そこに追加してみてください。

mkz

こんにちは!いつも参考にさせていただいています。 多言語対応の場合ですが、URL内の言語コードをカスタマイズすることは可能でしょうか?例えば、 https://example.com/ja/ ⇒ https://example.com/jp/ https://example.com/zh-hans/ ⇒ https://example.com/cn/ などです。特に中国語は長いので簡単な cn にしたいのですが、変更できるという記事が英語でもなかなかありません。 もし可能なようでしたらヒントでも教えていただけないでしょうか。

返信する