2009-03-05 13 views
2

Disons que je le modèle Django suivant:étiquettes Django et traductions - Modèle de conception

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

Chaque étiquette a un numéro d'identification, le texte de l'étiquette, et une abréviation. Maintenant, je veux que ces étiquettes soient traduisibles dans d'autres langues. Quelle est la meilleure façon de procéder?

Comme je le vois, j'ai quelques options:

1: Ajouter les traductions en tant que champs du modèle:

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) 

Ce n'est évidemment pas idéal - l'ajout de langues nécessite la modification du modèle, le nom de champ correct dépend de la langue.

2: Ajouter la langue comme une clé étrangère:

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') 

C'est beaucoup mieux, maintenant, je peux demander toutes les étiquettes avec une certaine langue, et de les jeter dans un dict:

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

Mais le problème est ici que les étiquettes dict est censé être une table de consultation, comme ceci:

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

qui doe sn't fonctionne s'il y a une ligne par étiquette, éventuellement avec plusieurs langues pour une seule étiquette. Pour résoudre celui-là, je besoin d'un autre domaine:

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: Throw texte ImprimEtiq dans un nouveau modèle:

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) 

Mais cela ne fonctionne pas, et provoque une base de données a frappé à chaque fois Je fais référence au texte de l'étiquette:

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

J'ai 2 option existe, mais je trouve très laid - je n'aime pas le champ group_id, et je ne peux pas penser à quelque chose de mieux pour le nommer. En outre, StandardLabel comme je l'utilise est un modèle abstrait, que je sous-classe pour obtenir différents ensembles d'étiquettes pour différents champs.

Je suppose que si l'option 3/n'a pas/touché la base de données, c'est ce que je choisirais. Je crois que le vrai problème est que le filtre text__language=1 ne cache pas les instances LabelText, et donc la DB est frappé quand je text.get(language=1)

Que pensez-vous de cela? Quelqu'un peut-il recommander une solution plus propre?

Édition: Pour que ce soit clair, ce ne sont pas des étiquettes de formulaire, donc le système d'internationalisation de Django n'aide pas.

Répondre

1

Je préférerais de beaucoup ajouter un champ par langue qu'une nouvelle instance de modèle par langue. Cela nécessite une modification du schéma lorsque vous ajoutez une nouvelle langue, mais ce n'est pas difficile, et à quelle fréquence pensez-vous ajouter des langues? En attendant, cela vous donnera de meilleures performances de base de données (pas de jointures ou d'index ajoutés) et vous n'aurez pas à améliorer votre logique de requête avec des trucs de traduction; Gardez tout dans les modèles où il appartient.

Encore mieux, utilisez une application réutilisable comme django-transmeta ou django-modeltranslation qui rend ce stupide simple et presque complètement transparent.

+0

Merci beaucoup! Ces deux applications semblent très prometteuses - maintenant je dois juste décider ce que je veux utiliser :) –

+0

Oui, c'est un appel difficile. Personnellement, je préfère certaines choses sur le django-transmeta (comme la façon dont il se débarrasse du champ d'origine pour ne pas avoir besoin de logique complexe sur ce qu'il contient), mais j'aime que la modélisation django me permette de traduire des modèles sans toucher à leur code. –

2

Je garderais les choses aussi simples que possible.La recherche sera plus rapide et le nettoyeur de code avec quelque chose comme ceci:

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') 

basé Scrutez ensuite sur l'abréviation et la langue:

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

Je préfèrerais charger toutes les étiquettes en mémoire une fois pour ne frapper qu'une fois la DB. J'ai plusieurs objets avec des numéros d'identification qui référenceront ces étiquettes. J'utiliserais une clé étrangère pour les étiquettes si je le pouvais, mais alors ça se verrouille dans une langue à moins que je parte avec l'option 3, qui frappe toujours la base de données. –

+0

Je pense que vous pensez trop. Si vous devez mettre en cache des données, faites-le simplement: 'StandardLabel.objects.all(). Values ​​()'. Si vous êtes préoccupé par d'autres requêtes de base de données, utilisez le paramètre 'select_related'. –

+0

+1. Simple devrait être le chemin à parcourir. La traduction est déjà une tâche compliquée. – muhuk

3

Une autre option que vous pourriez envisager, en fonction de votre conception d'applications bien sûr, est d'utiliser les fonctionnalités d'internationalisation de Django. L'approche qu'ils utilisent est assez commune à l'approche trouvée dans les logiciels de bureau.

Je vois que la question a été modifiée pour ajouter une référence à l'internationalisation de Django, donc vous le savez, mais les fonctionnalités intl dans Django s'appliquent à bien plus qu'à Forms; cela touche beaucoup et n'a besoin que de quelques modifications pour la conception de votre application.

Leurs documents sont ici: http://docs.djangoproject.com/en/dev/topics/i18n/#topics-i18n

L'idée est que vous définissez votre modèle comme s'il n'y avait qu'une seule langue. En d'autres termes, ne faites aucune référence à la langue, et mettez seulement, disons, l'anglais dans le modèle.

Alors:

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

Je sais que cela semble que vous avez Bouleversée la question de la langue, mais vous avez fait juste déménagé il. Au lieu que le langage se trouve dans votre modèle de données, vous l'avez poussé vers la vue.

Les fonctions d'internationalisation de django vous permettent de générer des fichiers de traduction de texte et offrent un certain nombre de fonctionnalités pour extraire le texte du système dans des fichiers. C'est en fait très utile car cela vous permet d'envoyer des fichiers simples à votre traducteur, ce qui facilite leur travail. Ajouter une nouvelle langue est aussi simple que de traduire le fichier dans une nouvelle langue.

Les fichiers de traduction définissent l'étiquette de la base de données et une traduction pour cette langue. Il existe des fonctions permettant de gérer dynamiquement la traduction de la langue au moment de l'exécution pour les modèles, les vues d'administration, le javascript et les modèles.

Par exemple, dans un modèle, vous pourriez faire quelque chose comme:

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

Ou dans le code de vue, vous pouvez faire:

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

Bien sûr, si votre application est tout au sujet d'entrer nouvelles langues on the fly, alors cette approche peut ne pas fonctionner pour vous. Jetez un coup d'œil au projet d'internationalisation, car vous pourrez peut-être l'utiliser «tel quel» ou vous inspirer d'une solution appropriée à django qui fonctionne pour votre domaine.

+0

J'ai regardé ceci, mais malheureusement ce n'est pas exactement ce que je veux faire. Je préférerais vraiment que les traductions se trouvent dans la base de données, pour diverses raisons. Merci pour votre aide de toute façon! –

+1

-1 Sur tout site Web raisonnablement dynamique, vous ne pouvez pas utiliser gettext pour traduire le contenu d'une base de données; ça change beaucoup trop souvent. –

0

Bien que j'irais avec Daniel's solution, voici une alternative de ce que j'ai compris de vos commentaires:

Vous pouvez utiliser un XMLField ou JSONField pour stocker vos paires langue/traduction. Cela permettrait à vos objets référençant vos étiquettes d'utiliser un seul id pour toutes les traductions.Et alors vous pouvez avoir une méthode de gestion personnalisée pour appeler une traduction spécifique:

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

Ou une solution un peu plus propre et un peu plus complexe qui joue bien avec admin serait de dénormaliser le XMLField à un autre modèle avec de nombreux-to une relation au modèle Label. Même API, mais au lieu d'analyser XML, il pourrait interroger des modèles apparentés.

Pour les deux suggestions, il y a un objet unique sur lequel les utilisateurs d'une étiquette pointe.

Je ne m'inquiéterais pas trop des requêtes, Django met en cache des requêtes et votre SGBD aurait probablement aussi une mise en cache supérieure.

+0

Ceci est une solution décente, mais je voudrais un intégré dans l'interface utilisateur d'administration (je sais, je suis gâté :)). –