实操多语言博客
一、今日学习内容概述
学习模块 | 重要程度 | 主要内容 |
---|---|---|
国际化配置 | ⭐⭐⭐⭐⭐ | 基础设置、语言切换 |
翻译模型 | ⭐⭐⭐⭐⭐ | 多语言字段、翻译管理 |
视图处理 | ⭐⭐⭐⭐ | 多语言内容展示、URL处理 |
前端实现 | ⭐⭐⭐⭐ | 语言切换、界面适配 |
二、模型设计
python"># models.py
from django.db import models
from django.conf import settings
from django.urls import reverse
from django.utils.translation import gettext_lazy as _class Category(models.Model):"""文章分类"""name = models.CharField(_('名称'), max_length=100)slug = models.SlugField(_('URL标识'), unique=True)class Meta:verbose_name = _('分类')verbose_name_plural = _('分类')def __str__(self):return self.nameclass Post(models.Model):"""博客文章"""category = models.ForeignKey(Category,on_delete=models.CASCADE,verbose_name=_('分类'))created_at = models.DateTimeField(_('创建时间'), auto_now_add=True)updated_at = models.DateTimeField(_('更新时间'), auto_now=True)is_active = models.BooleanField(_('是否激活'), default=True)class Meta:verbose_name = _('文章')verbose_name_plural = _('文章')def get_absolute_url(self):return reverse('post_detail', args=[str(self.id)])class PostTranslation(models.Model):"""文章翻译"""post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name='translations')language = models.CharField(_('语言'),max_length=10,choices=settings.LANGUAGES)title = models.CharField(_('标题'), max_length=200)content = models.TextField(_('内容'))slug = models.SlugField(_('URL标识'), max_length=200)class Meta:unique_together = ('post', 'language')verbose_name = _('文章翻译')verbose_name_plural = _('文章翻译')
三、视图实现
python"># views.py
from django.shortcuts import render, get_object_or_404
from django.utils.translation import get_language
from django.views.generic import ListView, DetailView
from .models import Post, PostTranslationclass PostListView(ListView):template_name = 'blog/post_list.html'context_object_name = 'posts'def get_queryset(self):language = get_language()return Post.objects.filter(translations__language=language,is_active=True).select_related('category').prefetch_related('translations')class PostDetailView(DetailView):model = Posttemplate_name = 'blog/post_detail.html'def get_object(self, queryset=None):post = super().get_object(queryset)language = get_language()translation = get_object_or_404(PostTranslation,post=post,language=language)post.translation = translationreturn postdef get_context_data(self, **kwargs):context = super().get_context_data(**kwargs)context['available_languages'] = PostTranslation.objects.filter(post=self.object).values_list('language', flat=True)return context
四、URL配置
python"># urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
from . import viewsurlpatterns = [path('i18n/', include('django.conf.urls.i18n')),
]urlpatterns += i18n_patterns(path('', views.PostListView.as_view(), name='post_list'),path('<int:pk>/<slug:slug>/', views.PostDetailView.as_view(), name='post_detail'),prefix_default_language=False
)
五、模板实现
<!-- templates/blog/base.html -->
{% load i18n %}
<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE }}">
<head><meta charset="UTF-8"><title>{% block title %}{% trans "多语言博客" %}{% endblock %}</title>
</head>
<body><header><nav><form action="{% url 'set_language' %}" method="post" class="language-form">{% 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 %}{% for lang_code, lang_name in LANGUAGES %}<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %}selected{% endif %}>{{ lang_name }}</option>{% endfor %}</select></form></nav></header><main>{% block content %}{% endblock %}</main>
</body>
</html><!-- templates/blog/post_list.html -->
{% extends "blog/base.html" %}
{% load i18n %}{% block content %}
<div class="container"><h1>{% trans "文章列表" %}</h1>{% for post in posts %}<article class="post-preview"><h2>{{ post.translations.title }}</h2><p class="meta">{% blocktrans with category=post.category.name date=post.created_at %}分类:{{ category }} | 发布于:{{ date }}{% endblocktrans %}</p><div class="excerpt">{{ post.translations.content|truncatewords:50 }}</div><a href="{{ post.get_absolute_url }}" class="read-more">{% trans "" %}</a></article>{% endfor %}
</div>
{% endblock %}
六、流程图
七、管理接口
python"># admin.py
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Category, Post, PostTranslationclass PostTranslationInline(admin.StackedInline):model = PostTranslationextra = 1@admin.register(Post)
class PostAdmin(admin.ModelAdmin):list_display = ('get_title', 'category', 'created_at', 'is_active')list_filter = ('is_active', 'category', 'created_at')search_fields = ('translations__title', 'translations__content')inlines = [PostTranslationInline]def get_title(self, obj):default_trans = obj.translations.filter(language=settings.LANGUAGE_CODE).first()return default_trans.title if default_trans else _('无标题')get_title.short_description = _('标题')@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):list_display = ('name', 'slug')prepopulated_fields = {'slug': ('name',)}
八、表单处理
python"># forms.py
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import Post, PostTranslationclass PostTranslationForm(forms.ModelForm):class Meta:model = PostTranslationfields = ['title', 'content', 'slug']def clean_slug(self):slug = self.cleaned_data['slug']language = self.cleaned_data.get('language')if PostTranslation.objects.filter(slug=slug,language=language).exists():raise forms.ValidationError(_('此URL标识已被使用'))return slugclass PostForm(forms.ModelForm):class Meta:model = Postfields = ['category', 'is_active']
九、中间件
python"># middleware.py
from django.utils import translation
from django.conf import settings
from django.urls import resolveclass LanguageMiddleware:def __init__(self, get_response):self.get_response = get_responsedef __call__(self, request):# 检查URL中的语言代码resolved = resolve(request.path_info)language = resolved.kwargs.get('language')if language:# 如果URL中包含语言代码,则设置语言translation.activate(language)request.LANGUAGE_CODE = languageelse:# 否则使用默认语言或用户偏好language = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME)if language:translation.activate(language)request.LANGUAGE_CODE = languageresponse = self.get_response(request)return response
十、工具函数
python"># utils.py
from django.utils import translation
from django.conf import settingsdef get_translated_field(obj, field_name, language=None):"""获取翻译字段的值"""if language is None:language = translation.get_language()try:trans = obj.translations.get(language=language)return getattr(trans, field_name)except obj.translations.model.DoesNotExist:# 如果找不到翻译,返回默认语言的值try:trans = obj.translations.get(language=settings.LANGUAGE_CODE)return getattr(trans, field_name)except obj.translations.model.DoesNotExist:return Nonedef copy_translation(obj, from_lang, to_lang):"""复制翻译"""try:source = obj.translations.get(language=from_lang)target, created = obj.translations.get_or_create(language=to_lang)if not created:return Falsefor field in ['title', 'content', 'slug']:setattr(target, field, getattr(source, field))target.save()return Trueexcept obj.translations.model.DoesNotExist:return False
通过本章学习,你应该能够:
- 实现多语言博客系统
- 管理翻译内容
- 处理URL国际化
- 优化多语言用户体验
怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!