Je développe un logiciel multilingue. En ce qui concerne le code d'application, la localisation n'est pas un problème. Nous pouvons utiliser des ressources spécifiques aux langues et disposer de toutes sortes d'outils qui fonctionnent bien avec elles.
Mais quelle est la meilleure approche pour définir un schéma de base de données multilingue? Disons que nous avons beaucoup de tables (100 ou plus), et chaque table peut avoir plusieurs colonnes qui peuvent être localisées (la plupart des colonnes nvarchar doivent être localisables). Par exemple, l'un des tableaux peut contenir des informations sur le produit:
CREATE TABLE T_PRODUCT (
NAME NVARCHAR(50),
DESCRIPTION NTEXT,
PRICE NUMBER(18, 2)
)
Je peux penser à trois approches pour prendre en charge le texte multilingue dans les colonnes NOM et DESCRIPTION:
Colonne séparée pour chaque langue
Lorsque nous ajoutons une nouvelle langue au système, nous devons créer des colonnes supplémentaires pour stocker le texte traduit, comme ceci:
CREATE TABLE T_PRODUCT ( NAME_EN NVARCHAR(50), NAME_DE NVARCHAR(50), NAME_SP NVARCHAR(50), DESCRIPTION_EN NTEXT, DESCRIPTION_DE NTEXT, DESCRIPTION_SP NTEXT, PRICE NUMBER(18,2) )
Tableau de traduction avec colonnes pour chaque langue
Au lieu de stocker du texte traduit, seule une clé étrangère de la table des traductions est stockée. Le tableau des traductions contient une colonne pour chaque langue.
CREATE TABLE T_PRODUCT ( NAME_FK int, DESCRIPTION_FK int, PRICE NUMBER(18, 2) ) CREATE TABLE T_TRANSLATION ( TRANSLATION_ID, TEXT_EN NTEXT, TEXT_DE NTEXT, TEXT_SP NTEXT )
Tableaux de traduction avec des lignes pour chaque langue
Au lieu de stocker du texte traduit, seule une clé étrangère de la table des traductions est stockée. La table des traductions contient uniquement une clé et une table distincte contient une ligne pour chaque traduction dans une langue.
CREATE TABLE T_PRODUCT ( NAME_FK int, DESCRIPTION_FK int, PRICE NUMBER(18, 2) ) CREATE TABLE T_TRANSLATION ( TRANSLATION_ID ) CREATE TABLE T_TRANSLATION_ENTRY ( TRANSLATION_FK, LANGUAGE_FK, TRANSLATED_TEXT NTEXT ) CREATE TABLE T_TRANSLATION_LANGUAGE ( LANGUAGE_ID, LANGUAGE_CODE CHAR(2) )
Il y a des avantages et des inconvénients à chaque solution, et je voudrais savoir quelles sont vos expériences avec ces approches, que recommandez-vous et comment allez-vous concevoir un schéma de base de données multilingue.
LANGUAGE_CODE
sont la clé naturelle, à éviterLANGUAGE_ID
.Réponses:
Que pensez-vous d'avoir une table de traduction associée pour chaque table traduisible?
De cette façon, si vous avez plusieurs colonnes traduisibles, il ne faudrait qu'une seule jointure pour l'obtenir + puisque vous ne générez pas automatiquement une traduction, il peut être plus facile d'importer des éléments avec leurs traductions associées.
Le côté négatif de ceci est que si vous avez un mécanisme de secours de langage complexe, vous devrez peut-être l'implémenter pour chaque table de traduction - si vous comptez sur une procédure stockée pour le faire. Si vous le faites depuis l'application, cela ne sera probablement pas un problème.
Dites-moi ce que vous en pensez - je suis également sur le point de prendre une décision à ce sujet pour notre prochaine candidature. Jusqu'à présent, nous avons utilisé votre 3ème type.
la source
T_PRODUCT
a 1 million de lignes,T_PRODUCT_tr
aurait 2 millions. Cela réduirait-il beaucoup l'efficacité sql?C'est un problème intéressant, alors nécromancions.
Commençons par les problèmes de la méthode 1:
Problème: vous dénormalisez pour gagner en vitesse.
En SQL (sauf PostGreSQL avec hstore), vous ne pouvez pas passer un langage de paramètres et dire:
Vous devez donc faire ceci:
Ce qui signifie que vous devez modifier TOUTES vos requêtes si vous ajoutez une nouvelle langue. Cela conduit naturellement à utiliser du "SQL dynamique", vous n'avez donc pas à modifier toutes vos requêtes.
Cela se traduit généralement par quelque chose comme ça (et il ne peut pas être utilisé dans les vues ou les fonctions table par ailleurs, ce qui est vraiment un problème si vous avez réellement besoin de filtrer la date de rapport)
Le problème avec ceci est
a) Le formatage de la date est très spécifique à la langue, donc vous obtenez un problème là-bas, si vous n'entrez pas au format ISO (ce que le programmeur de jardin moyen ne fait généralement pas, et en cas de un rapport que l'utilisateur sûr que l'enfer ne fera pas pour vous, même s'il est explicitement chargé de le faire).
et
b) plus important encore , vous perdez tout type de vérification de syntaxe . Si
<insert name of your "favourite" person here>
modifie le schéma parce que soudainement, les exigences de changement d'aile et qu'une nouvelle table est créée, l'ancienne est partie mais le champ de référence a été renommé, vous n'avez aucun avertissement. Un rapport fonctionne même lorsque vous l'exécutez sans sélectionner le paramètre wing (==> guid.empty). Mais soudain, quand un utilisateur réel sélectionne réellement une aile ==>boom . Cette méthode rompt complètement tout type de test.Méthode 2:
En résumé: "Grande" idée (avertissement - sarcasme), combinons les inconvénients de la méthode 3 (vitesse lente avec de nombreuses entrées) aux inconvénients plutôt horribles de la méthode 1.
Le seul avantage de cette méthode est que vous gardez toutes les traductions dans une seule table, et donc la maintenance est simple. Cependant, la même chose peut être obtenue avec la méthode 1 et une procédure stockée SQL dynamique, et une table (éventuellement temporaire) contenant les traductions et le nom de la table cible (et est assez simple en supposant que vous avez nommé tous vos champs de texte le même).
Méthode 3:
une table pour toutes les traductions: Inconvénient: vous devez stocker n clés étrangères dans la table des produits pour n champs que vous souhaitez traduire. Par conséquent, vous devez effectuer n jointures pour n champs. Lorsque la table de traduction est globale, elle comporte de nombreuses entrées et les jointures deviennent lentes. De plus, vous devez toujours joindre la table T_TRANSLATION n fois pour n champs. C'est tout à fait une surcharge. Maintenant, que faites-vous lorsque vous devez accepter des traductions personnalisées par client? Vous devrez ajouter encore 2x n jointures sur une table supplémentaire. Si vous devez rejoindre, disons 10 tables, avec 2x2xn = 4n jointures supplémentaires, quel gâchis! De plus, cette conception permet d'utiliser la même traduction avec 2 tableaux. Si je change le nom d'un élément dans une table, est-ce que je veux vraiment changer une entrée dans une autre table aussi CHAQUE FOIS?
De plus, vous ne pouvez plus supprimer et réinsérer la table, car il y a maintenant des clés étrangères DANS LES TABLEAUX DE PRODUITS ... vous pouvez bien sûr omettre de définir les FK, puis
<insert name of your "favourite" person here>
supprimer la table et réinsérer toutes les entrées avec newid () [ou en spécifiant l'id dans l'insertion, mais ayant l' identité-insertion désactivée ], et cela conduirait (et entraînera) très rapidement des données-garbage (et des exceptions de référence nulle).Méthode 4 (non répertoriée): stockage de toutes les langues dans un champ XML de la base de données. par exemple
Ensuite, vous pouvez obtenir la valeur par XPath-Query en SQL, où vous pouvez mettre la variable de chaîne dans
Et vous pouvez mettre à jour la valeur comme ceci:
Où vous pouvez remplacer
/lang/de/...
par'.../' + @in_language + '/...'
Un peu comme l'hstore PostGre, sauf qu'en raison de la surcharge d'analyse XML (au lieu de lire une entrée d'un tableau associatif dans PG hstore), il devient beaucoup trop lent et l'encodage xml le rend trop pénible pour être utile.
Méthode 5 (comme recommandé par SunWuKung, celle que vous devez choisir): Une table de traduction pour chaque table "Produit". Cela signifie une ligne par langue et plusieurs champs "texte", il ne nécessite donc qu'une seule jointure (gauche) sur N champs. Ensuite, vous pouvez facilement ajouter un champ par défaut dans la table "Produit", vous pouvez facilement supprimer et réinsérer la table de traduction, et vous pouvez créer une deuxième table pour les traductions personnalisées (sur demande), que vous pouvez également supprimer et réinsérez), et vous avez toujours toutes les clés étrangères.
Faisons un exemple pour voir ce TRAVAIL:
Créez d'abord les tableaux:
Remplissez ensuite les données
Et puis interrogez les données:
Si vous êtes paresseux, vous pouvez également utiliser l'ISO-TwoLetterName ('DE', 'EN', etc.) comme clé primaire de la table des langues, vous n'avez donc pas à rechercher l'ID de la langue. Mais si vous le faites, vous voudrez peut-être utiliser la balise de langue IETF à la place, ce qui est mieux, car vous obtenez de-CH et de-DE, ce qui n'est vraiment pas la même chose en termes d'ortographie (double s au lieu de ß partout) , bien qu'il s'agisse du même langage de base. C'est un tout petit détail qui peut être important pour vous, surtout si l'on considère que en-US et en-GB / en-CA / en-AU ou fr-FR / fr-CA ont des problèmes similaires.
Quote: nous n'en avons pas besoin, nous ne faisons que notre logiciel en anglais.
Réponse: Oui - mais lequel ??
Quoi qu'il en soit, si vous utilisez un ID entier, vous êtes flexible et pouvez modifier votre méthode à tout moment.
Et vous devez utiliser cet entier, car il n'y a rien de plus ennuyeux, destructeur et gênant qu'une conception Db bâclée.
Voir aussi RFC 5646 , ISO 639-2 ,
Et, si vous dites toujours "nous" ne faisons notre demande que pour " une seule culture" (comme en-US en général) - donc je n'ai pas besoin de cet entier supplémentaire, ce serait le bon moment et l'endroit pour mentionner le Les balises de langue IANA , n'est-ce pas?
Parce qu'ils vont comme ça:
et
(il y a eu une réforme de l'orthographe en 1996 ...) Essayez de trouver un mot dans un dictionnaire s'il est mal orthographié; cela devient très important dans les applications traitant des portails juridiques et de service public.
Plus important encore, certaines régions passent de l'alphabet cyrillique à l'alphabet latin, ce qui peut être plus gênant que la nuisance superficielle d'une réforme obscure de l'orthographe, c'est pourquoi cela pourrait également être un facteur important, selon le pays dans lequel vous vivez. D'une façon ou d'une autre, il vaut mieux avoir cet entier là, juste au cas où ...
Edit:
Et en ajoutant
ON DELETE CASCADE
aprèsvous pouvez simplement dire:
DELETE FROM T_Products
et n'obtenir aucune violation de clé étrangère.Quant au classement, je le ferais comme ceci:
A) Disposez de votre propre DAL
B) Enregistrez le nom du classement souhaité dans la table des langues
Vous voudrez peut-être placer les classements dans leur propre tableau, par exemple:
C) Ayez le nom du classement disponible dans vos informations auth.user.language
D) Écrivez votre SQL comme ceci:
E) Ensuite, vous pouvez le faire dans votre DAL:
Ce qui vous donnera alors cette requête SQL parfaitement composée
la source
La troisième option est la meilleure, pour plusieurs raisons:
-Adam
la source
Jetez un œil à cet exemple:
Je pense qu'il n'y a pas besoin d'expliquer, la structure se décrit.
la source
Je préfère généralement cette approche (pas le sql réel), cela correspond à votre dernière option.
Parce que le fait d'avoir tous les textes traduisibles au même endroit facilite la maintenance. Parfois, les traductions sont sous-traitées à des bureaux de traduction, de cette façon, vous pouvez leur envoyer un seul gros fichier d'exportation et les réimporter tout aussi facilement.
la source
Translation
tableau ou laTranslationItem.translationitemid
colonne?Avant de passer aux détails techniques et aux solutions, vous devez vous arrêter une minute et poser quelques questions sur les exigences. Les réponses peuvent avoir un impact énorme sur la solution technique. Voici des exemples de telles questions:
- Toutes les langues seront-elles utilisées tout le temps?
- Qui et quand remplira les colonnes avec les différentes versions linguistiques?
- Que se passe-t-il lorsqu'un utilisateur aura besoin d'une certaine langue de texte et qu'il n'y en a pas dans le système?
- Seuls les textes doivent être localisés ou il y a aussi d'autres articles (par exemple le PRIX peut être stocké en $ et € car ils peuvent être différents)
la source
Je cherchais quelques conseils pour la localisation et j'ai trouvé ce sujet. Je me demandais pourquoi cela est utilisé:
Vous obtenez donc quelque chose comme user39603 suggère:
Ne pouvez-vous pas simplement laisser la traduction de la table pour que vous obteniez ceci:
la source
ProductItem
table quelque chose commeProductTexts
ouProductL10n
bien. A plus de sens.Je suis d'accord avec randomizer. Je ne vois pas pourquoi vous avez besoin d'une table "traduction".
Je pense que cela suffit:
la source
L'approche ci-dessous serait-elle viable? Supposons que vous ayez des tableaux où plus d'une colonne doit être traduite. Ainsi, pour le produit, vous pouvez avoir à la fois le nom du produit et la description du produit qui doivent être traduits. Pourriez-vous faire ce qui suit:
la source
"Lequel est le meilleur" est basé sur la situation du projet. Le premier est facile à sélectionner et à maintenir, et les performances sont également meilleures car il n'a pas besoin de joindre des tables lors de la sélection d'une entité. Si vous avez confirmé que votre projet ne prend en charge que 2 ou 3 langues et qu'il n'augmentera pas, vous pouvez l'utiliser.
Le second est correct mais est difficile à comprendre et à maintenir. Et la performance est pire que la première.
Le dernier est bon en termes d'évolutivité mais mauvais en performances. La table T_TRANSLATION_ENTRY deviendra de plus en plus grande, c'est terrible lorsque vous voulez récupérer une liste d'entités de certaines tables.
la source
Ce document décrit les solutions possibles et les avantages et inconvénients de chaque méthode. Je préfère la "localisation en ligne" car vous n'avez pas à modifier le schéma de base de données lors de l'ajout d'une nouvelle langue.
la source