Djangoのラベルと翻訳-モデルデザイン
-
03-07-2019 - |
質問
次の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
1つのラベルに複数の言語がある場合、ラベルごとに行がある場合は機能しません。それを解決するには、別のフィールドが必要です:
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国際化システムは役に立ちません。
解決
言語ごとの新しいモデルインスタンスよりも、言語ごとにフィールドを追加したいです。新しい言語を追加するときにスキーマを変更する必要がありますが、それは難しくありません。どのくらいの頻度で言語を追加する予定ですか?それまでの間は、データベースのパフォーマンスが向上し(結合やインデックスが追加されない)、翻訳ロジックでクエリロジックを台無しにする必要がありません。それが属するテンプレートにすべて保存してください。
さらに良いのは、 django-transmeta や django-modeltranslation を使用すると、この愚かさはシンプルでほぼ完全に透明になります。
他のヒント
当然のことながら、アプリケーションの設計に応じて検討する可能性のあるもう1つのオプションは、Djangoの国際化機能を利用することです。彼らが使用するアプローチは、デスクトップソフトウェアに見られるアプローチに非常に一般的です。
質問はDjango国際化への参照を追加するために編集されたので、あなたはそれを知っていますが、Djangoのintl機能はForms以外にも多くのものに適用されます。非常に多くの操作が行われ、アプリの設計を少し調整するだけで済みます。
それらのドキュメントはこちらです: http://docs.djangoproject .com / en / dev / topics / i18n /#topics-i18n
アイデアは、1つの言語のみが存在するかのようにモデルを定義することです。つまり、言語をまったく参照せず、たとえば英語のみをモデルに入れます。
だから:
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)
もちろん、アプリが新しい言語の入力のみをその場で
する場合、このアプローチはうまくいかないかもしれません。それでも、「現状のまま」使用できるか、ドメインで機能するdjangoに適したソリューションに触発される可能性があるため、国際化プロジェクトをご覧ください。
できるだけシンプルにするようにします。ルックアップはより高速になり、コードは次のようになります。
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')
ダニエルのソリューションはこちらあなたのコメントから私が理解したものの代替です:
XMLField またはJSONFieldを使用して、言語/翻訳ペアを保存します。これにより、ラベルを参照するオブジェクトは、すべての翻訳に単一の id
を使用できます。そして、特定の翻訳を呼び出すカスタムマネージャーメソッドを持つことができます:
Label.objects.get_by_language('ru', **kwargs)
または admin
でうまく機能する、よりクリーンで少し複雑なソリューションは、 Label
との多対1の関係で、XMLFieldを別のモデルに非正規化することです。モデル。同じAPIですが、XMLを解析する代わりに、関連するモデルを照会できます。
どちらの提案にも、ラベルのユーザーが指す単一のオブジェクトがあります。
クエリについてあまり心配する必要はありません。Djangoはクエリをキャッシュするため、DBMSでも優れたキャッシュを使用できます。