やったこと
- ブラウザの言語設定に応じた言語を表示する。
- 日本語設定のブラウザなら日本語で、英語設定のブラウザなら英語でWebサイトを表示する。
- 技術的にはDjangoの国際化(i18n)を利用してAccept-Languageの優先順位が高い言語でページを生成する。
- Djangoのアカウント機能と合わせて、各ユーザが指定した言語に合わせてページを表示させる。
- 技術的にはDjangoのセッション情報(
session['_language']
)を任意のタイミングで書き換える。
- 技術的にはDjangoのセッション情報(
ちなみにソースコードはGitHubにもあります。
目次
- 環境の準備
- ブラウザの言語設定に応じたページ表示
- アカウント認証機能の追加
- ユーザ毎に言語設定機能の追加
環境の準備
本記事は以下の環境で実行した。
- OS: CentOS7.6
- Python 3.6.7
- Django 2.1
以下のコマンドで諸々インストール
$ sudo yum install -y https://centos7.iuscommunity.org/ius-release.rpm $ sudo yum install -y python36u python36u-libs python36u-devel python36u-pip $ sudo pip3.6 install django==2.1
ブラウザの言語設定に応じたページ表示
Djangoプロジェクトを作成する。
$ cd ~ $ django-admin startproject i18n $ cd i18n
i18n/settings.py
を編集する。(ファイル名は~/i18n/
からの相対パス。以後同様)
# どのホストからのアクセスも受け付ける ALLOWED_HOSTS = ['*'] # 国際化のミドルウェアを追加 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', # 追加 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates'),], # 変更 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] # デフォルトの言語を日本語にする LANGUAGE_CODE = 'ja' # localeフォルダの位置を指定する LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), ) # 選択できる言語を設定する from django.utils.translation import ugettext_lazy as _ LANGUAGES = [ ('en', _('English')), ('ja', _('Japanese')), ]
i18n/urls.py
を編集する。
from django.contrib import admin from django.urls import path from . import views # 追加 urlpatterns = [ path('admin/', admin.site.urls), path('', views.index, name='index'), # 追加 ]
i18n/views.py
を作成する。
from django.shortcuts import render def index(request): return render(request, 'index.html')
templates
フォルダを作成する。
templates/index.html
を作成する。
{% load i18n %} <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% trans "Djangoのテストページ" %}</title> </head> <body> <h1 id="title">{% trans "Djangoのテストページ" %}</h1> <button type="button" onclick='alert("{% trans "こんにちは" %}")'>{% trans "ここをクリック" %}</button> </body> </html>
locale
フォルダを作成する。
以下のコマンドで翻訳用ファイルを作成する。
$ django-admin makemessages -l en
locale/en/LC_MESSAGES/django.po
を編集する。
# : i18n/templates/index.html:6 i18n/templates/index.html:9 msgid "Djangoのテストページ" msgstr "Django test page" # 変更 # : i18n/templates/index.html:10 msgid "こんにちは" msgstr "Hello" # 変更 # : i18n/templates/index.html:10 msgid "ここをクリック" msgstr "Click here" # 変更
以下のコマンドで翻訳ファイルをコンパイルする。
$ django-admin compilemessages
Webサーバ起動
$ python3.6 manage.py runserver 0.0.0.0:8000
ブラウザでhttp://(仮想マシンのIPアドレス):8000
にアクセスし、ブラウザの言語設定に応じたページが表示されることを確認する。
アカウント認証機能の追加
i18n/settings.py
を編集してログインURL設定を追加する。
LOGIN_URL = '/login/'
i18n/urls.py
にログインとログアウトのエンドポイントを追加する。
※:ログインにはdjango.contrib.auth.views.LoginView
を使うのが簡単なのだが、今回はアカウント毎に言語を設定する処理を追加するため低水準関数のdjango.contrib.auth
のauthenticate
とlogin
を利用する。
from django.contrib import admin from django.urls import path from . import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index, name='index'), path('login/', views.login_view, name="login"), # ログイン用エンドポイント追加 path('logout/', views.logout_view, name='logout'), # ログアウト用エンドポイント追加 ]
i18n/views.py
にログインとログアウトの処理を追加する。
from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate, login from django.contrib.auth import views as auth_views @login_required # トップページもログインしないと入れないようにしてみる def index(request): return render(request, 'index.html') # ログイン用view関数を追加 def login_view(request): if request.method == 'GET': next = request.GET.get('next', '/') return render(request, 'login.html', {'next': next}) elif request.method == 'POST': username = request.POST.get('username', '') password = request.POST.get('password', '') next = request.POST.get('next', '/') user = authenticate(username=username, password=password) if user is not None: login(request, user) return redirect(next) else: return render(request, 'login.html', {'next': next, 'error': True}) # ログアウト用view関数を追加 @login_required def logout_view(request): return auth_views.logout_then_login(request)
トップページtemplates/index.html
にログアウトボタンを追加する。
{% load i18n %} <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% trans "Djangoのテストページ" %}</title> </head> <body> <h1 id="title">{% trans "Djangoのテストページ" %}</h1> <button type="button" onclick='alert("{% trans "こんにちは" %}")'>{% trans "ここをクリック" %}</button> <!-- ここを追加 --> <form action="{% url 'logout' %}"> <input type="submit" value="{% trans "ログアウト" %}" /> </form> <!-- 追加ここまで --> </body> </html>
ログイン用テンプレートtemplates/login.html
を作成
{% load i18n %} <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% trans "ログイン" %}</title> </head> <body> <h1 id="title">{% trans "ログイン" %}</h1> {% if error %} <p>{% trans "ユーザ名もしくはパスワードが間違っています。もう一度試してください。" %}</p> {% endif %} <form method="post" action="."> {% csrf_token %} <table> <tr><td>{% trans "ユーザ名" %}: </td><td><input type="text" name="username"></td></tr> <tr><td>{% trans "パスワード" %}: </td><td><input type="password" name="password"></td></tr> </table> <input type="hidden" name="next" value="{{ next }}"> <input type="submit" value="{% trans "ログイン" %}" /> </form> </body> </html>
翻訳用ファイルを更新する。
$ django-admin makemessages -l en
locale/en/LC_MESSAGES/django.po
を編集する。
# : templates/index.html:14 msgid "ログアウト" msgstr "Log out" # 変更 # : templates/login.html:6 templates/login.html:10 templates/login.html:23 msgid "ログイン" msgstr "Log in" # 変更 # : templates/login.html:12 msgid "ユーザ名もしくはパスワードが間違っています。もう一度試してください。" msgstr "Your username and password didn't match. Please try again." # 変更 # : templates/login.html:18 msgid "ユーザ名" msgstr "Username" # 変更 # : templates/login.html:20 msgid "パスワード" msgstr "Password" # 変更
翻訳ファイルをコンパイルする。
$ django-admin compilemessages
以下の2つのコマンドでDBマイグレーションを実施する。
$ python3.6 manage.py makemigrations $ python3.6 manage.py migrate
adminユーザを作成する。
$ python3.6 manage.py createsuperuser ユーザー名 (leave blank to use 'root'): admin メールアドレス: (空欄でも可) Password: (パスワード) Password (again): (パスワード) Superuser created successfully.
Webサーバ起動
$ python3.6 manage.py runserver 0.0.0.0:8000
ブラウザでhttp://(仮想マシンのIPアドレス):8000
にアクセスし、以下を確認する。
- ログインページにリダイレクトされる。
- ログインするとトップページに遷移する。
- 「ログアウト」をクリックするとログアウト処理が行われ、再度ログインページにリダイレクトされる。
ユーザ毎に言語設定機能の追加
i18n/models.py
を作成してDBモデルを追加する。
※:アカウント毎の言語を設定できるようにするため、Userテーブルと1対1関係にあるProfileテーブルを作成してProfileに言語を格納する。
from django.db import models from django.contrib.auth.models import User class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) language = models.CharField(max_length=128, default='', blank=True) # ユーザの設定した言語を格納する def __str__(self): return self.user.username
管理画面でDB設定できるようにi18n/admin.py
を作成する。
from django.contrib import admin from .models import Profile # Register your models here. @admin.register(Profile) class Profile(admin.ModelAdmin): pass
i18n/settings.py
を編集して、Profileモデルを読み込めるようにする。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'i18n', # 追加 ]
i18n/urls.py
に言語設定用のエンドポイントを追加する。
from django.contrib import admin from django.urls import path from . import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index, name='index'), path('login/', views.login_view, name="login"), path('logout/', views.logout_view, name='logout'), path('language/', views.language, name='language'), # 追加 ]
i18n/views.py
に言語設定の処理を追加する。
また、ログイン時に設定してある言語を適用する処理も追加する。
from django.shortcuts import render, redirect, get_object_or_404 # 便利な関数を追加 from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate, login from django.contrib.auth import views as auth_views from .models import Profile # 追加 @login_required def index(request): return render(request, 'index.html') # ログイン用view関数を編集 def login_view(request): if request.method == 'GET': next = request.GET.get('next', '/') return render(request, 'login.html', {'next': next}) elif request.method == 'POST': username = request.POST.get('username', '') password = request.POST.get('password', '') next = request.POST.get('next', '/') print(next) user = authenticate(username=username, password=password) if user is not None: login(request, user) profile = get_object_or_404(Profile, user=request.user) # Profileテーブルから該当のユーザを取得 request.session['_language'] = profile.language # この通信のsessionに言語を設定する return redirect(next) else: return render(request, 'login.html', {'next': next, 'error': True}) # ログアウト用view関数を編集 @login_required def logout_view(request): del request.session['_language'] # このsessionの言語設定を削除する return auth_views.logout_then_login(request) # 言語設定用view関数を追加 @login_required def language(request): if request.method == 'GET': return render(request, 'language.html') elif request.method == 'POST': language = request.POST['language'] profile = get_object_or_404(Profile, user=request.user) # Profileテーブルから該当のユーザを取得 profile.language = language # Profileにユーザからのリクエスト言語を設定 profile.save() # DB更新 request.session['_language'] = language # この通信のsessionに言語を設定する return redirect('language')
request.session['_language']
でセッション情報の言語設定を上書きすることがミソ。
トップページtemplates/index.html
に言語設定ページへのリンクを追加する。
{% load i18n %} <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% trans "Djangoのテストページ" %}</title> </head> <body> <h1 id="title">{% trans "Djangoのテストページ" %}</h1> <button type="button" onclick='alert("{% trans "こんにちは" %}")'>{% trans "ここをクリック" %}</button> <!-- ここを追加 --> <br/> <a href="{% url 'language' %}">{% trans "言語設定" %}</a> <!-- 追加ここまで --> <form action="{% url 'logout' %}"> <input type="submit" value="{% trans "ログアウト" %}" /> </form> </body> </html>
言語設定用テンプレートtemplates/language.html
を作成する。
{% load i18n %} <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% trans "言語設定ページ" %}</title> </head> <body> <h1 id="title">{% trans "言語設定ページ" %}</h1> <form action="." method="post"> {% csrf_token %} <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> <a href="{% url 'index' %}">{% trans "トップページに戻る" %}</a> </body> </html>
あとは翻訳系の作業。翻訳用ファイルを更新する。
$ django-admin makemessages -l en
locale/en/LC_MESSAGES/django.po
を編集する。
# : templates/index.html:12 msgid "言語設定" msgstr "Language setting" # 変更 # : templates/language.html:6 templates/language.html:9 msgid "言語設定ページ" msgstr "Language setting page" # 変更 # : templates/language.html:22 msgid "変更" msgstr "Change" # 変更 # : templates/language.html:25 msgid "トップページに戻る" msgstr "Back to top page" # 変更
翻訳ファイルをコンパイルする。
$ django-admin compilemessages
DBマイグレーション
$ python3.6 manage.py makemigrations i18n $ python3.6 manage.py migrate
Webサーバ起動
$ python3.6 manage.py runserver 0.0.0.0:8000
ブラウザで管理者ページhttp://(仮想マシンのIPアドレス):8000/admin
にアクセスし、adminユーザのProfileを作成する。
管理者ページからログアウトし、トップページにアクセスして以下を確認する。(ログインは前項と同じなので省略)
- 「言語設定」リンクがあること
- 「言語設定」リンクを押すと言語設定ページが表示されること
- プルダウンから英語を設定して「変更」をクリックすると英語のページが表示されること
- トップページに戻ると自分が設定した言語のまま
- ログアウトするとブラウザ本来の言語でログインページが表示されること