「英語対応もしたい」は後から出てくる要望の定番です。
すべてを一度に翻訳するのは現実的でないので、段階的に進めます。
有効化
USE_I18N = True
LANGUAGE_CODE = "ja"
LANGUAGES = [("ja", "日本語"), ("en", "English")]
LOCALE_PATHS = [BASE_DIR / "locale"]
MIDDLEWARE = [
...,
"django.middleware.locale.LocaleMiddleware", # Session の後、Common の前
...,
]
LocaleMiddleware の位置が決まっている点に注意します。
順序を間違えると言語切り替えが効きません。
テンプレートの文言
{% load i18n %}
<h1>{% translate "お問い合わせ" %}</h1>
<p>{% blocktranslate %}{{ count }}件の結果{% endblocktranslate %}</p>
Python 側では、遅延評価を使うのが安全です。
from django.utils.translation import gettext_lazy as _
class Order(models.Model):
status = models.CharField(_("ステータス"), max_length=20)
モジュール読み込み時に翻訳を確定させないため、
モデル定義やフォームでは必ず _lazy を使います。
翻訳ファイルの運用
python manage.py makemessages -l en -i venv -i node_modules
# locale/en/LC_MESSAGES/django.po を編集
python manage.py compilemessages
.po(編集用)はリポジトリに入れ、.mo(バイナリ)は
デプロイ時に生成する運用が扱いやすいです。
DBに入っているデータの翻訳
ここが本番です。静的な文言と違い、記事タイトルや商品名は DB にあります。
取れる方法は主に2つです。
A. カラムを増やす
title = models.CharField(max_length=200)
title_en = models.CharField(max_length=200, blank=True)
単純で分かりやすく、言語が2つなら十分です。
3言語以上になるとカラムが増えすぎます。
B. 翻訳テーブルを分ける
class ProductTranslation(models.Model):
product = models.ForeignKey(Product, related_name="translations", ...)
language = models.CharField(max_length=5)
name = models.CharField(max_length=200)
class Meta:
unique_together = [("product", "language")]
言語が増えても構造が変わりません。代わりに JOIN が増えます。
2言語なら A、3言語以上または将来増えるなら B を選んでいます。
未翻訳のときの挙動を決める
翻訳が無い場合に「空欄」を出すのが最悪です。元の言語にフォールバックさせます。
def localized_name(self, lang):
t = self.translations.filter(language=lang).first()
return t.name if t and t.name else self.name
URL 設計
urlpatterns += i18n_patterns(
path("", include("apps.pages.urls")),
prefix_default_language=False, # 日本語は / のまま
)
/about/(日本語)と /en/about/(英語)になります。
検索エンジン向けに hreflang も出しておきます。
<link rel="alternate" hreflang="ja" href="https://example.com/about/">
<link rel="alternate" hreflang="en" href="https://example.com/en/about/">
まとめ
LocaleMiddlewareの位置に注意- モデル・フォームでは
gettext_lazy - DB データは 2言語ならカラム追加、3言語以上なら翻訳テーブル
- 未翻訳は必ず元の言語にフォールバック
i18n_patterns+hreflangで検索エンジンにも伝える