简单的可翻译Django字段
项目描述
django-garnett
Django Garnett是一个字段级别翻译库,允许您在Django字段中存储多语言字符串,而无需对模型进行最小更改,也不必重写您的代码。
想要一个演示吗? https://django-garnett.herokuapp.com/
总结来说,它允许您做以下事情
models.py | 您可以这样操作! |
通过将您的模型从这样...class Greeting(models.model):
text = CharField(max_length=150)
target = models.CharField()
def __str__(self):
return f"{self.greeting}, {self.target}"
更改为这样... # Import garnett
from garnett.fields import Translated
class Greeting(models.model):
# Convert greeting to a translatable field
text = Translated(CharField(max_length=150))
target = models.CharField()
def __str__(self):
return f"{self.greeting} {self.target}"
|
from garnett.context import set_field_language
greeting = Greeting(text="Hello", target="World")
with set_field_language("en"):
greeting.text = "Hello"
with set_field_language("fr"):
greeting.text = "Bonjour"
greeting.save()
greeting.refresh_from_db()
with set_field_language("en"):
print(greeting.text)
print(greeting)
# >>> "Hello"
# >>> "Hello World"
with set_field_language("fr"):
print(greeting.text)
print(greeting)
# >>> "Bonjour"
# >>> "Bonjour World!"
with set_field_language("en"):
print(greeting.text)
print(greeting)
# >>> "Hello"
# >>> "Hello World"
Greeting.objects.filter(title="Hello").exists()
# >>> True
Greeting.objects.filter(title="Bonjour").exists()
# >>> False
Greeting.objects.filter(title__fr="Bonjour").exists()
# >>> True!!
# Assuming that GARNETT_DEFAULT_TRANSLATABLE_LANGUAGE="en"
# Or a middleware has set the language context
print(greeting.text)
# >>> Hello
print(greeting)
# >>> Hello World!
|
测试于
- Django 3.1+
- Postgres,SQLite,MariaDB
- Python 3.7+
使用以下库进行了测试
优点
- 在生产中经过战斗测试 - Aristotle Metadata 开发了、支持并使用此库为2个独立的产品,服务于政府和企业客户!
- 获取模型的全部翻译只需要一个查询
- 翻译存储在模型的单个数据库字段中
- 所有翻译都像普通字段一样操作,所以
Model.field_name = "some string"
和print(Model.field_name)
与您预期的工作方式相同 - 包括一个可配置的中介件,可以根据用户的cookie、查询字符串或HTTP头设置当前语言上下文
- 与Django Rest Framework兼容良好 - 可翻译字段可以设置为字符串或JSON字典
- 与Django ORM中的
F()
和Q()
对象兼容良好 - 如果不兼容,我们可以使用语言感知的LangF()
替换方案。
缺点
- 需要修改模型,因此不能将第三方库设置为可翻译。
- 默认情况下,它不支持
queryset.values_list
或queryset.values
- 但我们提供了一个简单的Queryset Mixin,可以添加语言支持。
为什么要写一个新的Django字段翻译器?
有以下几点原因
- 大多数现有的django字段翻译库都是静态的,并为每个翻译添加单独的数据库列或额外的表。
- 其他库可能不与常见的django库兼容,如django-rest-framework。
- 我们有一个庞大的代码库,我们希望升级为多语言 - 因此我们需要一个库,可以添加而无需重写模型中字段的每个访问,并且只需要进行一些小的调整。
注意:字段语言与Django显示语言不同。Django可以设置为根据用户的浏览器翻译页面,并使用用户界面在他们首选的语言中提供。
由于设计上的原因,Garnett 不会 使用浏览器语言 - 一个法语浏览器的用户可能希望用户界面为法语,但根据需要查看英语或法语的内容。
如何安装
-
将
django-garnett
添加到您的依赖项中。例如:pip install django-garnett
-
使用
Translated
函数转换您选择的字段- 例如:
title = fields.Translated(models.CharField(*args))
- 例如:
-
将
GARNETT_TRANSLATABLE_LANGUAGES
(一个可调用对象或语言代码列表)添加到您的Django设置中。注意:目前尚无允许“用户输入任何语言”的方法。
-
将
GARNETT_DEFAULT_TRANSLATABLE_LANGUAGE
(一个可调用对象或单个语言代码)添加到您的设置中。 -
重新运行
django makemigrations
,执行数据迁移以使现有数据可翻译(参见“数据迁移”)以及django migrate
以更新任何应用程序。 -
基本就这些。
您还可以添加一些可选功能
-
(可选)添加garnett中间件以处理字段语言处理
-
您希望捕获garnett语言并使其在视图中可用的上下文变量,请使用:
garnett.middleware.TranslationContextMiddleware
-
您希望捕获garnett语言并使其在视图中可用的上下文变量,并且当用户请求无效语言时引发404,请使用:
garnett.middleware.TranslationContextNotFoundMiddleware
-
(未来添加)您希望捕获garnett语言并使其在视图中可用的上下文变量,并且当用户请求无效语言时重定向到默认语言,请使用:
garnett.middleware.TranslationContextRedirectDefaultMiddleware
-
如果您希望在会话存储中缓存当前语言,请在使用上述中间件之后使用
garnett.middleware.TranslationCacheMiddleware
(这对于下面提到的会话选择器很有用)
-
-
(可选)将
garnett
应用添加到您的INSTALLED_APPS
中,以使用garnett的模板标签。如果在此之前安装了django.contrib.admin
,它还包括Django Admin Site中的语言切换器。 -
(可选)添加模板处理器
- 安装
garnett.context_processors.languages
,这将添加garnett_languages
(可用Language
的列表)和garnett_current_language
(当前选择的语言)。
- 安装
-
(可选)添加自定义翻译回退
默认情况下,如果字段没有可用语言,Garnett将显示类似的消息
该字段没有可用的英语翻译
您可以通过创建自定义回退方法来覆盖此行为
Translated(CharField(max_length=150), fallback=my_fallback_method))
其中
my_fallback_method
接受一个包含语言代码和对应字符串的字典,并返回必要的文本。此外,您可以通过创建一个新的
TranslationStr
类并覆盖__html__
方法 来自定义 django 在模板中输出文本的方式。
数据迁移
如果您有很多现有的数据(如果您使用这个库,您可能就是这样),您将需要执行数据迁移以确保所有现有的数据都是多语言感知的。幸运的是,我们增加了一些经过良好测试的迁移工具,可以帮您处理这些。
运行 django-admin makemigrations
后,您只需在模式迁移之前添加 step_1_safe_encode_content
,在之后添加 step_2_safe_prepare_translations
,如下例所示:
# Generated by Django 3.1.13 on 2022-01-11 10:13
from django.db import migrations, models
import garnett.fields
import library_app.models
#### Add this line in
from garnett.migrate import step_1_safe_encode_content, step_2_safe_prepare_translations
#### Define the models and fields you want ot migrate
model_fields = {
"book": ["title", "description"],
}
class Migration(migrations.Migration):
dependencies = [
("library_app", "0001_initial"),
]
operations = [
## Add this operation at the start
step_1_safe_encode_content("library_app", model_fields),
## These are the automatically generated migrations
migrations.AlterField( # ... migrate title to TranslatedField),
migrations.AlterField( # ... migrate description to TranslatedField),
## Add this operation at the start
step_2_safe_prepare_translations("library_app", model_fields),
]
Language
与语言
Django Garnett 使用 python 的 langcodes
库来确定正在使用的语言的相关信息,包括正在使用的语言的完整名称和本地名称。这些信息存储为 Language
对象。
Django 设置选项
GARNETT_DEFAULT_TRANSLATABLE_LANGUAGE
- 存储用于读取和写入字段时的默认语言,如果上下文管理器或请求中没有设置语言。
- 默认情况下为 'en-AU',这是 'Strayan'(或更常见地称为澳大利亚)的本土语言的 语言代码。
- 这也可以是一个返回语言代码列表的可调用对象。结合存储用户设置在类似(django-solo)[https://github.com/lazybird/django-solo] 的东西中,用户可以动态添加或更改他们的语言设置。
- 默认:
'en-AU'
GARNETT_TRANSLATABLE_LANGUAGES
:- 存储用户可以使用以保存到 TranslatableFields 的 语言代码 列表。
- 这也可以是一个返回语言代码列表的可调用对象。结合存储用户设置在类似(django-solo)[https://github.com/lazybird/django-solo] 的东西中,用户可以动态添加或更改他们的语言设置。
- 默认
[GARNETT_DEFAULT_TRANSLATABLE_LANGUAGE]
GARNETT_REQUEST_LANGUAGE_SELECTORS
:- 一个字符串模块列表,它决定了用于确定用户选择的语言的选项的顺序。如果找到第一个选择器,则使用该选择器用于请求的语言;如果没有找到,则使用 DEFAULT_LANGUAGE。这些可以是以下任意顺序的以下内容
garnett.selector.query
:检查GARNETT_QUERY_PARAMETER_NAME
以查找要显示的语言garnett.selector.cookie
:检查名为GARNETT_LANGUAGE_CODE
的cookie以查找要显示的语言。注意:您不能更改此cookie名称。garnett.selector.session
:检查名为GARNETT_LANGUAGE_CODE
的会话密钥以查找要显示的语言。注意:您不能更改此密钥名称。garnett.selector.header
:检查名为X-Garnett-Language-Code
的HTTP头以查找要显示的语言。注意:您不能更改此头名称。garnett.selector.browser
:使用 Django 的get_language
函数获取用户浏览器/UI语言 由 Django 确定。
- 例如,如果您只想按顺序检查头和cookie,则将其设置为
['garnett.selectors.header', 'garnett.selectors.cookie']
。 - 默认:
['garnett.selectors.header', 'garnett.selectors.query', 'garnett.selectors.cookie']
- 一个字符串模块列表,它决定了用于确定用户选择的语言的选项的顺序。如果找到第一个选择器,则使用该选择器用于请求的语言;如果没有找到,则使用 DEFAULT_LANGUAGE。这些可以是以下任意顺序的以下内容
GARNETT_QUERY_PARAMETER_NAME
:- 用于确定用户在 HTTP 请求期间请求的语言的查询参数。
- 默认:
glang
GARNETT_ALLOW_BLANK_FALLBACK_OVERRIDE
- 如果设置为 true,当应用当前语言中间件时,这将检查额外的 GET URL 参数以覆盖回退,以在没有内容的情况下返回空白字符串。这对于 API 很有用。
- 默认:False
高级设置(您可能不需要调整这些)
GARNETT_TRANSLATABLE_FIELDS_PROPERTY_NAME
:- Garnett 为所有模型添加一个属性,该属性返回所有 TranslatableFields 的列表。默认情况下,这是 'translatable_fields',但您可以根据需要在此处自定义。
- 默认:
translatable_fields
GARNETT_TRANSLATIONS_PROPERTY_NAME
:- Garnett 为所有模型添加了一个属性,该属性返回所有可翻译字段的全部翻译的字典。默认情况下,这是 'translations',但您可以在此处自定义它。
- 默认值:
translations
使用 Garnett
如果您正确完成了上述所有操作,Garnett 应该大部分“直接工作”。
切换活动语言
Garnett 随附一个方便的上下文管理器,可用于指定当前语言。在您想要手动控制当前语言的任何位置,将您的代码包裹在 set_field_language
中,Garnett 将正确存储语言。这可以嵌套,或者您可以在保存之前多次更改上下文的语言。
from garnett.context import set_field_language
greeting = Greeting(text="Hello", target="World")
with set_field_language("en"):
greeting.text = "Hello"
with set_field_language("fr"):
greeting.text = "Bonjour"
greeting.save()
使用 Garnett 与 values_list
或 values
这是 garnett 不 立即工作的领域之一,但有一个解决方案。
在您使用值列表或 values
的地方,将任何翻译字段包裹在一个 L 表达式中,值列表将返回正确的结果。例如
from garnett.expressions import L
Book.objects.values_list(L("title"))
Book.objects.values(L("title"))
使用 Garnett 与 Django-Rest-Framework
由于 TranslationField
基于JSONField,默认情况下 Django-Rest-Framework 将这些字段渲染为JSONField,这可能不是理想的。
您可以通过将 TranslatableSerializerMixin
作为第一个 mixin 使用来解决这个问题,这将在您的序列化器中添加必要的钩子。这将意味着类更改,但您不需要更新或覆盖每个字段。
例如
from rest_framework import serializers
from library_app import models
from garnett.ext.drf import TranslatableSerializerMixin
class BookSerializer(TranslatableSerializerMixin, serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
这将允许您将可翻译字段的值设置为活动语言字符串,或者设置一个包含所有要保存的语言的字典(注意:这将覆盖现有的语言设置)。例如
仅覆盖活动语言
curl -X PATCH ... -d "{ \"title\": \"Hello\"}"
特定地覆盖单个语言(例如,克林贡语)
curl -X PATCH ... -H "X-Garnett-Language-Code: tlh" -d "{ \"title\": \"Hello\"}"
覆盖所有语言
curl -X PATCH ... -d "{ \"title\": {\"en\": \"Hello\", \"fr\": \"Bonjour\"}}"
使用 Garnett 与 django-reversion 和 django-reversion-compare
为了使 Garnett 正确运行,您需要对 django-reversion 和 django-reversion-compare 进行一些小的调整,这取决于它们如何序列化和显示数据。
这是因为 Garnett 不使用相同的 'field.attname' 和 'field.name',这意味着 Django 的序列化将无法正确工作。
要使 django-reversion 工作,您需要使用一个翻译感知序列化器,并应用一个补丁以确保 django-reversion-compare 可以显示正确的信息。
Garnett 随附了一个示例 json 翻译感知序列化器,您可以通过以下两个设置在 settings.py
中应用它
# In settings.py
GARNETT_PATCH_REVERSION_COMPARE = True
SERIALIZATION_MODULES = {"json": "garnett.serializers.json"}
翻译字段将列出 json 中的历史和更改,但它确实可以正确地进行比较。
为什么叫 Garnett?
- 库需要一个好名字。
- 搜索“著名翻译家”将告诉您关于 Constance Garnett 的信息。
- 搜索“Django Garnett”显示没有名为此的 Python 库。
- 然而,它确实提到了 Garnet Clark(也拼作 Garnett),一位与 Django Reinhart(Django Web 框架的命名来源)一起演奏爵士钢琴的钢琴家。
- Voila - 一个好名字
警告
contains
在 SQLite 中表现得像icontains
,仅当执行包含查询时才进行不区分大小写的搜索。我不知道为什么 - https://www.youtube.com/watch?v=PgGNWRtceag- 由于 django 设置管理表单字段的方式,默认情况下您不会在 django 管理站点上的翻译字段中看到特定的管理小部件,如
AdminTextAreaWidget
。但是,您可以在相应的管理模型表单中明确指定它们。
想要帮助维护这个库吗?
有一个 /dev/
目录,其中包含一个 docker-compose stack,您可以用来启动数据库和干净的开发环境。
想要其他选项?
为Django添加可翻译字符串有几种好的选择,可能也符合其他用例。这里我们包括了一些其他选项,以及它们的优势和为什么我们没有选择它们。
-
优点:这个库允许您在不修改模型的情况下将翻译应用于外部应用。
缺点:每个翻译都会增加一个额外的列,这意味着语言是在代码中指定的,用户以后无法更改。
-
优点:使用了一个很棒的上下文处理器来切换语言(这也是我们获得灵感的来源)。
缺点:语言在django设置的指定中,用户以后无法更改。
-
优点:支持Django管理站点。
缺点:语言存储在单独的表中,用户以后无法更改。翻译字段在模型元数据中指定,远离字段定义,这使得复杂查询更困难。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪一个,请了解更多关于安装包的信息。
源分发
构建分发
django_garnett-0.5.2.tar.gz 的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 8c1ca52e1794f3ee3dbe55373114bd658ee8bdc05db4b58733ab1bc4932495a1 |
|
MD5 | 6b8df12d17fa5f913adc0ee0f2d6dae1 |
|
BLAKE2b-256 | 82f13834eda18055e754719f270d18fc358676c6a5b3849bb64fe5c7339d0dc1 |
django_garnett-0.5.2-py3-none-any.whl 的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 571060dd123450c66f76a35ed56f6d896122a9906c69101a14776445b25b8428 |
|
MD5 | f07418845fe4e34f1844508c7d2c2505 |
|
BLAKE2b-256 | bf3b92e8397154942c1c1b6ee8635fc793513e09043836b2826895c07d1d7339 |