跳转到主要内容

Django模型翻译,无需魔法伤害

项目描述

CI Status

Django模型翻译,无需魔法伤害。

安装和使用

在Python环境中安装django-translated-fields后,您只需在设置中定义LANGUAGES,并将翻译字段添加到模型中即可。

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

from translated_fields import TranslatedField

class Question(models.Model):
    question = TranslatedField(
        models.CharField(_("question"), max_length=200),
    )
    answer = TranslatedField(
        models.CharField(_("answer"), max_length=200),
    )

    def __str__(self):
        return self.question

基本用法

模型字段会自动从传递给TranslatedField的字段创建,每个语言一个字段。例如,如果LANGUAGES = [("en", "English"), ("de", "German"), ("fr", "French")],则将创建以下字段列表:question_enquestion_dequestion_franswer_enanswer_deanswer_fr

这意味着在更改 LANGUAGES 时,您还必须运行 makemigrationsmigrate

实际上没有创建任何 questionanswer 模型字段。 TranslatedField 实例是一个 描述符,默认情况下充当当前语言字段的属性。

from django.utils.translation import override

question = Question(
    question_en="How are you?",
    question_de="Wie geht es Dir?",
    question_fr="Ça va?",
)

# The default getter automatically returns the value
# in the current language:
with override("en"):
    assert question.question == "How are you?"

with override("de"):
    assert question.question == "Wie geht es Dir?"

# The default setter can also be used to set the value
# in the current language:
with override("fr"):
    question.question = "Comment vas-tu?"

assert question.question_fr == "Comment vas-tu?"

TranslatedField 有一个 fields 属性,它返回创建的所有语言字段的列表。

assert Question.answer.fields == ["answer_en", "answer_de", "answer_fr"]

有关更多属性,请参阅下面的 ``TranslatedField`` 实例 API 部分。

questionanswer 只能与模型实例一起使用,它们在数据库中不存在。如果您想使用引用单个翻译字段的查询集方法,您必须自己使用特定语言的字段名称。如果您只想获取英文问题字段和答案字段,可以按以下方式操作

questions = Question.objects.values_list("question_en", "answer_en")

或者更好的方法是使用 to_attribute 辅助工具,该工具自动使用活动语言(如果未传递特定的语言代码作为其第二个参数)

from django.utils.translation import override
from translated_fields import to_attribute

with override("en"):
    questions = Question.objects.values_list(
        to_attribute("question"), to_attribute("answer")
    )

按语言更改字段属性

有时按语言具有略微不同的模型字段是有用的,例如,为了使主语言成为必填项。可以通过将包含每个语言的键值参数的字典作为 TranslatedField 的第二个位置参数传递来实现这一点。

例如,如果您在站点已运行时向 LANGUAGES 中添加语言,则可能有用地使新语言成为非必填项,以简化通过 Django 的管理界面编辑现有数据。

以下示例将 blank=True 添加到西班牙语字段

from translated_fields import TranslatedField

class Question(models.Model):
    question = TranslatedField(
        models.CharField(_("question"), max_length=200),
        {"es": {"blank": True}},
    )

重写属性访问(默认值,回退)

没有默认值或回退,只有包装的属性访问。默认属性获取器和设置函数简单地返回或设置当前语言(由 django.utils.translation.get_language 返回)的字段。默认获取器在 get_language() 返回 None 时回退到字段的第一种语言。除此之外,默认获取器没有安全功能,可能会引发 AttributeError,并且设置器可能会在模型实例上设置与模型字段不相关的属性。

可以通过指定自己的 attrgetterattrsetter 函数来覆盖获取器和设置器。如果您想始终回退到默认语言并允许其他语言的字段为空,则可以使用 TranslatedFieldWithFallback

from translated_fields import TranslatedFieldWithFallback

class Question(models.Model):
    question = TranslatedFieldWithFallback(
        models.CharField(_("question"), max_length=200),
    )

它的作用是:为所有语言添加问题字段,并在当前语言的字段为空或根本未激活任何语言时自动回退到第一个定义的语言。它还将 blank=True 设置在所有字段实例上,除了第一个。由于这是一个非常常见的用例,因此可以直接使用 TranslatedFieldWithFallback,或者您可以使用 translated_fields.utils.fallback_to_default attrgetter。

可能需要回退到任何语言的不同用例,这由捆绑的 translated_fields.utils.fallback_to_any attrgetter 处理。

另一个用例可能是在使用具有区域代码的定位(例如 fr-fr)时,您想回退到不带区域代码的语言。以下是一个 attrgetter 实现示例

from translated_fields import to_attribute

def fallback_to_all_regions(name, field):
    def getter(self):
        value = getattr(self, to_attribute(name), None)
        if value:
            return value
        return getattr(self, to_attribute(name, get_language().split("-")[0]))

    return getter

以下是一个始终设置所有字段的自定义 attrsetter(可能不是很实用,但希望具有指导意义)

def set_all_fields(name, field):
    def setter(self, value):
        for field in field.fields:
            setattr(self, field, value)
    return setter

TranslatedField 实例 API

《TranslatedField》描述符具有一些有用的属性(继续使用上面示例中的模型和字段)

  • Question.question.fields 包含所有自动生成的字段名称,例如 ["question_en", "question_...", ...]

  • Question.question.languages 是语言代码列表。

  • Question.question.short_description 被设置为基本字段的 verbose_name,这样就可以在例如 ModelAdmin.list_display 中很好地使用可翻译属性。

使用不同的语言集

也可以覆盖使用的语言代码列表,例如如果你想要翻译 settings.LANGUAGES 的子集或超集。结合使用 attrgetterattrsetter,你就可以使用这个字段进行不同类型的翻译,而不仅限于 django.utils.translation 或语言本身。

无需创建模型字段即可翻译属性

如果不想创建模型字段,也可以使用 translated_attributes 类装饰器。这将只创建属性获取器属性。

from translated_fields import translated_attributes

@translated_attributes("attribute", "anything", ...)
class Test(object):
    attribute_en = "some value"
    attribute_de = "some other value"

模型管理器支持

TranslatedFieldAdmin 类将相应语言添加到各个字段的标签中。你将得到名为“问题 [en]”、“问题 [de]”和“问题 [fr]”的字段,而不是三个名为“问题”的字段。它故意只提供修改字段标签的功能。

from django.contrib import admin
from translated_fields import TranslatedFieldAdmin
from .models import Question

@admin.register(Question)
class QuestionAdmin(TranslatedFieldAdmin, admin.ModelAdmin):
    pass

# For inlines:
# class SomeInline(TranslatedFieldAdmin, admin.StackedInline):
#     ...

如上所述,TranslatedField 实例上的 fields 属性包含生成的字段列表。如果你想自定义 ModelAdmin 子类的各个方面,这可能很有用。以下是一个示例,展示了各种技术。

from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from translated_fields import TranslatedFieldAdmin, to_attribute
from .models import Question

@admin.register(Question)
class QuestionAdmin(TranslatedFieldAdmin, admin.ModelAdmin):
    # Pack question and answer fields into their own fieldsets:
    fieldsets = [
        (_("question"), {"fields": Question.question.fields}),
        (_("answer"), {"fields": Question.answer.fields}),
    ]

    # Show all fields in the changelist:
    list_display = [
        *Question.question.fields,
        *Question.answer.fields
    ]

    # Order by current language's question field:
    def get_ordering(self, request):
        return [to_attribute("question")]

表单

当你想要表单字段的标签包含语言代码时,django-translated-fields 提供了一个辅助工具。如果你觉得这很有用,可以这样做

from django import forms
from translated_fields.utils import language_code_formfield_callback
from .models import Question

class QuestionForm(forms.ModelForm):

    class Meta:
        model = Question
        fields = [
            *Question.question.fields,
            *Question.answer.fields
        ]
        # Supported starting with Django 4.2: (Previously it was supported
        # directly on the modelform class, but only as an implementation
        # detail https://code.djangoproject.com/ticket/26456)
        formfield_callback = language_code_formfield_callback

你也可以全局配置语言代码标签在块中显示

from translated_fields import show_language_code

def view(request):
    form = ...
    with show_language_code(True):
        return render(request, "...", {"form": form})

请注意,必须在 show_language_code 块中渲染响应。当使用 Django 的 TemplateResponse 对象时,这不会自动发生。

其他特性

不支持在查询中自动引用当前语言的字段,也不支持自动将字段添加到管理字段集等。实现这些功能的代码并不太难编写,但维护起来比较困难,这与我编写低维护软件的目标相矛盾。尽管如此,反馈和拉取请求非常欢迎!请在提交拉取请求之前在本地上运行样式检查和测试套件 - 这只需要运行 tox

项目详情


下载文件

下载适合您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源分布

django_translated_fields-0.13.0.tar.gz (8.2 kB 查看散列值)

上传时间

构建分布

django_translated_fields-0.13.0-py3-none-any.whl (9.0 kB 查看散列值)

上传时间 Python 3

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面