
目次
- 1 user@sinyblog:~/article ❯ 01_concepts.mdi18n / l10n / m17n の違い
- 2 user@sinyblog:~/article ❯ 02_overview.md実装の全体像 — 4 ステップ
- 3 user@sinyblog:~/article ❯ 03_settings.mdStep 1. アプリケーション設定(settings.py)
- 4 user@sinyblog:~/article ❯ 04_routing.mdStep 2. URL ルーティング設定
- 5 user@sinyblog:~/article ❯ 05_marking.mdStep 3. 翻訳用の文字列をマーキング
- 6 user@sinyblog:~/article ❯ 06_messages.mdStep 4. メッセージファイルの生成・編集・コンパイル
- 7 user@sinyblog:~/article ❯ 07_url_switch.mdURL prefix で言語切り替え
- 8 user@sinyblog:~/article ❯ 08_admin_switch.mdadmin サイトに言語切り替えボタンを追加
- 9 user@sinyblog:~/article ❯ 09_user_switch.md一般画面に言語切り替えメニューを表示
- 10 user@sinyblog:~/article ❯ 99_summary.mdまとめ
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 ステップ
- アプリケーション設定 — settings.py に MIDDLEWARE / LANGUAGE_CODE / LANGUAGES / LOCALE_PATHS を設定
- URL ルーティング設定 — i18n_patterns で言語 prefix を URL に組み込む
- 翻訳用の文字列をマーキング — models.py / views.py / templates で
gettext_lazyや{% trans %}を使う(一番大変な部分) - メッセージファイルの生成・編集・コンパイル —
makemessagesで .po 生成 → 翻訳記入 →compilemessagesで .mo 生成
user@sinyblog:~/article ❯ 03_settings.mdStep 1. アプリケーション設定(settings.py)
① MIDDLEWARE に LocaleMiddleware を追加
言語設定を自動で検出してくれる LocaleMiddleware を MIDDLEWARE に追加します。
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 は以下の順で言語をチェックします。
- URL の language prefix(
/en/,/ja/等) - Language cookie(
LANGUAGE_COOKIE_NAME) - Request の
Accept-LanguageHTTP ヘッダ - 上記が見つからない場合は
LANGUAGE_CODE設定値
SessionMiddlewareの 後 にLocaleMiddlewareを置く(セッションを利用するため)CommonMiddlewareの 前 にLocaleMiddlewareを置く(URL prefix を利用するため)
② LANGUAGE_CODE — デフォルト言語
LANGUAGE_CODE = 'ja'
③ USE_I18N — 翻訳機能を有効化
USE_I18N = True # 翻訳機能を有効化
古い記事では USE_L10N = True も書かれていますが、Django 4.0 で非推奨化、5.0 で削除されました。常に有効として扱われるため、新規プロジェクトでは記述不要です。Django 4.x までは記述しても問題ありません。
④ LANGUAGES — 翻訳に使う言語を限定
from django.utils.translation import gettext_lazy as _
LANGUAGES = [
('en', _('English')),
('ja', _('Japanese')),
]
Django 4.0 で ugettext_lazy は gettext_lazy に統合されました。古い記事のサンプルコードに ugettext_lazy がある場合は、すべて gettext_lazy に置き換えてください(挙動は同じ)。
⑤ LOCALE_PATHS — 言語ファイルの配置場所
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 を使います。
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 を使います(慣例で _ としてインポート)。
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 ではなく 遅延翻訳の gettext_lazy が必須です。前者を使うと意図した言語に切り替わらないバグの原因になります。
views.py でのマーキング
ビューなど実行時に呼ばれる場所では gettext を使います。
from django.http import HttpResponse
from django.utils.translation import gettext as _
def my_view(request):
output = _("ようこそホームページへ")
return HttpResponse(output)
テンプレートでのマーキング
HTML テンプレートでは {% trans %} タグを使います。
{% 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 ファイルを生成
python manage.py makemessages -l ja
マーキングされた文字列をすべてスキャンして、<プロジェクト>/locale/ja/LC_MESSAGES/django.po を生成します。既に .po が存在する場合は、新しいマーキングを追記してくれます。
例えば簡体字中国語の場合、言語コードは zh-hans ですがロケール名は zh_Hans。makemessages -l zh_Hans のようにロケール名を指定します。
② .po ファイルに翻訳を記入
生成された .po ファイルの msgstr 部分に、対応する翻訳文字列を記入します。
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 ファイルにコンパイル
python manage.py compilemessages
.po ファイルを Django が実行時に読める .mo ファイルに変換します。翻訳を変更した後は必ず再コンパイルが必要です。
user@sinyblog:~/article ❯ 07_url_switch.mdURL prefix で言語切り替え
ここまでの設定が完了すれば、URL の prefix を変えるだけで言語が切り替わります。

/ja/kakeibo/kakeibo_list/ — 日本語表示
/en/kakeibo/kakeibo_list/ — 英語表示user@sinyblog:~/article ❯ 08_admin_switch.mdadmin サイトに言語切り替えボタンを追加
admin サイトの右上に国旗アイコンの言語切り替えボタンを表示させる実装です。
① テンプレートタグを作成
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 を新規作成します。
{% 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 %}
古い記事では {% load staticfiles %} と書かれていますが、Django 2.1+ では {% load static %} に統合されています。
③ CSS と画像を配置
.i18n_flag img {
width: 16px;
vertical-align: text-top;
}
static/images/ 配下に flag-ja-16.png と flag-usa-16.png を配置します。

user@sinyblog:~/article ❯ 09_user_switch.md一般画面に言語切り替えメニューを表示
admin だけでなく、一般ユーザー画面に言語切り替えのプルダウンメニューを表示させる実装です。
① urls.py に i18n エンドポイントを追加
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,
)
② テンプレートに言語切り替えフォームを追加
<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 つです。
- settings.py に LocaleMiddleware を追加し、LANGUAGES / LOCALE_PATHS を設定するのが土台
- i18n_patterns + prefix_default_language で URL ベースの言語切り替えが完成
- マーキングは models = gettext_lazy / views = gettext / templates = {% trans %} の使い分けを徹底
- makemessages → 翻訳記入 → compilemessages のサイクルを翻訳更新のたびに回す
マーキング作業が一番大変ですが、一度仕組みが出来れば言語追加(LANGUAGES に項目追加 → makemessages → 翻訳記入 → compilemessages)で 3 言語目以降も対応できます。
ugettext_lazy 廃止 / USE_L10N 削除 / staticfiles タグ統合)を反映しました。家計簿アプリを題材にした実装手順は元のサンプルを踏襲しています。運営者(現役 IT エンジニア・15 年以上の業界経験)。
