如何在Django项目中实现多区域多语言的内容版本?
实现Django「语言+区域」双层路由与内容定制方案
技术名称
这种实现方式属于多维度本地化路由(Multi-dimensional Localization Routing),核心是在语言前缀基础上增加区域维度的URL捕获与内容过滤,和Django官网的「语言+版本」路由逻辑完全一致。
分步实现方法
1. 重构URL路由结构
在现有i18n路由基础上,新增区域前缀的捕获规则:
# project/urls.py from django.conf.urls.i18n import i18n_patterns from django.urls import path, include from .views import redirect_to_default_region urlpatterns = [ path('admin/', admin.site.urls), ] # 扩展i18n路由,加入区域前缀 urlpatterns += i18n_patterns( # 匹配「语言/区域/」开头的路径 path('<str:region>/', include('blog.urls')), # 处理无区域前缀的请求,重定向到默认区域 path('', redirect_to_default_region), )
对应的重定向视图:
# project/views.py from django.conf import settings from django.http import HttpResponseRedirect def redirect_to_default_region(request): default_region = getattr(settings, 'DEFAULT_REGION', 'glo') lang_code = request.LANGUAGE_CODE return HttpResponseRedirect(f'/{lang_code}/{default_region}/')
2. 自定义区域中间件
将区域信息绑定到request对象,并支持通过session记住用户选择的区域:
# project/middleware.py from django.conf import settings from django.http import HttpResponseRedirect class RegionMiddleware: def __init__(self, get_response): self.get_response = get_response self.default_region = getattr(settings, 'DEFAULT_REGION', 'glo') self.allowed_regions = getattr(settings, 'ALLOWED_REGIONS', ['glo', 'us', 'eu']) def __call__(self, request): # 从URL解析区域 path_parts = request.path.lstrip('/').split('/') if len(path_parts) >=2 and path_parts[1] in self.allowed_regions: request.region = path_parts[1] else: # 优先从session取,无则用默认 request.region = request.session.get('region', self.default_region) lang_code = path_parts[0] if path_parts else settings.LANGUAGE_CODE return HttpResponseRedirect(f'/{lang_code}/{request.region}/') # 存入session,维持用户选择 request.session['region'] = request.region response = self.get_response(request) return response
在settings.py中注册中间件(放在LocaleMiddleware之后):
MIDDLEWARE = [ # ...其他中间件 'django.middleware.locale.LocaleMiddleware', 'project.middleware.RegionMiddleware', ] # 新增区域配置 ALLOWED_REGIONS = ['glo', 'us', 'eu'] DEFAULT_REGION = 'glo'
3. 适配django-parler的区域内容过滤
给文章模型新增区域字段,在视图中基于当前区域过滤内容:
# blog/models.py from django.db import models from parler.models import TranslatableModel, TranslatedFields class Post(TranslatableModel): translations = TranslatedFields( title=models.CharField(max_length=200), content=models.TextField(), ) # 区域字段:glo=全球,us=美国,eu=欧盟 region = models.CharField( max_length=3, choices=[('glo', 'Global'), ('us', 'US'), ('eu', 'EU')], default='glo' ) # 其他字段...
视图中过滤查询集:
# blog/views.py from django.views.generic import ListView from .models import Post from django.db.models import Q class PostListView(ListView): model = Post template_name = 'blog/post_list.html' def get_queryset(self): # 显示当前区域内容 + 全球通用内容 return super().get_queryset().filter( Q(region=self.request.region) | Q(region='glo') )
4. 实现语言+区域选择菜单
在模板中添加选择器,语言选择复用Django i18n标签,区域选择生成对应URL:
<!-- templates/base.html --> {% load i18n %} <!-- 语言选择器 --> <form action="{% url 'set_language' %}" method="post"> {% csrf_token %} <input name="next" type="hidden" value="{{ request.path }}"> <select name="language" onchange="this.form.submit()"> {% get_current_language as LANGUAGE_CODE %} {% get_available_languages as LANGUAGES %} {% get_language_info_list for LANGUAGES as languages %} {% for lang in languages %} <option value="{{ lang.code }}" {% if lang.code == LANGUAGE_CODE %}selected{% endif %}> {{ lang.name_local }} ({{ lang.code }}) </option> {% endfor %} </select> </form> <!-- 区域选择器 --> <div class="region-selector"> {% with current_region=request.region %} <a href="/{{ LANGUAGE_CODE }}/glo/" {% if current_region == 'glo' %}class="active"{% endif %}>Global</a> <a href="/{{ LANGUAGE_CODE }}/us/" {% if current_region == 'us' %}class="active"{% endif %}>US</a> <a href="/{{ LANGUAGE_CODE }}/eu/" {% if current_region == 'eu' %}class="active"{% endif %}>EU</a> {% endwith %} </div>
关键注意事项
- 确保
ALLOWED_REGIONS配置的区域代码和URL中的捕获值完全一致 - 若需要区域专属的配置(如时区、支付方式),可在
settings.py中新增REGION_SETTINGS字典存储 - 可扩展区域模型到数据库,支持后台动态管理区域配置
内容的提问来源于stack exchange,提问作者mdev
相关产品推荐
相关产品推荐

