2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

DjangoのユーザーにLanguageとTimezone設定をもたせて動的に言語を切り替えたり時刻表示を切り替える

Last updated at Posted at 2020-05-05

やりたいこと

これ

ユーザーに言語やタイムゾーンを設定させたい

Djangoファンの皆さん、ドジャンゴー(挨拶)。

私達はDjangoがI18NやL10Nに対応していることをよく知っています。そして、いともたやすくそれらの力を利用できることを知っているはずです。

多言語翻訳が必要なら USE_I18N = True として LANGUAGE_CODEを指定、フォーマットローカライゼーションが必要なら USE_L10N = True、タイムゾーンをあわせたいなら USE_TZ = True として TIMEZONE を指定するだけなんですから。

そして、Google翻訳やDeepL翻訳にお願いして翻訳ファイルを作っていくだけでグローバルなアプリケーションを作り上げることができます。もしあなたがDjangoを使ってアプリケーションを作っているのなら、すぐにでも国際展開を念頭においたアプリケーションを作ることができます。

しかし、問題があります。

もし、国際展開を念頭においたアプリをあなたが作ったとして、日本人、アメリカ人、フランス人、アジア諸国の人が利用していたとして、その言語設定は一体どこで切り替えたら良いのでしょうか。

そう、settings.py です。しかしLANGUAGE_CODEの設定をいじってしまったら最後、指定した言語でDjangoは出力を行います。もしLANGUAGE_CODEjaとした場合は、各国それぞれの人にDjangoは日本語で表示を行います。そうなると、一体なんのために国際化したのかよくわからなくなってしまいます。

個人的にもこれは不思議でなりませんが、Django標準ではLanguageはおろか、Timezoneの設定すらユーザー側では変更することができません。今回はそれをユーザーが自分で変更可能にするというお話です。

仕組み

LANGUAGEについて

基本となるコードはdjangoのマニュアルから抜粋これです。利用したい言語をtranslation.activateに渡し、クッキーに値をセットします。これでDjangoがこのセッション中で利用する言語のみを切り替えることができます。

from django.conf import settings
from django.http import HttpResponse
from django.utils import translation
user_language = 'fr'
translation.activate(user_language)
response = HttpResponse(...)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)

今回はユーザーモデルに対して

TIMEZONEについて

基本となるコードはdjangoのマニュアルから抜粋これです。
利用したいタイムゾーンをtimezone.activateに渡します。

import pytz

from django.utils import timezone

class TimezoneMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        tzname = request.session.get('django_timezone')
        if tzname:
            timezone.activate(pytz.timezone(tzname))
        else:
            timezone.deactivate()
        return self.get_response(request)

あとはこれらのコードを基礎として、ユーザーモデルをカスタマイズして言語設定とタイムゾーン設定を扱うフィールドを用意し、ユーザー自身がこれを変更可能にすることです。

というわけで、今回用意したのがdjango-user-g11nです。

django-user-g11n

django-user-g11nは、Djangoのカスタムユーザーモデルを用意し、そこに "TimeZoneAndUserLanguageSupportMixin"を継承することでlanguagetimezone、2つのフィールドをユーザーモデルに持たせ、Middlewareを用いて設定された言語やタイムゾーンを動的に変更します。

ちなみに、g11nとは(Globalization)を省略したもので、I18n+L10Nを指します。

使い方

の前に、もしサンプルをすぐに試したい方は"サンプルを利用する"を参照してください。

まずはDjangoとdjango-user-g11nをインストールします。

$ pip install django django-user-g11n

次にdjango-admin.pyを用いてプロジェクトをスタートします。

$ django-admin.py startproject example

次に、カスタムユーザーモデルを作成します。ここでは便宜上accountsというアプリケーション名にします。

$ manage.py startapp accounts

アプリを作り終えたら、accounts/models.pyを変更して、カスタムユーザーモデルを追加します。
その際に、UserLanguageSupportMixinUserTimeZoneSupportMixinを継承します。

from django.contrib.auth import models as auth_models
from user_g11n.models import UserLanguageSupportMixin, UserTimeZoneSupportMixin


class User(UserTimeZoneSupportMixin,
           UserLanguageSupportMixin,
           auth_models.AbstractUser):
    pass

次はsettings.pyの変更です。

settings.pyの変更

INSTALLED_APPSに今回作成したカスタムユーザーモデル用のアプリと、user-g11nを追加します。

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    .
    .
    .
    'accounts',  # Your Custom user model application
    'user_g11n', # Add
)

次にMIDDLEWAREを変更し、UserLanguageMiddlewareUserTimeZoneMiddlewareを追加します。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    .
    .
    .
    'user_g11n.middleware.UserLanguageMiddleware', # Add
    'user_g11n.middleware.UserTimeZoneMiddleware', # Add
]

そして、AUTH_USER_MODELを指定し、カスタムユーザーモデルを利用可能にします。

AUTH_USER_MODEL = 'accounts.User'

次にI18N, L10N, TZ、TIME_ZONEをそれぞれ設定します。

USE_I18N = True

USE_L10N = True

USE_TZ = True

TIME_ZONE = "Asia/Tokyo" # Change to your local timezone

変更が完了したらmigrationを行いDBを作成します。

$ ./manage.py makemigrations && ./manage.py migrate

これで完了です。あとはsuperuserを作成し、ユーザーの管理画面で言語、タイムゾーンの変更をおこなうことで、リアルタイムに表示が切り替わります。

サンプルを利用する

このDjangoアプリはGitHub上にて開発を行っています。

プロジェクトをCloneしてもらい、

$ docker-compose up

したあと、http://localhost:8000にアクセスすることで、サンプルアプリを試すことができます。

まとめ

  • Djangoの国際化機構最高
  • 最初からこれ用意してくれよって思う。なぜないのか…
  • DjangoおぢさんはDjangoが大好き。
  • 今年(2020)はCovid-19の影響でDjango Congressなくなっちゃったけど、俺らの心のなかでは開催されているのだ!毎日のように・・・!

2022/5 にUpdate

Django4 からpytzがtimezoneに置き換わることにより、動かなくなる事態が発生したのでUpdateしました。

対応したのは以下の通り

  • Django3ならPytzを利用(timezoneは入れられれば使えるけど、面倒なので対応無し)
  • Django4で
    • USE_DEPRECATED_PYTZがFalseならばtimezoneを利用
    • USE_DEPRECATED_PYTZがTrueならばpytzを利用

以上

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?