【问题标题】:Django labels and translations - Model DesignDjango 标签和翻译 - 模型设计
【发布时间】:2010-10-11 14:22:42
【问题描述】:

假设我有以下 Django 模型:

class StandardLabel(models.Model):
    id = models.AutoField(primary_key=True)
    label = models.CharField(max_length=255)
    abbreviation = models.CharField(max_length=255)

每个标签都有一个 ID 号、标签文本和缩写。现在,我想让这些标签可以翻译成其他语言。最好的方法是什么?

在我看来,我有几个选择:

1:将翻译作为字段添加到模型中:

class StandardLabel(models.Model):
    id = models.AutoField(primary_key=True)
    label_english = models.CharField(max_length=255)
    abbreviation_english = models.CharField(max_length=255)
    label_spanish = models.CharField(max_length=255)
    abbreviation_spanish = models.CharField(max_length=255)

这显然不太理想 - 添加语言需要编辑模型,正确的字段名称取决于语言。

2:添加语言作为外键:

class StandardLabel(models.Model):
    id = models.AutoField(primary_key=True)
    label = models.CharField(max_length=255)
    abbreviation = models.CharField(max_length=255)
    language = models.ForeignKey('languages.Language')

这好多了,现在我可以要求所有具有某种语言的标签,并将它们放入字典中:

labels = StandardLabel.objects.filter(language=1)
labels = dict((x.pk, x) for x in labels)

但这里的问题是标签 dict 是一个查找表,如下所示:

x = OtherObjectWithAReferenceToTheseLabels.object.get(pk=3)
thelabel = labels[x.labelIdNumber].label

如果每个标签有一行,这不起作用,可能单个标签有多种语言。为了解决这个问题,我需要另一个字段:

class StandardLabel(models.Model):
    id = models.AutoField(primary_key=True)
    group_id = models.IntegerField(db_index=True)
    label = models.CharField(max_length=255)
    abbreviation = models.CharField(max_length=255)
    language = models.ForeignKey('languages.Language')
    class Meta:
        unique_together=(("group_id", "language"),)
#and I need to group them differently:
labels = StandardLabel.objects.filter(language=1)
labels = dict((x.group_id, x) for x in labels)

3:将标签文本扔到新模型中:

class StandardLabel(models.Model):
    id = models.AutoField(primary_key=True)
    text = models.ManyToManyField('LabelText')

class LabelText(models.Model):
    id = models.AutoField(primary_key=True)
    label = models.CharField(max_length=255)
    abbreviation = models.CharField(max_length=255)
    language = models.ForeignKey('languages.Language')

labels = StandardLabel.objects.filter(text__language=1)
labels = dict((x.pk, x) for x in labels)

但是这不起作用,并且每次我引用标签的文本时都会导致数据库命中:

x = OtherObjectWithAReferenceToTheseLabels.object.get(pk=3)
thelabel = labels[x.labelIdNumber].text.get(language=1)

我已经实现了选项 2,但我觉得它很丑 - 我不喜欢 group_id 字段,而且我想不出更好的名字来命名它。此外,我使用的 StandardLabel 是一个抽象模型,我将其子类化以获得不同字段的不同标签集。

我想如果选项 3 /没有/命中数据库,这就是我会选择的。我相信真正的问题是过滤器text__language=1 没有缓存LabelText 实例,所以当我text.get(language=1) 时数据库被命中

您对此有何看法?谁能推荐一个更清洁的解决方案?

编辑:为了清楚起见,这些不是表单标签,所以 Django 国际化系统没有帮助。

【问题讨论】:

    标签: python django django-models localization


    【解决方案1】:

    根据您的应用程序设计,您可能会考虑的另一个选择当然是利用 Django 的国际化功能。他们使用的方法与桌面软件中的方法很常见。

    我看到该问题已被编辑以添加对 Django 国际化的引用,所以您确实知道,但 Django 中的 intl 功能不仅仅适用于表单;它涉及很多内容,只需要对您的应用设计进行一些调整。

    他们的文档在这里:http://docs.djangoproject.com/en/dev/topics/i18n/#topics-i18n

    这个想法是你定义你的模型,就好像只有一种语言一样。换句话说,根本不提及语言,并且只在模型中放置例如英语。

    所以:

    class StandardLabel(models.Model):
        abbreviation = models.CharField(max_length=255)
        label = models.CharField(max_length=255)
    

    我知道这看起来您已经完全排除了语言问题,但实际上您只是重新定位了它。您已将语言推送到视图中,而不是数据模型中的语言。

    django 国际化功能允许您生成文本翻译文件,并提供了许多将文本从系统中提取到文件中的功能。这实际上非常有用,因为它允许您将纯文件发送给您的翻译,这使他们的工作更容易。添加新语言就像将文件翻译成新语言一样简单。

    翻译文件定义数据库中的标签,以及该语言的翻译。有一些函数可以在运行时动态处理模型、管理视图、javascript 和模板的语言翻译。

    例如,在模板中,您可能会执行以下操作:

    <b>Hello {% trans "Here's the string in english" %}</b>
    

    或者在查看代码中,你可以这样做:

    # See docs on setting language, or getting Django to auto-set language
    s = StandardLabel.objects.get(id=1)
    lang_specific_label = ugettext(s.label)
    

    当然,如果您的应用完全是为了输入新语言on the fly,那么这种方法可能不适合您。不过,请查看 Internationalization 项目,因为您可以“按原样”使用它,或者受到启发,找到适合您的域的 django 解决方案。

    【讨论】:

    • 我看过这个,但不幸的是这不是我想要做的。出于各种原因,我真的更愿意将翻译保存在数据库中。无论如何感谢您的帮助!
    • -1 在任何合理动态的网站上,您都不能使用 gettext 来翻译数据库内容;它变化太频繁了。
    【解决方案2】:

    我会让事情尽可能简单。查找会更快,代码更清晰,如下所示:

    class StandardLabel(models.Model):
        abbreviation = models.CharField(max_length=255)
        label = models.CharField(max_length=255)
        language = models.CharField(max_length=2)
        # or, alternately, specify language as a foreign key:
        #language = models.ForeignKey(Language)
    
        class Meta:
            unique_together = ('language', 'abbreviation')
    

    然后根据缩写和语言查询:

    l = StandardLabel.objects.get(language='en', abbreviation='suite')
    

    【讨论】:

    • 我宁愿将所有标签加载到内存中一次,而只访问数据库一次。我有几个带有 ID 号的对象,它们将引用这些标签。如果可以的话,我会使用外键来标记,但是除非我选择选项 3,否则它会锁定一种语言,它仍然会命中数据库。
    • 我觉得你想多了。如果你需要缓存数据,那就去做吧:StandardLabel.objects.all().values()。如果您担心额外的数据库查询,请使用select_related 参数。
    • +1。简单应该是要走的路。翻译已经是一项复杂的任务。
    • 关于该缓存的好建议,但我的印象是获取 .all() 然后过滤 (.get(pk=4)) 不会将缓存重用于所有类型的过滤.
    • .values() 返回一个类似字典的对象。然后,您将使用普通的 python dict 表示法,即mydict['mykey'] 来访问这些值。
    【解决方案3】:

    我更喜欢为每种语言添加一个字段,而不是为每种语言添加一个新模型实例。添加新语言时确实需要更改架构,但这并不难,您希望多久添加一次语言?同时,它会为您提供更好的数据库性能(不添加连接或索引),并且您不必用翻译的东西弄乱您的查询逻辑;将其全部保存在它所属的模板中。

    更好的是,使用像django-transmeta 或django-modeltranslation 这样的可重复使用的应用程序,让这个愚蠢的操作变得简单且几乎完全透明。

    【讨论】:

    • 非常感谢!这两个应用看起来很有前途 - 现在我只需要决定我要使用哪个 :)
    • 是的,这是一个艰难的决定。就我个人而言,我更喜欢 django-transmeta 的一些内容(比如它如何摆脱原始字段,因此您不需要关于其中内容的复杂逻辑),但我喜欢 django-modeltranslation 允许我在不接触其代码的情况下翻译模型。
    【解决方案4】:

    虽然我会选择Daniel's solution,但这是我从您的 cmets 中了解到的另一种选择:

    您可以使用XMLField 或 JSONField 来存储您的语言/翻译对。这将允许引用标签的对象对所有翻译使用单个 id。然后你可以有一个自定义的管理器方法来调用特定的翻译:

    Label.objects.get_by_language('ru', **kwargs)
    

    或者与admin 配合良好的稍微更简洁和稍微复杂的解决方案是将XMLField 非规范化为与Label 模型具有多对一关系的另一个模型。相同的 API,但它可以查询相关模型,而不是解析 XML。

    对于这两个建议,标签的用户将指向一个对象。

    我不会太担心查询,Django 会缓存查询,您的 DBMS 可能也会在那里拥有出色的缓存。

    【讨论】:

    • 这是一个不错的解决方案,但我想要一个集成到管理 UI 中的解决方案(我知道,我被宠坏了 :))。
    猜你喜欢
    • 2012-10-30
    • 1970-01-01
    • 2014-07-05
    • 2013-09-19
    • 2017-12-03
    • 1970-01-01
    • 2023-01-12
    • 1970-01-01
    • 2015-06-03
    相关资源
    最近更新 更多