Django:多言語化の実装で悩んだポイントと対応策

Django:多言語化の実装で悩んだポイントと対応策

まずはDjangoの多言語化について簡単にまとめます。

基本的な設定について

多言語化に必要なプラグイン

  • gettext (インストール方法などは前回記事をどうぞ)

多言語化のための設定

  • settings.py の編集(通常言語の設定 / ミドルウェアの追加 / localeディレクトリへのパス作成)
  • localeディレクトリの設置(作成したパスの場所にディレクトリを設置)

settings.pyのファイルシステム系の話(os.pathとpathlib)は別記事にて。
この記事内ではlocaleディレクトリ作成についての補足を記載しておきます。

localeディレクトリはどこに設置すればよいのか

多言語化について色々調べているとlocaleディレクトリの作成場所は多様であることに気付きます。
大別すると次の3パターンに分かれているようです。

  1. BASE_DIR(manage.pyが存在するディレクトリ)に設置
  2. PROJECT_ROOT(settings.pyが存在するディレクトリ)に設置
  3. BASE_DIRと各アプリディレクトリ(views.py やmodels.pyが存在する場所)に設置

このようにパターンはあるものの厳粛なルールがあるわけではありません。
言ってみれば「パスさえ通していれば何処においてもOK」の様です。

パターン3の設置方法について(※補足程度)

上記ディレクトリ設置パターンの『3番目』については上2つと異なる理由がある様です。
それが【翻訳の上書き】機能。
この機能については情報量が少なくイマイチ正確性に欠けるため補足程度の情報にとどめますが、『BASE_DIR\locale』に配置した翻訳ルールよりも『APP\locale』に配置したルールを採用するのだとか。

#: accounts/templates/login.html:24
#: accounts/templates/login.html:32
msgid "ログイン"
msgstr "LogIn"

確かに、こんな感じで設定していくので「同じ文字でも訳を変えたい」という要望はあるだろうな感じます。
例えば、ボタンでの表記は【LOGIN】にして欲しいとか。
上書き機能はそんなときに使うようです。

まぁ今回はそんなに細かくないのでスルーで。

翻訳ルールを設置/適用するためのコマンド

■パスを通したlocaleディレクトリに翻訳ファイルを自動生成する。

django-admin makemessages -l en

■変更したdjango.poファイルを適用し『django.mo』ファイルを作成する

django-admin compilemessages

共にコマンドの実行はSSHなどでログインして実行します。
Dockerで組んでいる場合はもちろん「Djangoの入ったコンテナの中」で実行します。

日本語サイトを英語に翻訳する場合は『-l en』『-l ja』どっち?

これ超悩みました。動作云々も含めてね、もう「え?なんでそうなる?」という感じで...。

日本語の英訳は【django-admin makemessages -l en】を実行

[ django-admin makemessages -l ja ]は日本語訳の時に利用します。参考サイトの中には2パターンつっくるという方法を採用している方もいましたが、日本語で記載したサイトであれば 『jaディレクトリ』は不要だと思います。

『Django 多言語化 』でググった時に上位表示されるサイトの多くが 英語=>日本語 で記載しています。
なので【 django-admin makemessages -l ja 】の方をよく目にしますが、間違わないように気を付けましょう。

Django多言語化の注意点

まぁ言ってみれば今回の設定で私が間違えた個所です。

  1. 日本語の英訳の場合【 django-admin makemessages -l ja 】ではなく【 django-admin makemessages -l en 】が正解
  2. 【 django-admin makemessages -l ja 】で作成しても自動英訳してくれるパーツがある(これで誤解しやすい)
  3. 【 django-admin compilemessages 】を実行してもすぐに翻訳内容が変わるわけではない(変わらないことがある)
  4. 翻訳個所のマーキング方法がTemplate(html)と pyファイルで異なる

1つづつ取り上げで行きます。

日本語の英訳の場合【 django-admin makemessages -l en 】が正解

上の項でも書いた通りです。
知っていれば間違えることはないですが、コピペしてしまうと間違いやすいので注意しましょう。

【 -l ja 】で作成しても自動英訳してくれるパーツがある

今回「なんで??」と長いテストを繰り返したきっかけがコレです。
 
動作テストに使ったのはこんなログインページです。
この中のE-mailやPasswordには【{% trans "E-mail" %}】も【{% trans "メールアドレス" %}】も記載していません。

このページのコードは下の通りです。(※ヘッダーMenuなどの読込ブロックは省略しました)

(前略)
{% block contents %}
{% 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="{% trans '変更' %}">
</form>
<div class="container">
    <div class="row">
        <div class="my-div-style">
            <h1>{% trans "ログイン" %}</h1>
            <form method="post" action="{% url 'account_login' %}">
                {% csrf_token %}
                {{ form.as_p }}

                {% if redirect_field_value %}
                <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
                {% endif %}
                <button class="btn btn-primary" type="submit">{% trans "ログイン" %}</button>
                <p><a class="button secondaryAction" htef="{% url 'account_reset_password' %}">パスワードを忘れましたか?</a></p>
            </form>
        </div>
    </div>
</div>
{% endblock %}

さて、ここで勘違いポイント。

勝手に翻訳してくれた【メールアドレス】などの文字ですが、『-l ja』でも『-l en』でも同じように正しく翻訳してくれます。素晴らしいのですが、これで「設定は正しい」と判断してしまったのが私の沼への第一歩でした。

【django-admin compilemessages】を実行してもすぐに翻訳内容が変わるわけではない

翻訳を書いてアップロード、django-admin compilemessages でコンパイル適用しますがこれすぐに適用されません。
新規追加したルールであったとしても、先に空白でUPしたり「翻訳書いたのに適用されない」という現象が発生します。「テストだから」と安易に空白でUPすると痛い目見ます。これが私の沼への2歩目。

2歩目まで沼に入るとこんなループが待っています。

  • msgstr に訳書いてUPしたのに何で表示変わらないんだろう
    => その前に [ msgstr "" ] でUPしてるので、残念ながら変わりません...。
  • -l ja じゃなくて -l en が正しかったのかな、-l en で作成して適用させてみよう
    => -l en は正しくても -l ja で作った翻訳が日本語にした時に作動します...。
  • 意図した動きにならないなぁ。記載を英語表記にしてみたらどうだろう。
    => 言語で英語を選ぶと日本語訳されるという訳判らない動作発生。記載戻してみても動作変わらず...。

この沼ループはdjango.moにコンパイルしたハズの翻訳がすぐ適用されない事で『疑心暗鬼』に駆られて発動します。ちなみに、ブラウザのキャッシュクリアしても動作は同じです。なので、DjangoかPythonのどこかに翻訳情報がStackされているのだろうと勝手に推測しています。

【翻訳即採用】の方法 ※ Docker構築のみ

Djangoの入ったコンテナを削除しリビルドすると『修正した翻訳』が適用されます。

【 locale\en 】【 locale\ja 】が共に存在するとき日本語はどうなるか

settings.py にて LANGUAGE_CODE = 'ja' の設定をしていたとします。
この場合、標準(言語を指定しない場合)の表示は『日本語』になっています。
なので、local\ja\LC_MESSAGES\django.poで設定した翻訳が採用される事になります。

例:ログインという文字を変える ※ http://hogehoge.com/login/

■HTML

<h1>{% trans "ログイン" %}</h1>

■local\ja\LC_MESSAGES\django.po ※コンパイル実行済みとする

(前略)
msgid "ログイン"
msgstr "LOGIN"

■local\en\LC_MESSAGES\django.po ※コンパイル実行済みとする

(前略)
msgid "ログイン"
msgstr "Log In"

■結果(※ブラウザは日本語環境)

  1. http://hogehoge.com/login/ =>日本語選択 => 【<h1>LOGIN</h1>】を表示
  2. http://hogehoge.com/en/login/ =>英語選択 => 【<h1>Log In</h1>】を表示
  3. http://hogehoge.com/ja/login/ =>404エラー やり方は何かあるんだろうけど...。

その他、makemessages 周りの便利なコード

翻訳を追加したい時

django-admin makemessages -l en

同じコードで問題ありません。新しく追加されたマーカー({% trans "文字列" %})をリストに追加してくれます。
既に設定した翻訳はリスト内にそのまま維持されます。

ただし、django.poファイルを削除してしまうとすでに設定した翻訳も記載がなくなるので注意が必要です。

その状態(以前設定した翻訳が空白の状態)で compilemessages しても前述の通りすぐに翻訳が機能しなくなる事はありません。が、moファイルは空白として更新されています。そのためリビルドすると何も変換されなくなります。

moの中身を知りたい、またはmoファイルからpoファイルを生成したい

とはいえ、力余ってdjango.poを削除してしまう可能性はゼロとは言えません。
そんな時はmoファイルからpoファイルを作成するという手段をとることができます。

こちらのアプリを利用してみましょう。
使い方はこちらこちらを参考に。

全ての言語のdjango.poファイルを更新する

django-admin makemessages -a

注意事項は『翻訳を追加したい時』と同じです。

翻訳個所のマーキング方法がTemplate(html)と pyファイルで異なる

■Template(html)の場合

  • マーキング方法:{% trans "翻訳言語" %}
  • 必須読込コード:{% load i18n %}

■pyファイル(forms.py / models.py / views.py)の場合

  • マーキング方法:_(”翻訳言語”)
  • 必須読込コード:from django.utils.translation import gettext_lazy as _

例えば、メール送信フォームなどはforms.pyに書き込んでいると思います。
Template側にてforms.pyのコードを読み込んでいる場合はforms.pyでマーキングしなくてはいけません。

pyファイル内は【_( "" ) 】でマーキング

最終的にpoファイルに翻訳を登録するのはtemplateと同じですが、マーキング方法がTemplateと異なります。

■例:forms.py(お問合せパーツ)

from django import forms
from django.core.mail import EmailMessage
from.models import Company, Contents
from django.utils.translation import gettext_lazy as _

class InquiryForm(forms.Form):
    name = forms.CharField(label=_('お名前'), max_length=30)
    email = forms.EmailField(label=_('メールアドレス'))
    title = forms.CharField(label=_('タイトル'), max_length=30)
    message = forms.CharField(label=_('メッセージ'), widget=forms.Textarea)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fields['name'].widget.attrs['class'] = 'form-control col-9'
        self.fields['name'].widget.attrs['placeholder'] = _('お名前をここに入力してください。')

        self.fields['email'].widget.attrs['class'] = 'form-control col-11'
        self.fields['email'].widget.attrs['placeholder'] = _('メールアドレスをご記載ください。')

        self.fields['title'].widget.attrs['class'] = 'form-control col-11'
        self.fields['title'].widget.attrs['placeholder'] = _('タイトルの記入場所です。')

        self.fields['message'].widget.attrs['class'] = 'form-control col-12'
        self.fields['message'].widget.attrs['placeholder'] = _('メッセージを入力する場所です。')
(以下略)

こんな感じで指示すると、poファイル生成時リストに取り込んでくれます。

pythonではgettextを【 _ 】で置き換える事が通例らしく、gettextをインポートした後に[ as _ ] と書くのがセオリーなのだそうです。

まとめ

覚えてしまえば「何てことはない」と思える内容です。
まぁ何でもそうですけど、最初が躓くんですよね。

いい経験させてもらいました。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください