跳转到主要内容

简单的可翻译Django字段

项目描述

django-garnett

Django Garnett是一个字段级别翻译库,允许您在Django字段中存储多语言字符串,而无需对模型进行最小更改,也不必重写您的代码。

想要一个演示吗? https://django-garnett.herokuapp.com/

Aristotle Metadata

总结来说,它允许您做以下事情

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_listqueryset.values - 但我们提供了一个简单的Queryset Mixin,可以添加语言支持。

为什么要写一个新的Django字段翻译器?

有以下几点原因

  • 大多数现有的django字段翻译库都是静态的,并为每个翻译添加单独的数据库列或额外的表。
  • 其他库可能不与常见的django库兼容,如django-rest-framework。
  • 我们有一个庞大的代码库,我们希望升级为多语言 - 因此我们需要一个库,可以添加而无需重写模型中字段的每个访问,并且只需要进行一些小的调整。

注意:字段语言与Django显示语言不同。Django可以设置为根据用户的浏览器翻译页面,并使用用户界面在他们首选的语言中提供。

由于设计上的原因,Garnett 不会 使用浏览器语言 - 一个法语浏览器的用户可能希望用户界面为法语,但根据需要查看英语或法语的内容。

如何安装

  1. django-garnett添加到您的依赖项中。例如:pip install django-garnett

  2. 使用Translated函数转换您选择的字段

    • 例如:title = fields.Translated(models.CharField(*args))
  3. GARNETT_TRANSLATABLE_LANGUAGES(一个可调用对象或语言代码列表)添加到您的Django设置中。

    注意:目前尚无允许“用户输入任何语言”的方法。

  4. GARNETT_DEFAULT_TRANSLATABLE_LANGUAGE(一个可调用对象或单个语言代码)添加到您的设置中。

  5. 重新运行django makemigrations,执行数据迁移以使现有数据可翻译(参见“数据迁移”)以及django migrate以更新任何应用程序。

  6. 基本就这些。

您还可以添加一些可选功能

  1. (可选)添加garnett中间件以处理字段语言处理

    • 您希望捕获garnett语言并使其在视图中可用的上下文变量,请使用:garnett.middleware.TranslationContextMiddleware

    • 您希望捕获garnett语言并使其在视图中可用的上下文变量,并且当用户请求无效语言时引发404,请使用:garnett.middleware.TranslationContextNotFoundMiddleware

    • (未来添加)您希望捕获garnett语言并使其在视图中可用的上下文变量,并且当用户请求无效语言时重定向到默认语言,请使用:garnett.middleware.TranslationContextRedirectDefaultMiddleware

    • 如果您希望在会话存储中缓存当前语言,请在使用上述中间件之后使用garnett.middleware.TranslationCacheMiddleware(这对于下面提到的会话选择器很有用)

  2. (可选)将garnett应用添加到您的INSTALLED_APPS中,以使用garnett的模板标签。如果在此之前安装了django.contrib.admin,它还包括Django Admin Site中的语言切换器。

  3. (可选)添加模板处理器

    • 安装garnett.context_processors.languages,这将添加garnett_languages(可用Language的列表)和garnett_current_language(当前选择的语言)。
  4. (可选)添加自定义翻译回退

    默认情况下,如果字段没有可用语言,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']
  • 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_listvalues

这是 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-modeltranslation

    优点:这个库允许您在不修改模型的情况下将翻译应用于外部应用。

    缺点:每个翻译都会增加一个额外的列,这意味着语言是在代码中指定的,用户以后无法更改。

  • django-translated-fields

    优点:使用了一个很棒的上下文处理器来切换语言(这也是我们获得灵感的来源)。

    缺点:语言在django设置的指定中,用户以后无法更改。

  • django-parler

    优点:支持Django管理站点。

    缺点:语言存储在单独的表中,用户以后无法更改。翻译字段在模型元数据中指定,远离字段定义,这使得复杂查询更困难。

项目详情


下载文件

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

源分发

django_garnett-0.5.2.tar.gz (27.0 kB 查看散列)

上传

构建分发

django_garnett-0.5.2-py3-none-any.whl (27.4 kB 查看散列)

上传 Python 3

支持者:

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