最近、mezzanine1のソースコードを読んでいて、django-modeltranslationというライブラリの存在を知ったので、紹介したいと思います。

django-modeltranslatioは動的コンテンツのi18n対応を簡単にできるライブラリです

django-modeltranslatioは動的コンテンツのi18n対応を簡単にできるライブラリです

このライブラリを使うと、モデル定義を変更せずに動的コンテンツのi18n対応ができます。

どんなものなのか、実際にサンプルアプリケーションを作って体験してみましょう。

サンプルアプリケーションで使用する環境は以下のとおりです。

  • Python 3.8.6
  • Django 2.2.17
  • django-modeltranslation 0.16
  • SQLite 3.24.0

サンプルアプリケーションの要件

サンプルアプリケーションの要件は以下のとおりです。

  • 英語・日本語のどちらかの言語で記事を投稿できるニュースサイト
  • ユーザーは自分の言語設定が「英語」なら英語版記事の検索、「日本語」なら日本語版記事の検索を行える

実際に作ってみる

django-modeltranslationのインストール

まず、startprojectコマンドでDjangoプロジェクトを作成し、settings.pyを以下のように編集します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
INSTALLED_APPS = [
    'modeltranslation',  # これを追加。必ずdjango.contrib.adminの上に書く。
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

LANGUAGE_CODE = 'en-us'  # ここはデフォルトのままで


# 以下を追加
def gettext(s):
    return s


LANGUAGES = (
    ('ja', gettext('Japanese')),
    ('en', gettext('English')),
)

記事投稿機能のi18n対応

startappコマンドでnewsというアプリケーションを追加してみましょう。

モデル定義は以下のとおりです。

1
2
3
4
5
6
from django.db import models


class News(models.Model):
    title = models.CharField(max_length=255)
    text = models.TextField()

上記モデルは「英語の記事」「日本語の記事」を区別する設計になっていません。このままではi18n対応はできないはずです。

ところが、django-modeltranslationでは直接ソースコードを編集することなく、メタプログラミングでモデル定義を変更させることができます。

news/translation.pyに以下の内容を書いてください。

1
2
3
4
5
6
7
8
9
from modeltranslation.translator import translator, TranslationOptions
from .models import News


class NewsTranslationOptions(TranslationOptions):
    fields = ('title', 'text')


translator.register(News, NewsTranslationOptions)

makemigrationsmigrateコマンド実行後にsqlmigrateコマンドを実行してテーブル定義を確認してみてください。モデルにはないはずのtitle_jatitle_entext_jatext_enが定義されています。

1
2
3
4
5
6
7
$ ./manage.py sqlmigrate news 0001
BEGIN;
--
-- Create model News
--
CREATE TABLE "news_news" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(255) NOT NULL, "title_ja" varchar(255) NULL, "title_en" varchar(255) NULL, "text" text NOT NULL, "text_ja" text NULL, "text_en" text NULL);
COMMIT;

jaenは前述のsettings.pyに書いたLANGUAGESで定義した言語名です。

それでは、Newsモデルを利用して実際にニュースを登録してみましょう。shellコマンドで以下コードを実行してください。

1
2
3
>>> from news.models import News
>>> News.objects.create(title_ja="木登りできた", title_en="I Could Climb a Tree", text_ja="上野動物園で6月に誕生したパンダ・シャンシャンが、木登りできるようになった。", text_en="Panda Shanshan, born in June at the Ueno Zoo, can now climb trees.")
<News: News object (1)>

_ja_enが付いたフィールドを指定して日本語版・英語版のニュースを登録しています。

さて、このデータのtitletextフィールドはどんな内容になっているでしょうか?

1
2
3
4
>>> from news.models import News
>>> n = News.objects.first()
>>> n.title, n.text
('I Could Climb a Tree', 'Panda Shanshan, born in June at the Ueno Zoo, can now climb trees.')

上記例では英語版が表示されましたが、これは現在の言語設定に依存します。言語を'ja'に変更してもう一度上記コードを実行すると、日本語版が表示されるはずです。

1
2
3
4
5
6
>>> from django.utils.translation import activate
>>> activate('ja')
>>> from news.models import News
>>> n = News.objects.first()
>>> n.title, n.text
('木登りできた', '上野動物園で6月に誕生したパンダ・シャンシャンが、木登りできるようになった。')

ウェブ上で動的に言語設定を切り替えたい場合はset_languageを使います。

記事検索機能のi18n対応

次に、検索機能を試してみましょう。こちらも言語設定によってどのフィールドを検索対象になるかが変わります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>> from django.utils.translation import activate
>>> from news.models import News
>>> # 英語の場合
>>> activate('en-us')
>>> News.objects.filter(text__icontains='panda')
<MultilingualQuerySet [<News: News object (1)>]>
>>> News.objects.filter(text__icontains='パンダ')
<MultilingualQuerySet []>
>>> # 日本語の場合
>>> activate('ja')
>>> News.objects.filter(text__icontains='panda')
<MultilingualQuerySet []>
>>> News.objects.filter(text__icontains='パンダ')
<MultilingualQuerySet [<News: News object (1)>]>

adminのi18n対応

adminからの記事投稿もi18n対応にできます。news/admin.pyを以下のように編集してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from django.contrib import admin
from modeltranslation.admin import TranslationAdmin

from .models import News


class NewsAdmin(TranslationAdmin):
    pass


admin.site.register(News, NewsAdmin)

すると、http://127.0.0.1:8000/admin/news/news/add/は以下のような画面になります。

http://127.0.0.1:8000/admin/news/news/add/の画面

http://127.0.0.1:8000/admin/news/news/add/の画面

titletextフィールドの入力欄は省かれて、title_jatitle_entext_jatext_enだけが入力できるようになっています。

(おまけ)データベース上では titletext には何が入っているのか?

dbshellコマンドで確認してみましょう。

1
2
sqlite> select title, text from news_news;
I Could Climb a Tree|Panda Shanshan, born in June at the Ueno Zoo, can now climb trees.

こちらは登録時の言語設定の値によって英語・日本語のいずれかが入るようです。上記は言語設定が英語の状態で登録したので英語ですが、日本語に切り替えて登録すると日本語が入っていました。


  1. Django製のCMS ↩︎