2008-11-24 5 views
7

Avoir une application entièrement internationalisée est une nécessité si vous voulez vendre dans le monde entier. En Java, nous utilisons des regroupements de ressources et cela résout des problèmes pour les codes statiques de texte. Mais que faites-vous à propos du texte qui est stocké dans la base de données?Comment internationalisez-vous le texte dans la base de données?

Commençant par les définitions statiques, par les objets modifiables par l'utilisateur, se terminant par les données entrées par l'utilisateur.

En supposant que vous ayez une base de données utilisée par des utilisateurs avec des paramètres régionaux différents - comment gérez-vous cela? Jusqu'où internationalisez-vous? Où dessinez-vous la ligne? Quelle solution de contournement peut empêcher les utilisateurs de recevoir du texte dans une langue qu'ils ne comprennent pas?

Répondre

6

Ne stockez pas de texte généré par le système dans la base de données. Au lieu de cela, stockez un code (comme un numéro de message), puis internationalisez-le au niveau de l'interface graphique. Assurez-vous que le seul texte qui provient directement de la base de données est du texte que l'utilisateur a lui-même mis en place. Assurez-vous que votre base de données est configurée pour accepter le texte Unicode.

0

Les données statiques sont les plus simples Je créerais une table de traduction, donc imaginez une table UserStatus qui a un StatusId, TranslationToken, puis le TranslationTable a un Token, une langue et un texte.

Ou simillary vous pouvez simplement renvoyer le jeton pour l'application à traiter en utilisant vos fichiers de ressources.

En ce qui concerne les données d'entrée de l'utilisateur, c'est beaucoup plus complexe. Vous devez au minimum accepter les caractères Unicode, mais la question devient Tri et Comparaison. Le tri est le plus grand. Beaucoup de ce que vous pouvez faire dépend de votre application. Donc, si votre base de données ne doit supporter qu'une seule langue à un moment donné (Imaginez si votre application a été distribuée à vos clients), alors le classement est un point discutable puisque vous pouvez le définir au moment de l'installation. Toutefois, si vous devez gérer plusieurs langues dans une même base de données, vous devrez gérer correctement le classement. Le seul moyen que nous avons trouvé pour modifier le classement à la volée était de le placer dans nos requêtes, et cela nécessitait que SQL dynamique soit généré. Dans cet exemple, vous stockez le russe, l'anglais et le polonais dans un seul champ de la même table.

Nous n'avons jamais exploré autre chose que les classements en latin et en cyrillique, mais j'imagine que les langues asiatiques fonctionneraient de la même manière.

0

Nous utilisons un fichier XML pour notre système. Le fichier contient des associations de touches avec une partie spécifique de nos modules. De cette façon, nous pouvons rapidement faire XPath pour récupérer des informations. Nous avons 1 fichier pour chaque langue (nous supportons 2 langues pour l'instant, mais l'ajout d'une langue est très simple, il suffit de copier-coller le fichier). Cette solution n'est pas parfaite mais présente certains avantages:

  1. Pas dans la base de données.
  2. Peut être modifié par une personne externe à la programmation.
  3. Facile à mettre en œuvre dans plusieurs vues (nous avons WinForm et WebForm).
1

Quelle solution peut empêcher les utilisateurs de recevoir du texte dans une langue qu'ils ne comprennent pas ?

Cela ne poserait problème que pour les données saisies par l'utilisateur.Ainsi, si vous voulez éviter que d'autres utilisateurs voient du contenu dans une langue qu'ils ne comprennent pas, stockez le code de paramètres régionaux avec le contenu et affichez uniquement ce contenu à toute personne ayant la même langue/langue choisie par l'utilisateur. D'autre part, les utilisateurs peuvent connaître plusieurs langauges, donc je ne voudrais pas les empêcher de voir le contenu, je voudrais juste ajouter un avis comme "Ce contenu n'est pas disponible dans la langue de votre choix, ..." et ensuite afficher le contenu de la langue disponible. De cette façon, vous augmentez la probabilité que l'utilisateur obtienne un contenu qu'elle peut comprendre.

2

Tout d'abord, soyez très conscient des limitations. Pour le contenu créé par l'utilisateur, vous cherchez une traduction communautaire (erratique), une traduction automatique (peu fiable) ou le paiement de traducteurs humains (cher!) Si vous voulez localiser des choses que vos utilisateurs entrent dans votre application. Vous pouvez demander à vos utilisateurs de fournir deux versions: une pour votre culture par défaut (anglais?) Et une pour leur culture localisée. Vous pouvez donc fournir une traduction de secours pour les autres utilisateurs? Deuxièmement, préparez-vous à des migrations de base de données extrêmement longues ... si vous avez quatre colonnes de texte dans une feuille de calcul Excel, vous avez soudainement besoin d'insérer chaque valeur dans votre système de traduction, en récupérant l'ID localisé, puis en stockant que dans la table que vous importez réellement - et SELECT * vous donnera seulement les ID de phrase, que vous devez resoudre en les localisant par rapport à vos tables de traduction. Cela dit - vous pouvez localiser beaucoup de tables de recherche, de listes déroulantes, etc. qui sont pilotées par la base de données dans un projet typique. D'autres commentaires ont déjà mentionné le stockage de valeurs StringId dans la base de données se référant à des fichiers de ressources externes ou des feuilles de calcul, mais si vous souhaitez conserver TOUT votre texte localisé dans la base de données, vous pourriez trouver cette approche utile.

Nous avons utilisé une table appelée Phrase, qui contient l'ID et le contenu (anglais) par défaut pour chaque partie de texte de votre application.

Vos autres tables finissent par ressembler à ceci:

CREATE TABLE ProductType (
    Id int primary key, 
    NamePhraseId int, -- link to the Phrase containing the name of this product type. 
    DescriptionPhraseId int 
) 

Créer une seconde culture de la table, qui contient les cultures spécifiques et neutres que vous soutenez. Pour les points bonus, implémentez cette table comme un arbre auto-référentiel (chaque enregistrement Culture contient une référence ParentCultureCode nullable), de sorte que vous pouvez revenir de cultures spécifiques ("fr-CA" pour le français canadien) à des cultures neutres ("fr" si aucune localisation régionale existe), à ​​votre culture invariante/default (normalement « en » parce qu'il est si largement parlé)

vos traductions réelles sont dans une table de LocalizedPhrase, qui ressemble à:

CREATE TABLE LocalizedPhrase (
    PhraseId int primary key, 
    CultureCode varchar(8) primary key, 
    Content nvarchar(255) -- the actual localized content 
) 

Vous pouvez étendez ce modèle si vous souhaitez fournir des localisations spécifiques aux hommes/aux femmes:

CREATE TABLE GenderedLocalizedPhrase (
    PhraseId int primary key, 
    CultureCode varchar(8) primary key, 
    GenderCode char(1) primary key, -- 'm', 'f' or '?' - links to Gender table 
    Content nvarchar(255) 
) 

Vous devez mettre en mémoire cache tout ce graphique de table et modifier vos stratégies de requête/jointure en conséquence - la mise en cache des localisations dans les classes Phrase et la substitution de la méthode ToString() sur l'objet Phrase est une approche. Si vous essayez de faire ce genre de choses à l'intérieur de vos requêtes, vous encourez un coût de performance importante et chaque requête finir par ressembler à ceci:

-- assume @MyCulture contains the culture code ('ca-FR') that we are looking for: 
SELECT 
    Product.Id, 
    Product.Name, 

    COALESCE(ProductStatusLocalizedPhrase.Content, ProductStatusPhrase.Content) as ProductStatus, 
    COALESCE(ProductTypeLocalizedPhrase.Content, ProductTypePhrase.Content) as ProductType, 
    FROM Product 

    INNER JOIN ProductStatus ON Product.StatusId = ProductStatus.Id 
    INNER JOIN Phrase as ProductStatusPhrase ON ProductStatus.NamePhraseId = Phrase.Id 
    LEFT JOIN LocalizedPhrase as ProductStatusLocalizedPhrase 
     ON ProductStatus.NamePhraseId = ProductStatusLocalizedPhrase.Id and CultureCode = @MyCulture 

    INNER JOIN ProductType ON Product.TypeId = ProductType.Id 
    INNER JOIN Phrase as ProductTypePhrase ON ProductType.NamePhraseId = Phrase.Id 
    LEFT JOIN LocalizedPhrase as ProductTypeLocalizedPhrase 
     ON ProductType.NamePhraseId = ProductTypeLocalizedPhrase.Id and CultureCode = @MyCulture 
1

Nous changeons beaucoup de texte dans notre base de données pour être « key: default text "puis en regardant la" clé "dans nos fichiers de traduction.Cela couvre tout le texte que le client ne modifie pas dans la base de données (par exemple, ce qu'il faut appeler une "note de crédit"). Lorsque le client change le texte, il peut simplement enlever la clé, de manière à ce qu'il y ait toujours de la valeur. Notre système a quelques tables qui contiennent les données de configuration qui ont besoin de ce qui précède, les tables qui contiennent juste le texte que les clients entrent ne sont pas un problème si chaque client n'a besoin que d'une seule langue.

1

Disons que vous avez une table:

create table countries (
    country_id int primary key, 
    short_name text not null unique, 
    official_name text not null unique, 
    iso_code char(2) not null unique 
); 

insert into countries values (12, 'Algeria', 'The People''s Democratic Republic of Algeria' 'DZ'); 

Ensuite, vous créez une table de traduction:

create table countries_t (
    country_id int not null references countries(country_id), 
    short_name text not null, 
    official_name text not null, 
    locale varchar(5) not null, 

    primary key (country_id, locale) 
); 

insert into countries_t values 
(12, 'Algérie', 'la République algérienne démocratique et populaire', 'fr'), 
(12, 'Algerien', 'Demokratische Volksrepublik Algerien', 'de-DE'); 

Créer une vue de renvoyer les données à partir d'une user - variable de session defined. Ci-dessous est spécifique à PostgreSQL, mais votre base de données pourrait soutenir les variables de session personnalisées, autre table temporaire de l'utilisation:

create view countries_i18n as 
    select 
    a.country_id, 
    coalesce(c.short_name, b.short_name, a.short_name) as short_name, --default to countries.name if translation not found 
    coalesce(c.official_name, b.official_name, a.official_name) as official_name 
    countries.iso_code 
    from countries a 
    left join countries_t b on b.id = a.id and b.locale = current_setting('my_custom_guc.locale') 
    left join countries_t c on c.id = a.id and c.locale = left(current_setting('my_custom_guc.locale'), 2); --fall-back to 2-letter locale 

Interrogation la table en allemand parlé en Allemagne:

SET my_custom_guc.language_code = 'de-DE'; 

select country_id, iso_code, short_name, official_name from countries_i18n; 

country_id iso_code short_name official_name 
----------------------------------------------- 
12   DZ  Algerien Demokratische ... 

Interrogez la table canadienne-française (retombe vers le français générique):

SET my_custom_guc.locale= 'fr-CA'; 

select country_id, iso_code, short_name, official_name from countries_i18n; 

country_id iso_code short_name official_name 
----------------------------------------------- 
12   DZ  Algérie  la République ... 

Interrogation la table en espagnol (il n'y a pas de traduction, retourne l'anglais):

SET my_custom_guc.language_code = 'es'; 

select country_id, iso_code, short_name, official_name from countries_i18n; 

country_id iso_code short_name official_name 
----------------------------------------------- 
12   DZ  Algeria The People's D ... 
Questions connexes