【Django】多言語化(i18n)完全実装ガイド|gettext_lazy・makemessages・言語切り替えメニューまで

スポンサードリンク



目次

Django · Internationalization · 2026 Edition

外向けに Django アプリを公開したり、社内向けに英日両対応する場面で必須になるのが 多言語対応(i18n) です。本記事では、PyCon US 2020 のセッション内容をベースに、settings.py の各種設定 → URL ルーティング → 翻訳文字列のマーキング → メッセージファイルの生成・コンパイル → admin / 一般画面の言語切り替えメニュー実装まで、家計簿アプリを題材に完全解説します。所要時間 30 分。

user@sinyblog:~/article 01_concepts.mdi18n / l10n / m17n の違い

Django の翻訳機能を理解する上で、まず以下の 3 つの用語を整理しておきます。

用語 正式名称 意味
i18n Internationalization 多言語化対応(複数言語に切り替えられる土台を作ること)
l10n Localization 地域に合わせた最適化(具体的な翻訳・日付フォーマットを当てはめること)
m17n Multilingualization 複数言語を同時に扱える状態(エディタ・ブラウザ等)

Django の実装手順では、最初の 3 ステップ(アプリ設定・ルーティング・マーキング)が i18n、最後のメッセージファイル生成・編集が l10n に該当します。

user@sinyblog:~/article 02_overview.md実装の全体像 — 4 ステップ

  1. アプリケーション設定 — settings.py に MIDDLEWARE / LANGUAGE_CODE / LANGUAGES / LOCALE_PATHS を設定
  2. URL ルーティング設定 — i18n_patterns で言語 prefix を URL に組み込む
  3. 翻訳用の文字列をマーキング — models.py / views.py / templates で gettext_lazy{% trans %} を使う(一番大変な部分)
  4. メッセージファイルの生成・編集・コンパイルmakemessages で .po 生成 → 翻訳記入 → compilemessages で .mo 生成

user@sinyblog:~/article 03_settings.mdStep 1. アプリケーション設定(settings.py)

① MIDDLEWARE に LocaleMiddleware を追加

言語設定を自動で検出してくれる LocaleMiddleware を MIDDLEWARE に追加します。

python— settings.py


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',
]

LocaleMiddleware は以下の順で言語をチェックします。

  1. URL の language prefix(/en/, /ja/ 等)
  2. Language cookie(LANGUAGE_COOKIE_NAME)
  3. Request の Accept-Language HTTP ヘッダ
  4. 上記が見つからない場合は LANGUAGE_CODE 設定値
MIDDLEWARE の順序が重要

  • SessionMiddlewareLocaleMiddleware を置く(セッションを利用するため)
  • CommonMiddlewareLocaleMiddleware を置く(URL prefix を利用するため)

② LANGUAGE_CODE — デフォルト言語

python— settings.py


LANGUAGE_CODE = 'ja'

③ USE_I18N — 翻訳機能を有効化

python— settings.py


USE_I18N = True   # 翻訳機能を有効化
USE_L10N は Django 5.0 以降不要

古い記事では USE_L10N = True も書かれていますが、Django 4.0 で非推奨化、5.0 で削除されました。常に有効として扱われるため、新規プロジェクトでは記述不要です。Django 4.x までは記述しても問題ありません。

④ LANGUAGES — 翻訳に使う言語を限定

python— settings.py


from django.utils.translation import gettext_lazy as _

LANGUAGES = [
    ('en', _('English')),
    ('ja', _('Japanese')),
]
ugettext_lazy は廃止済み

Django 4.0 で ugettext_lazygettext_lazy に統合されました。古い記事のサンプルコードに ugettext_lazy がある場合は、すべて gettext_lazy に置き換えてください(挙動は同じ)。

⑤ LOCALE_PATHS — 言語ファイルの配置場所

python— settings.py


import os

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

BASE_DIR 直下の locale/ ディレクトリに翻訳ファイルが配置されます。設定しない場合、各アプリケーションの locale/ ディレクトリが参照されます。

user@sinyblog:~/article 04_routing.mdStep 2. URL ルーティング設定

URL の頭に言語コード(/en/, /ja/ 等)を埋め込むため、i18n_patterns を使います。

python— urls.py


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


urlpatterns = i18n_patterns(
    path('admin/', admin.site.urls),
    path('kakeibo/', include('kakeibo.urls')),
    prefix_default_language=False,
)
URL 例 表示される言語
/en/kakeibo/ 英語
/ja/kakeibo/ 日本語
/kakeibo/ (prefix なし) デフォルト言語(LANGUAGE_CODE)

prefix_default_language=False を指定すると、デフォルト言語のときは prefix なしの URL でアクセスできるようになります。

user@sinyblog:~/article 05_marking.mdStep 3. 翻訳用の文字列をマーキング

翻訳対象の文字列を、コード内で「これは翻訳する文字列だよ」とマーキングしていきます。本実装で一番手間のかかる部分です。

models.py でのマーキング

モデルでは gettext_lazy を使います(慣例で _ としてインポート)。

python— kakeibo/models.py


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


class Category(models.Model):
    class Meta:
        verbose_name = _('category')
        verbose_name_plural = _('category')

    category_name = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.category_name


class Kakeibo(models.Model):
    class Meta:
        verbose_name = _("Household account book")
        verbose_name_plural = _("Household account book")

    date = models.DateField(_("date"), default=datetime.now)
    category = models.ForeignKey(Category, on_delete=models.PROTECT, verbose_name=_("category"))
    money = models.IntegerField(_("Amount of money"), help_text=_("The unit is Japanese yen"))
    quantity = models.IntegerField(verbose_name=_("quantity"), default=0)
    memo = models.CharField(verbose_name=_("memo"), max_length=500)

    def __str__(self):
        return self.memo
モデルでは必ず gettext_lazy を使う

モデルクラスはモジュール読み込み時に評価されるため、即時翻訳の gettext ではなく 遅延翻訳の gettext_lazy が必須です。前者を使うと意図した言語に切り替わらないバグの原因になります。

views.py でのマーキング

ビューなど実行時に呼ばれる場所では gettext を使います。

python— kakeibo/views.py


from django.http import HttpResponse
from django.utils.translation import gettext as _


def my_view(request):
    output = _("ようこそホームページへ")
    return HttpResponse(output)

テンプレートでのマーキング

HTML テンプレートでは {% trans %} タグを使います。

html— kakeibo/templates/kakeibo_list.html


{% load i18n %}

<th class="text-center">{% trans "date" %}</th>
<th class="text-center">{% trans "category" %}</th>
<th class="text-center">{% trans "Amount of money" %}</th>
<th class="text-center">{% trans "quantity" %}</th>
<th class="text-center">{% trans "total fee" %}</th>
<th class="text-center">{% trans "memo" %}</th>
<th class="text-center">{% trans "Update / Delete" %}</th>

user@sinyblog:~/article 06_messages.mdStep 4. メッセージファイルの生成・編集・コンパイル

① makemessages — .po ファイルを生成

bash— manage.py


python manage.py makemessages -l ja

マーキングされた文字列をすべてスキャンして、<プロジェクト>/locale/ja/LC_MESSAGES/django.po を生成します。既に .po が存在する場合は、新しいマーキングを追記してくれます。

言語コードとロケール名の違い

例えば簡体字中国語の場合、言語コードは zh-hans ですがロケール名は zh_Hansmakemessages -l zh_Hans のようにロケール名を指定します。

② .po ファイルに翻訳を記入

生成された .po ファイルの msgstr 部分に、対応する翻訳文字列を記入します。

po— locale/ja/LC_MESSAGES/django.po(編集後)


msgid "category"
msgstr "カテゴリ"

msgid "Household account book"
msgstr "家計簿アプリ"

msgid "date"
msgstr "日付"

msgid "Amount of money"
msgstr "金額"

msgid "The unit is Japanese yen"
msgstr "単位は日本円"

msgid "quantity"
msgstr "数量"

msgid "memo"
msgstr "メモ"

msgid "total fee"
msgstr "合計金額"

msgid "Update / Delete"
msgstr "更新/削除"

msgid "English"
msgstr "英語"

msgid "Japanese"
msgstr "日本語"

③ compilemessages — .mo ファイルにコンパイル

bash— manage.py


python manage.py compilemessages

.po ファイルを Django が実行時に読める .mo ファイルに変換します。翻訳を変更した後は必ず再コンパイルが必要です。

user@sinyblog:~/article 07_url_switch.mdURL prefix で言語切り替え

ここまでの設定が完了すれば、URL の prefix を変えるだけで言語が切り替わります。

日本語(/ja/)で表示された家計簿アプリの一覧画面
/ja/kakeibo/kakeibo_list/ — 日本語表示
英語(/en/)で表示された家計簿アプリの一覧画面
/en/kakeibo/kakeibo_list/ — 英語表示

user@sinyblog:~/article 08_admin_switch.mdadmin サイトに言語切り替えボタンを追加

admin サイトの右上に国旗アイコンの言語切り替えボタンを表示させる実装です。

① テンプレートタグを作成

python— kakeibo/templatetags/i18n_switcher.py


from django import template
from django.template.defaultfilters import stringfilter
from django.conf import settings


def switch_lang_code(path, language):
    lang_codes = 

    if path == '':
        raise Exception('URL path for language switch is empty')
    elif path[0] != '/':
        raise Exception('URL path for language switch does not start with "/"')
    elif language not in lang_codes:
        raise Exception(f'{language} is not a supported language code')

    parts = path.split('/')

    if parts[1] in lang_codes:
        parts[1] = language
    else:
        parts[0] = "/" + language

    return '/'.join(parts)


register = template.Library()


@register.filter
@stringfilter
def switch_i18n_prefix(path, language):
    return switch_lang_code(path, language)


@register.filter
def switch_i18n(request, language):
    return switch_lang_code(request.get_full_path(), language)

② admin テンプレートを上書き

<project>/templates/admin/base_site.html を新規作成します。

html— templates/admin/base_site.html


{% extends "admin/base_site.html" %}
{% load i18n %}
{% load static %}
{% load i18n_switcher %}

{% block extrahead %}
    <link rel="shortcut icon" href="{% static 'images/favicon.ico' %}" />
    <link rel="stylesheet" type="text/css" href="{% static 'css/custom_admin.css' %}"/>
{% endblock %}

{% block userlinks %}
    <a href="{{ request|switch_i18n:'en' }}">
        <img class="i18n_flag" src="{% static 'images/flag-usa-16.png' %}"/>
    </a> /
    <a href="{{ request|switch_i18n:'ja' }}">
        <img class="i18n_flag" src="{% static 'images/flag-ja-16.png' %}"/>
    </a> /
    {% if user.is_active and user.is_staff %}
        {% url 'django-admindocs-docroot' as docsroot %}
        {% if docsroot %}
            <a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
        {% endif %}
    {% endif %}
    {% if user.has_usable_password %}
        <a href="{% url 'admin:password_change' %}">{% trans 'Change password' %}</a> /
    {% endif %}
    <a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>
{% endblock %}
staticfiles タグは廃止済み

古い記事では {% load staticfiles %} と書かれていますが、Django 2.1+ では {% load static %} に統合されています。

③ CSS と画像を配置

css— static/css/custom_admin.css


.i18n_flag img {
    width: 16px;
    vertical-align: text-top;
}

static/images/ 配下に flag-ja-16.pngflag-usa-16.png を配置します。

admin サイト右上に英語/日本語の国旗アイコンが表示されている
admin サイトに国旗ボタンが追加された状態

user@sinyblog:~/article 09_user_switch.md一般画面に言語切り替えメニューを表示

admin だけでなく、一般ユーザー画面に言語切り替えのプルダウンメニューを表示させる実装です。

① urls.py に i18n エンドポイントを追加

python— urls.py


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

urlpatterns = [
    path('i18n/', include('django.conf.urls.i18n')),  # 言語切り替え用エンドポイント
]

urlpatterns += i18n_patterns(
    path('admin/', admin.site.urls),
    path('kakeibo/', include('kakeibo.urls')),
    prefix_default_language=False,
)

② テンプレートに言語切り替えフォームを追加

html— kakeibo_list.html(抜粋)


<div class="btn-group float-right">
    <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>
</div>
家計簿アプリの一覧画面の右上に言語切り替えプルダウンが表示されている
一般画面右上にプルダウン式の言語切り替えメニュー

user@sinyblog:~/article 99_summary.mdまとめ

本記事のポイントは 4 つです。

  1. settings.py に LocaleMiddleware を追加し、LANGUAGES / LOCALE_PATHS を設定するのが土台
  2. i18n_patterns + prefix_default_language で URL ベースの言語切り替えが完成
  3. マーキングは models = gettext_lazy / views = gettext / templates = {% trans %} の使い分けを徹底
  4. makemessages → 翻訳記入 → compilemessages のサイクルを翻訳更新のたびに回す

マーキング作業が一番大変ですが、一度仕組みが出来れば言語追加(LANGUAGES に項目追加 → makemessages → 翻訳記入 → compilemessages)で 3 言語目以降も対応できます。

本記事は 2020 年 4 月に PyCon US 2020 のセッションをベースに初版を公開し、2026 年 5 月に sinytech デザインへ移行・Django 4.0+ の API 変更(ugettext_lazy 廃止 / USE_L10N 削除 / staticfiles タグ統合)を反映しました。家計簿アプリを題材にした実装手順は元のサンプルを踏襲しています。運営者(現役 IT エンジニア・15 年以上の業界経験)。

おすすめの記事